commit 809a8e5e87c71ee3f3213e7e6f1f31eb972b70da
parent e87c69019725a0c08d8a1329614ac3e519758d9e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Fri, 5 Jun 2026 21:00:54 -0700
Fix aarch64 COFF weak-null ADRP relocation
Diffstat:
5 files changed, 52 insertions(+), 18 deletions(-)
diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md
@@ -5,15 +5,6 @@ fixed, remove it instead of checking it off or keeping a closed entry.
Add new deferred fixes below as they are discovered.
-## aarch64-windows: `118_decl_extra_attrs` fails to link (ADRP out of range)
-
-`kit cc -target aarch64-windows` on `test/toy/cases/118_decl_extra_attrs.toy`
-aborts with `fatal: link: ADR_PREL_PG_HI21 out of range (need ±4GiB)` at both O0
-and O1. The same case links cleanly on x86_64-windows and aarch64-freebsd, so it
-is aarch64-windows-specific. The case combines TLS-model attrs + ifunc/dllimport
-+ section/merge attrs; the resulting aarch64-windows section/layout overflows an
-ADRP page-relative relocation. Surfaced by `test-toy-windows-vm` (test/toy/vm.sh).
-
## x86_64-windows: tail-call + sret miscompiles at O1 (access violation)
`test/toy/cases/{36_musttail_sret,37_tail_sret}.toy` built `-target
diff --git a/src/link/link_internal.h b/src/link/link_internal.h
@@ -278,6 +278,9 @@ LinkRelocApply* link_append_reloc_slot(LinkImage* img);
/* Emit or upsert a synthetic global boundary symbol (link_layout.c). */
void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
u64 vaddr);
+void link_emit_section_boundary_sym(Linker* l, LinkImage* img,
+ const char* name, LinkSectionId section_id,
+ u64 value);
/* Detect __start_<X> / __stop_<X> with <X> a valid C identifier.
* Defined in link_resolve.c; used by link_reloc_layout.c. */
diff --git a/src/link/link_layout.c b/src/link/link_layout.c
@@ -598,12 +598,16 @@ static Sym boundary_name(Linker* l, const char* name) {
return obj_format_c_mangle(l->c, name);
}
-/* Upsert a global symbol with the given absolute vaddr. Satisfies any
- * prior undef ref in place; fans out to per-input duplicate name slots. */
-void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
- u64 vaddr) {
+/* Upsert a global boundary symbol. Satisfies any prior undef ref in place; fans
+ * out to per-input duplicate name slots. `section_id` is LINK_SEC_NONE for
+ * absolute boundaries, or the owning LinkSectionId for boundaries that must
+ * follow a format-specific section relayout. */
+static void link_emit_boundary_sym_ex(Linker* l, LinkImage* img,
+ const char* name, u64 vaddr,
+ LinkSectionId section_id, u64 value) {
Sym sym = boundary_name(l, name);
LinkSymId id = symhash_get(&img->globals, sym);
+ LinkSymId canonical_id = id;
LinkSymbol rec;
u8 kind = SK_OBJ;
int fmt_kind;
@@ -617,6 +621,8 @@ void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
rec.name = sym;
rec.kind = kind;
rec.defined = 1;
+ rec.section_id = section_id;
+ rec.value = value;
rec.vaddr = vaddr;
rec.bind = SB_GLOBAL;
if (id != LINK_SYM_NONE) {
@@ -625,15 +631,16 @@ void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
} else {
LinkSymId fresh = link_append_symbol(img, &rec);
symhash_insert(&img->globals, sym, fresh, &id);
+ canonical_id = fresh;
}
n = LinkSyms_count(&img->syms);
for (i = 0; i < n; ++i) {
LinkSymbol* s = LinkSyms_at(&img->syms, i);
if (s->name != sym) continue;
- if (s->id == id) continue;
+ if (s->id == canonical_id) continue;
if (s->bind == SB_LOCAL) continue;
- s->section_id = LINK_SEC_NONE;
- s->value = 0;
+ s->section_id = section_id;
+ s->value = value;
s->vaddr = vaddr;
s->kind = kind;
s->defined = 1;
@@ -641,6 +648,25 @@ void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
}
}
+void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
+ u64 vaddr) {
+ link_emit_boundary_sym_ex(l, img, name, vaddr, LINK_SEC_NONE, 0);
+}
+
+void link_emit_section_boundary_sym(Linker* l, LinkImage* img,
+ const char* name, LinkSectionId section_id,
+ u64 value) {
+ const LinkSection* sec;
+ u64 vaddr;
+ if (section_id == LINK_SEC_NONE || section_id > img->nsections)
+ compiler_panic(img->c, SRCLOC_NONE,
+ "link: boundary symbol '%.*s' has no containing section",
+ SLICE_ARG(slice_from_cstr(name)));
+ sec = &img->sections[section_id - 1];
+ vaddr = sec->vaddr + (value - sec->obj_offset);
+ link_emit_boundary_sym_ex(l, img, name, vaddr, section_id, value);
+}
+
/* link_define_boundary: public alias used by link_dyn.c. */
void link_define_boundary(Linker* l, LinkImage* img, const char* name,
u64 vaddr) {
diff --git a/src/link/link_reloc_layout.c b/src/link/link_reloc_layout.c
@@ -726,8 +726,9 @@ void link_layout_iplt(Linker* l, LinkImage* img) {
SF_ALLOC | SF_WRITE, SSEM_PREINIT_ARRAY,
init_size, 8, &init_vaddr, NULL);
- link_emit_boundary_sym(l, img, "__start_iplt_pairs", pairs_vaddr);
- link_emit_boundary_sym(l, img, "__stop_iplt_pairs", pairs_vaddr + pairs_size);
+ link_emit_section_boundary_sym(l, img, "__start_iplt_pairs", pairs_sec_id, 0);
+ link_emit_section_boundary_sym(l, img, "__stop_iplt_pairs", pairs_sec_id,
+ pairs_size);
if (use_rela_iplt) {
/* One Elf64_Rela (24 bytes) per IFUNC: r_offset = igot slot, r_info =
diff --git a/src/obj/coff/link.c b/src/obj/coff/link.c
@@ -1352,6 +1352,19 @@ static void coff_apply_all_relocs(LinkImage* img,
wr_u32_le(P_bytes, (u32)(v & 0xffffffffu));
continue;
}
+ if (tgt->bind == SB_WEAK && tgt->kind == SK_ABS && tgt->vaddr == 0) {
+ /* AArch64 cannot generally ADRP from a PE image base down to absolute
+ * NULL. Materialize the weak-undef address as zero directly; the paired
+ * ADD low-12 relocation is already a no-op. */
+ if (r->kind == R_AARCH64_ADR_PREL_PG_HI21 ||
+ r->kind == R_AARCH64_ADR_PREL_PG_HI21_NC) {
+ u32 instr = rd_u32_le(P_bytes);
+ u32 rd = instr & 0x1fu;
+ wr_u32_le(P_bytes, 0xd2800000u | rd); /* movz Xrd, #0 */
+ continue;
+ }
+ if (r->kind == R_AARCH64_ADD_ABS_LO12_NC) continue;
+ }
link_reloc_apply(c, r->kind, P_bytes, S, r->addend, P);
}
}