# musl's strtod, unmodified, under AddressSanitizer. See README.
#
#   make MUSL=/path/to/musl         report
#   make MUSL=/path/to/musl fixed   with floatscan-oob.patch applied to a copy
#   make fetch                      clone v1.2.6 into ./musl

MUSL ?= $(firstword $(wildcard ../../../third_party/musl-1.2.6 musl))
MUSL_GIT ?= https://git.musl-libc.org/git/musl
MUSL_TAG ?= v1.2.6

# The error is at parse time, so the targets that do not need a tree --
# fetch above all, which is how you get one -- have to be let through.
ifeq ($(MUSL),)
ifeq ($(filter fetch clean dist,$(MAKECMDGOALS)),)
$(error no musl tree: pass MUSL=/path/to/musl, or `make fetch` to clone $(MUSL_TAG) here)
endif
MUSL := musl
endif

BUILD := build
OBJ := $(BUILD)/musl
CC ?= cc
ASAN := -fsanitize=address

MUSL_CFLAGS := -O2 -g $(ASAN) -std=c99 -ffreestanding -nostdinc \
    -D_XOPEN_SOURCE=700 \
    -I $(MUSL)/arch/x86_64 -I $(MUSL)/arch/generic \
    -I $(OBJ)/obj/src/internal \
    -I $(MUSL)/src/include -I $(MUSL)/src/internal \
    -I $(OBJ)/obj/include -I $(MUSL)/include

DIST := musl-strtod-oob
DIST_FILES := README Makefile repro.c stub.c floatscan-oob.patch

.PHONY: all fixed fetch dist clean
all: $(BUILD)/repro
	./$(BUILD)/repro

$(BUILD):
	mkdir -p $(BUILD)

# For anyone without a musl checkout to hand. Run it, then run make again.
fetch:
	git clone $(MUSL_GIT) musl
	cd musl && git checkout $(MUSL_TAG)

$(OBJ)/config.mak: | $(BUILD)
	mkdir -p $(OBJ)
	cd $(OBJ) && $(abspath $(MUSL))/configure --target=x86_64 CC=$(CC)

GEN := $(OBJ)/obj/include/bits/alltypes.h $(OBJ)/obj/include/bits/syscall.h

$(GEN): $(OBJ)/config.mak
	$(MAKE) -C $(OBJ) $(patsubst $(OBJ)/%,%,$@)

$(BUILD)/floatscan.o: $(MUSL)/src/internal/floatscan.c $(GEN)
	$(CC) $(MUSL_CFLAGS) -c $< -o $@
$(BUILD)/shgetc.o: $(MUSL)/src/internal/shgetc.c $(GEN)
	$(CC) $(MUSL_CFLAGS) -c $< -o $@
$(BUILD)/strtod.o: $(MUSL)/src/stdlib/strtod.c $(GEN)
	$(CC) $(MUSL_CFLAGS) -c $< -o $@
$(BUILD)/%.o: $(MUSL)/src/math/%.c $(GEN)
	$(CC) $(MUSL_CFLAGS) -c $< -o $@

MATH := $(BUILD)/scalbn.o $(BUILD)/scalbnl.o $(BUILD)/copysignl.o \
    $(BUILD)/fabsl.o $(BUILD)/fmodl.o $(BUILD)/__fpclassifyl.o
OBJS := $(BUILD)/shgetc.o $(BUILD)/strtod.o $(MATH)

$(BUILD)/repro: repro.c stub.c $(BUILD)/floatscan.o $(OBJS)
	$(CC) -O2 -g $(ASAN) -o $@ repro.c stub.c $(BUILD)/floatscan.o $(OBJS) -lm

# The contrast, built from a patched COPY so the checkout stays clean.
$(BUILD)/floatscan_fixed.c: $(MUSL)/src/internal/floatscan.c floatscan-oob.patch | $(BUILD)
	cp $< $@
	patch -s -p1 $@ < floatscan-oob.patch

$(BUILD)/floatscan_fixed.o: $(BUILD)/floatscan_fixed.c $(GEN)
	$(CC) $(MUSL_CFLAGS) -c $< -o $@

$(BUILD)/repro_fixed: repro.c stub.c $(BUILD)/floatscan_fixed.o $(OBJS)
	$(CC) -O2 -g $(ASAN) -o $@ repro.c stub.c $(BUILD)/floatscan_fixed.o $(OBJS) -lm

fixed: $(BUILD)/repro_fixed
	./$(BUILD)/repro_fixed

# The tarball, holding only what is ours: no musl, no build output.
dist: $(DIST_FILES)
	rm -rf $(BUILD)/$(DIST)
	mkdir -p $(BUILD)/$(DIST)
	cp $(DIST_FILES) $(BUILD)/$(DIST)/
	tar czf $(BUILD)/$(DIST).tar.gz -C $(BUILD) $(DIST)
	@echo "wrote $(BUILD)/$(DIST).tar.gz"

clean:
	rm -rf $(BUILD)
