commit 3beca0093e126f7b015b802d8f67bf78eecd1cf3
parent ba66f6d185d5821bdbd424aa95957d9af2fdfdca
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 17 Jun 2026 08:07:16 -0700
ld: keep GNU-ld non-PIE default after fill_options
driver_link_flags_fill_options derives lopts.pie via driver_link_pie, which
defaults hosted targets (target.pic == KIT_PIC_PIE) to PIE — correct for the
cc/build compiler drivers but not for a raw linker, which must match GNU ld /
lld's non-PIE ET_EXEC default (PIE is opt-in via -pie; a PIE default with no
guaranteed dynamic loader faults). Override lopts.pie back to ld's documented
policy after fill_options; freestanding_strict is then derived from it.
Diffstat:
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c
@@ -3142,15 +3142,25 @@ static int ld_run_link(LdOptions* o) {
: o->shared ? KIT_LINK_OUTPUT_SHARED
: KIT_LINK_OUTPUT_EXE;
/* The shared flag model fills entry/text-base/defsym/section-start/orphan/
- * fatal-warnings/build-id/gc/strip/pe-subsystem/interp/soname/rpath and the
- * PIE decision (driver_link_pie, equivalent to the old
- * o->pie && !o->shared && !o->relocatable; -shared/-r never PIE — a
- * freestanding or static link has no dynamic loader to apply a PIE image's
- * relocations). Callers that want a hosted PIE executable pass -pie. */
+ * fatal-warnings/build-id/gc/strip/pe-subsystem/interp/soname/rpath. It also
+ * derives lopts.pie via driver_link_pie, but ld overrides that below. */
if (driver_link_flags_fill_options(&o->lf, o->target, o->pie, o->shared,
o->relocatable, output_kind, script,
&lopts, &rpath_slices) != 0)
goto out;
+ /* GNU ld and lld default to a non-PIE ET_EXEC; PIE is opt-in via -pie. The
+ * linker imposes no hosted-PIE default the way a compiler driver does (kit
+ * cc still defaults hosted executables to PIE through the link API) — a
+ * freestanding or static link has no dynamic loader to apply a PIE image's
+ * relocations or choose its base, so a PIE default produces a binary that
+ * faults under a direct/qemu loader (its writable segments sit below
+ * mmap_min_addr at the vaddr-0 PIE base). -shared/-r never PIE. Callers that
+ * want a hosted PIE executable pass -pie explicitly (as the musl dynamic
+ * lane and real compiler drivers do); the hosted-libc dynamic lane forces
+ * o->pie = 1 above. So override the shared driver_link_pie decision (which
+ * would default hosted target.pic == KIT_PIC_PIE targets to PIE) back to
+ * ld's policy. */
+ lopts.pie = o->pie && !o->shared && !o->relocatable;
/* Strict freestanding policy: a `*-none-*` (FREESTANDING) static non-PIE
* executable must reject dynamic-link artifacts and cross-input
* target/format mismatches. Only the EXE lane imposes it; -r/-shared do