kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit c04e8d39488b1599fc07d0ca7633029866ef5fce
parent 97b868ff24f7d52083af2a924890ed48b14a5f6d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 28 May 2026 11:02:06 -0700

link/elf: fix PIE relocations for code-address data

Two bugs broke PIE binaries that load a code address from data and branch
to it (jump tables, @labeladdr arrays, function pointers). Both manifest
as a SIGSEGV under a real loader (qemu-aarch64 / arm64), while the
in-process JIT path masked them.

1. RELATIVE records dropped the reloc addend: emit_relative_record was
   passed tgt->vaddr, ignoring r->addend. RELA RELATIVE ignores the
   in-place slot and writes load_base + r_addend, so every entry of an
   addend-bearing table (a 16-way jump table targets `pick+0x88`,
   `+0x94`, ...) collapsed onto the symbol base. Pass tgt->vaddr +
   r->addend.

2. Reloc-bearing read-only data sat in a never-writable PT_LOAD: const
   data (.rodata) carrying abs32/abs64 relocs becomes a RELATIVE/GLOB_DAT
   dynamic record the loader must *write* into the slot, which faults a
   PF_R page. Promote such sections to the writable segment in PIE mode
   (the .data.rel.ro convention, minus PT_GNU_RELRO re-protection).

Fixes toy X-path aa64 cases: 119_static_labeladdr_data,
119_switch_strategy_hints, 123_spec_demo, 127_switch_forced_jump_table,
136_call_builtin_attrs.

Diffstat:
Msrc/link/link_layout.c | 24++++++++++++++++++++++++
Msrc/obj/elf/link.c | 8+++++++-
2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/link/link_layout.c b/src/link/link_layout.c @@ -70,6 +70,27 @@ SegBucket link_bucket_for(u16 flags) { return SEG_R; } +/* PIE `.data.rel.ro` placement: a read-only data section that carries an + * absolute (abs32/abs64) reloc cannot stay in a never-writable PT_LOAD. + * In PIE the linker rewrites those relocs into dynamic records — a + * RELATIVE for an internal target, a GLOB_DAT for an import — and the + * loader *writes* the resolved pointer into the slot at load time. A + * PF_R-only segment faults that store (manifesting as a SIGSEGV in the + * dynamic loader). Jump tables, @labeladdr arrays, and const pointer + * initializers all land here. Promote such sections to the writable + * segment; we forgo the post-relocation RELRO re-protection that a full + * toolchain would apply via PT_GNU_RELRO. */ +static int link_pie_ro_section_needs_write(const ObjBuilder* ob, + ObjSecId sid) { + u32 i, total = obj_reloc_total(ob); + for (i = 0; i < total; ++i) { + const Reloc* r = obj_reloc_at(ob, i); + if (!r || r->removed || r->section_id != sid) continue; + if (r->kind == R_ABS64 || r->kind == R_ABS32) return 1; + } + return 0; +} + /* ---- LinkImage growth helpers ---- * * syms / relocs back onto SegVec — pointers stay stable across pushes, @@ -263,6 +284,9 @@ void link_layout_sections(Linker* l, LinkImage* img, const GcLive* g) { entries[e].size = has_atoms ? a->size : section_size_for_link(s); entries[e].name = s->name; entries[e].bucket = link_bucket_for(s->flags); + if (l->emit_pie && entries[e].bucket == SEG_R && + link_pie_ro_section_needs_write(ob, j)) + entries[e].bucket = SEG_RW; entries[e].next = PLACE_NONE; key = place_group_key(entries[e].name, entries[e].bucket); diff --git a/src/obj/elf/link.c b/src/obj/elf/link.c @@ -457,7 +457,13 @@ static void apply_all_relocs(LinkImage* img, u64 img_base) { * the site, and the RELATIVE record tells the loader to add * load_base on top. */ if (pie && reloc_is_abs(r->kind) && tgt->defined && tgt->kind != SK_ABS) { - emit_relative_record(img, r->write_vaddr, tgt->vaddr); + /* RELA RELATIVE ignores the in-place site value: the loader writes + * (load_base + r_addend) into the slot. So the addend must be the + * full image-relative target — symbol vaddr plus the reloc's own + * addend — not just the symbol vaddr. Dropping r->addend collapses + * every entry of an addend-bearing table (jump tables, labeladdr + * arrays, &sym+off initializers) onto the symbol base. */ + emit_relative_record(img, r->write_vaddr, tgt->vaddr + (u64)r->addend); } link_reloc_apply(img->c, r->kind, P_bytes, S, r->addend, P); }