flan/spike/embed/symbols.sh
Joseph Ferano 2e9b29e549 One binary that is both a Flan program and the compiler that built it
The first six probes each proved a piece. merged.sh puts them together: the
program's @main is renamed out of the way, a C main takes the main thread and
runs it there, caml_startup happens on a thread beside it, and clang links the
lot -- the emitted program object, flan_rt.c, flan_dev.c, flan_agent.c and the
whole compiler as one -output-complete-obj. It runs, and the compiler inside it
compiles the very source the program was built from.

Nothing is wired up. The two halves share an address space and do not speak.
That is the point: the question was whether they can, not what they would say.

sig.sh and symbols.sh answer the two questions the first pass got wrong or
skipped. The SIGSEGV reading in harness5.c was taken at the wrong moment --
OCaml 5 starts domains after caml_startup returns, so the disposition had to be
read from inside the runtime, and against a plain ocamlopt executable as a
control. symbols.sh is the hazard nobody looks for until the link fails: four
.c files that are compiled into two different processes today, and the OCaml
runtime, all landing in one link.
2026-09-12 20:32:51 +07:00

51 lines
2.0 KiB
Bash

#!/usr/bin/env bash
# Step 7: the integration hazard nobody asks about until the link fails.
#
# Today runtime/flan_rt.c, runtime/flan_dev.c and vendor/agent/flan_agent.c are
# compiled into the *program*, and lib/dynload_stubs.c into the *compiler*.
# Merging the processes puts all four and the OCaml runtime in one link. This
# checks, symbol by symbol, whether anything collides.
set -u
here=$(cd "$(dirname "$0")" && pwd)
root=$(cd "$here/../.." && pwd)
out=$(mktemp -d)
trap 'rm -rf "$out"' EXIT
cd "$root" || exit 1
defs() { nm --defined-only "$@" 2>/dev/null | awk 'NF==3 {print $3}' | sort -u; }
clang -c runtime/flan_rt.c -o "$out/rt.o" || exit 1
clang -c runtime/flan_dev.c -o "$out/dev.o" || exit 1
clang -c vendor/agent/flan_agent.c -o "$out/ag.o" || exit 1
clang -c -I"$(ocamlopt -where)" "$here/dynload_stubs.c" -o "$out/dl.o" || exit 1
defs "$out/rt.o" "$out/dev.o" "$out/ag.o" "$out/dl.o" > "$out/flan.syms"
defs /home/joe/.opam/default/lib/ocaml/libasmrun.a > "$out/ml.syms" 2>/dev/null
[ -s "$out/ml.syms" ] || defs "$(ocamlopt -where)/libasmrun.a" > "$out/ml.syms"
echo "Flan's own C defines $(wc -l < "$out/flan.syms") symbols;" \
"libasmrun defines $(wc -l < "$out/ml.syms")."
echo "collisions between Flan's C and the OCaml runtime:"
if comm -12 "$out/flan.syms" "$out/ml.syms" | grep . ; then
echo " ^^ those would have to be renamed"
else
echo " none"
fi
echo "collisions among Flan's own four .c files:"
for a in rt dev ag dl; do defs "$out/$a.o" > "$out/$a.syms"; done
found=0
for a in rt dev ag dl; do
for b in rt dev ag dl; do
[ "$a" \< "$b" ] || continue
c=$(comm -12 "$out/$a.syms" "$out/$b.syms")
[ -n "$c" ] && { echo " $a vs $b:"; echo "$c" | sed 's/^/ /'; found=1; }
done
done
[ $found -eq 0 ] && echo " none"
echo "what Flan's C needs that the OCaml runtime also exports (shared libc etc):"
nm --undefined-only "$out/rt.o" "$out/ag.o" 2>/dev/null | awk 'NF==2{print $2}' \
| sort -u > "$out/need.syms"
comm -12 "$out/need.syms" "$out/ml.syms" | sed 's/^/ /' | head -20