kit

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

link_layout.c (93303B)


      1 /* link_layout.c — section bucketing, vaddr assignment, scripted layout,
      2  * COMMON BSS allocation, segment-byte copying, and the top-level
      3  * link_resolve orchestration function.
      4  *
      5  * Image-relative discipline: every vaddr / file_offset on the produced
      6  * image treats the image as based at 0. Consumers (link_emit_elf,
      7  * kit_jit_from_image) add their own runtime base before patching
      8  * relocations or writing PT_LOAD headers. Segment byte buffers hold raw
      9  * input section bytes — no relocations are applied here, in line with
     10  * the incremental-link discipline (link.h:136). */
     11 
     12 #include <kit/core.h>
     13 #include <kit/jit.h>
     14 #include <stdarg.h>
     15 #include <string.h>
     16 
     17 #include "core/buf.h"
     18 #include "core/bytes.h"
     19 #include "core/diag.h"
     20 #include "core/heap.h"
     21 #include "core/metrics.h"
     22 #include "core/pool.h"
     23 #include "core/slice.h"
     24 #include "core/util.h"
     25 #include "core/vec.h"
     26 #include "link/link.h"
     27 #include "link/link_arch.h"
     28 #include "link/link_internal.h"
     29 #include "obj/format.h"
     30 
     31 LinkImage* link_image_alloc(Compiler*); /* defined in link.c */
     32 
     33 #define LINK_ELF_SHF_COMPRESSED 0x800u
     34 
     35 /* Page size used for ELF segment alignment. We pull from env->execmem
     36  * when present (matches the eventual JIT mapping granularity) and fall
     37  * back to 16 KiB otherwise — large enough for any current Linux/aarch64
     38  * loader. A future cross-link with mismatched host/target page sizes
     39  * will need a target-derived value here instead. */
     40 u64 link_layout_page_size(Linker* l) {
     41   const KitExecMem* m = (l && l->jit_host) ? l->jit_host->execmem : NULL;
     42   if (m && m->page_size) return (u64)m->page_size;
     43   return 0x4000u;
     44 }
     45 
     46 /* Four-bucket segment partitioning: see SegBucket in link_internal.h. */
     47 
     48 int link_section_kept(const Section* s) {
     49   /* This cut keeps allocatable progbits/nobits sections only. Debug,
     50    * symtab/strtab, group, and note sections are dropped — none of
     51    * them participate in a static ET_EXEC layout. */
     52   if (!(s->flags & SF_ALLOC)) return 0;
     53   if (s->sem == SSEM_PROGBITS || s->sem == SSEM_NOBITS || s->sem == SSEM_NOTE)
     54     return 1;
     55   if (s->sem == SSEM_INIT_ARRAY || s->sem == SSEM_FINI_ARRAY ||
     56       s->sem == SSEM_PREINIT_ARRAY)
     57     return 1;
     58   return 0;
     59 }
     60 
     61 int link_section_kept_fileonly(const Section* s) {
     62   /* Non-allocatable .debug_* sections. They get no PT_LOAD segment but
     63    * are carried through to the file so addr2line / gdb resolve
     64    * file:line on the linked image.
     65    *
     66    * ELF SHF_COMPRESSED debug sections carry compressed bytes but relocation
     67    * offsets refer to the uncompressed DWARF stream. Until the object model has
     68    * a decompression/recompression path, dropping them is the only safe linked
     69    * executable behavior. */
     70   return s && !s->removed && s->kind == SEC_DEBUG &&
     71          !(s->ext_kind == OBJ_EXT_ELF &&
     72            (s->ext_flags & LINK_ELF_SHF_COMPRESSED));
     73 }
     74 
     75 SegBucket link_bucket_for(u16 flags) {
     76   if (flags & SF_TLS) return SEG_TLS;
     77   if (flags & SF_EXEC) return SEG_RX;
     78   if (flags & SF_WRITE) return SEG_RW;
     79   return SEG_R;
     80 }
     81 
     82 /* PIE `.data.rel.ro` placement: a read-only data section that carries an
     83  * absolute (abs32/abs64) reloc cannot stay in a never-writable PT_LOAD.
     84  * In PIE the linker rewrites those relocs into dynamic records — a
     85  * RELATIVE for an internal target, a GLOB_DAT for an import — and the
     86  * loader *writes* the resolved pointer into the slot at load time. A
     87  * PF_R-only segment faults that store (manifesting as a SIGSEGV in the
     88  * dynamic loader). Jump tables, @labeladdr arrays, and const pointer
     89  * initializers all land here. Promote such sections to the writable
     90  * segment; we forgo the post-relocation RELRO re-protection that a full
     91  * toolchain would apply via PT_GNU_RELRO. */
     92 static int link_pie_ro_section_needs_write(const ObjBuilder* ob, ObjSecId sid) {
     93   u32 i, total = obj_reloc_total(ob);
     94   for (i = 0; i < total; ++i) {
     95     const Reloc* r = obj_reloc_at(ob, i);
     96     if (!r || r->removed || r->section_id != sid) continue;
     97     if (r->kind == R_ABS64 || r->kind == R_ABS32) return 1;
     98   }
     99   return 0;
    100 }
    101 
    102 /* ---- LinkImage growth helpers ----
    103  *
    104  * syms / relocs back onto SegVec — pointers stay stable across pushes,
    105  * so callers may stash LinkSymbol/LinkRelocApply references and
    106  * re-enter mutation without invalidation. */
    107 
    108 static LinkSymbol* append_symbol_slot(LinkImage* img) {
    109   u32 idx;
    110   LinkSymbol* s = LinkSyms_push(&img->syms, &idx);
    111   if (!s) compiler_panic(img->c, SRCLOC_NONE, "link: oom growing symbols");
    112   s->id = (LinkSymId)(idx + 1u);
    113   return s;
    114 }
    115 
    116 LinkSymId link_append_symbol(LinkImage* img, const LinkSymbol* tmpl) {
    117   LinkSymbol* s = append_symbol_slot(img);
    118   LinkSymId id = s->id;
    119   *s = *tmpl;
    120   s->id = id;
    121   return id;
    122 }
    123 
    124 LinkRelocApply* link_append_reloc_slot(LinkImage* img) {
    125   LinkRelocApply* r = LinkRelocs_push(&img->relocs, NULL);
    126   if (!r) compiler_panic(img->c, SRCLOC_NONE, "link: oom growing relocs");
    127   return r;
    128 }
    129 
    130 /* ---- pass 2: section assignment + segment layout ---- */
    131 
    132 typedef struct SecRef {
    133   u32 input_idx;
    134   ObjSecId obj_sec_id;
    135   LinkSectionId link_sec_id;
    136 } SecRef;
    137 
    138 #define PLACE_NONE ((u32)~0u)
    139 
    140 /* Within a bucket, input sections sharing a name are placed contiguously
    141  * — the standard "merge sections by name" rule. Without this the .init
    142  * prologue from crti.o and the matching epilogue from crtn.o (both in
    143  * a .init section) get separated by intervening .text, and `_init` is
    144  * no longer a contiguous function. Placement walk:
    145  *
    146  *   1. Build a flat list of (input_idx, obj_sec_id) for kept+live
    147  *      sections.
    148  *   2. While collecting, append each section to an O(1)-expected lookup
    149  *      keyed by (bucket, name). The group array is append-only, so group
    150  *      order is still first occurrence; each group's linked list preserves
    151  *      input order.
    152  *   3. Lay out groups in first-occurrence order.
    153  */
    154 typedef struct PlaceEntry {
    155   u32 input_idx;
    156   ObjSecId obj_sec_id;
    157   ObjAtomId obj_atom_id;
    158   u32 obj_offset;
    159   u32 size;
    160   Sym name;
    161   SegBucket bucket;
    162   u32 next;
    163 } PlaceEntry;
    164 
    165 typedef struct PlaceGroup {
    166   u32 head;
    167   u32 tail;
    168 } PlaceGroup;
    169 
    170 static inline u32 place_group_hash_(u64 key) { return hash_u64(key); }
    171 HASHMAP_DEFINE(PlaceGroupHash, u64, u32, place_group_hash_);
    172 
    173 static u64 place_group_key(Sym name, SegBucket bucket) {
    174   return (((u64)name + 1u) << 3) | ((u64)bucket + 1u);
    175 }
    176 
    177 static u32 place_group_hash_cap(u32 n) {
    178   u32 cap = KIT_HASHMAP_INIT_CAP;
    179   while (cap < 0x80000000u && (cap - cap / 4u) < n) cap <<= 1;
    180   return cap;
    181 }
    182 
    183 static int live_section_units(const GcLive* g, const InputMap* m, u32 ii,
    184                               ObjBuilder* ob, ObjSecId sid) {
    185   u32 n = 0, first, count, i;
    186   if (link_input_section_has_atoms(m, sid)) {
    187     link_input_section_atoms(m, sid, &first, &count);
    188     for (i = 0; i < count; ++i) {
    189       ObjAtomId aid = m->section_atom_ids[first + i];
    190       const ObjAtom* a = obj_atom_get(ob, aid);
    191       if (!a || a->removed) continue;
    192       if (link_gc_atom_live_get(g, ii, aid)) ++n;
    193     }
    194     return n;
    195   }
    196   return link_gc_live_get(g, ii, sid) ? 1 : 0;
    197 }
    198 
    199 static void map_placed_unit(InputMap* m, ObjSecId sid, ObjAtomId aid,
    200                             LinkSectionId lsid) {
    201   if (aid != OBJ_ATOM_NONE) {
    202     m->atom[aid] = lsid;
    203     if (m->section[sid] == LINK_SEC_NONE) m->section[sid] = lsid;
    204     return;
    205   }
    206   m->section[sid] = lsid;
    207 }
    208 
    209 static void link_layout_sections_scripted(Linker* l, LinkImage* img,
    210                                           const GcLive* g);
    211 
    212 void link_layout_sections(Linker* l, LinkImage* img, const GcLive* g) {
    213   if (l->script) {
    214     link_layout_sections_scripted(l, img, g);
    215     return;
    216   }
    217   Heap* h = img->heap;
    218   u32 ii, j;
    219   u32 total_kept = 0;
    220 
    221   /* Pass 0: count kept sections (filtered by GC liveness). */
    222   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    223     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
    224     for (j = 1; j < obj_section_count(ob); ++j) {
    225       const Section* s = obj_section_get(ob, j);
    226       InputMap* m = &img->input_maps[ii];
    227       if (s && link_section_kept(s) && !m->comdat_discarded[j])
    228         total_kept += live_section_units(g, m, ii, ob, j);
    229     }
    230   }
    231 
    232   img->sections = total_kept ? (LinkSection*)h->alloc(
    233                                    h, sizeof(*img->sections) * total_kept,
    234                                    _Alignof(LinkSection))
    235                              : NULL;
    236   if (total_kept && !img->sections)
    237     compiler_panic(img->c, SRCLOC_NONE, "link: oom on sections");
    238 
    239   /* Pass 1: collect kept sections into a flat list. */
    240   PlaceEntry* entries =
    241       total_kept ? (PlaceEntry*)h->alloc(h, sizeof(*entries) * total_kept,
    242                                          _Alignof(PlaceEntry))
    243                  : NULL;
    244   PlaceGroup* groups =
    245       total_kept ? (PlaceGroup*)h->alloc(h, sizeof(*groups) * total_kept,
    246                                          _Alignof(PlaceGroup))
    247                  : NULL;
    248   PlaceGroupHash group_map;
    249   u32 ngroups = 0;
    250   if (total_kept && !entries)
    251     compiler_panic(img->c, SRCLOC_NONE, "link: oom on placement entries");
    252   if (total_kept && !groups)
    253     compiler_panic(img->c, SRCLOC_NONE, "link: oom on placement groups");
    254   if (total_kept) {
    255     PlaceGroupHash_init_cap(&group_map, h, place_group_hash_cap(total_kept));
    256     if (!group_map.slots)
    257       compiler_panic(img->c, SRCLOC_NONE, "link: oom on placement group map");
    258   }
    259   {
    260     u32 e = 0;
    261     for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    262       ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
    263       InputMap* m = &img->input_maps[ii];
    264       for (j = 1; j < obj_section_count(ob); ++j) {
    265         const Section* s = obj_section_get(ob, j);
    266         u64 key;
    267         u32* hit;
    268         u32 group_idx;
    269         if (!s || !link_section_kept(s) || m->comdat_discarded[j]) continue;
    270         u32 first = 0, count = 1, ai;
    271         int has_atoms = link_input_section_has_atoms(m, j);
    272         if (has_atoms) link_input_section_atoms(m, j, &first, &count);
    273         for (ai = 0; ai < count; ++ai) {
    274           ObjAtomId aid =
    275               has_atoms ? m->section_atom_ids[first + ai] : OBJ_ATOM_NONE;
    276           const ObjAtom* a = has_atoms ? obj_atom_get(ob, aid) : NULL;
    277           if (has_atoms) {
    278             if (!a || a->removed) continue;
    279             if (!link_gc_atom_live_get(g, ii, aid)) continue;
    280           } else if (!link_gc_live_get(g, ii, j)) {
    281             continue;
    282           }
    283           entries[e].input_idx = ii;
    284           entries[e].obj_sec_id = j;
    285           entries[e].obj_atom_id = has_atoms ? aid : OBJ_ATOM_NONE;
    286           entries[e].obj_offset = has_atoms ? a->offset : 0u;
    287           entries[e].size = has_atoms ? a->size : link_section_size_for_link(s);
    288           entries[e].name = s->name;
    289           entries[e].bucket = link_bucket_for(s->flags);
    290           if (l->emit_pie && entries[e].bucket == SEG_R &&
    291               link_pie_ro_section_needs_write(ob, j))
    292             entries[e].bucket = SEG_RW;
    293           entries[e].next = PLACE_NONE;
    294 
    295           key = place_group_key(entries[e].name, entries[e].bucket);
    296           hit = PlaceGroupHash_get(&group_map, key);
    297           if (hit) {
    298             group_idx = *hit - 1u;
    299           } else {
    300             group_idx = ngroups++;
    301             groups[group_idx].head = PLACE_NONE;
    302             groups[group_idx].tail = PLACE_NONE;
    303             PlaceGroupHash_set(&group_map, key, group_idx + 1u);
    304           }
    305           if (groups[group_idx].tail == PLACE_NONE) {
    306             groups[group_idx].head = e;
    307           } else {
    308             entries[groups[group_idx].tail].next = e;
    309           }
    310           groups[group_idx].tail = e;
    311           ++e;
    312         }
    313       }
    314     }
    315   }
    316 
    317   /* Four segment buckets; tracks per-bucket size during scan and
    318    * per-section file_offset within the bucket. */
    319   u64 seg_size[SEG_NBUCKETS] = {0};
    320   u32 seg_align[SEG_NBUCKETS] = {1, 1, 1, 1};
    321   u32 seg_count[SEG_NBUCKETS] = {0};
    322   /* Track trailing NOBITS so segment mem_size > file_size: SEG_RW
    323    * for .bss / COMMON, SEG_TLS for .tbss. */
    324   u64 seg_bss_extra[SEG_NBUCKETS] = {0};
    325 
    326   /* Pass 2: place sections, grouped by name within each bucket and
    327    * in first-occurrence order across groups. NOBITS (.bss/.tbss) sections
    328    * are placed in a second sub-pass so every bucket's file image stays a
    329    * contiguous prefix: ELF requires bss to trail, and for TLS specifically
    330    * a .tbss ahead of .tdata makes the loader copy garbage file bytes as the
    331    * zero-init image (FreeBSD/riscv _init_tls then crashes on a stale TLS
    332    * pointer). */
    333   for (int bss_phase = 0; bss_phase < 2; ++bss_phase) {
    334     for (u32 gi = 0; gi < ngroups; ++gi) {
    335       for (u32 k = groups[gi].head; k != PLACE_NONE; k = entries[k].next) {
    336         PlaceEntry* pe = &entries[k];
    337         SegBucket bucket = pe->bucket;
    338 
    339         ObjBuilder* ob = LinkInputs_at(&l->inputs, pe->input_idx)->obj;
    340         InputMap* m = &img->input_maps[pe->input_idx];
    341         const Section* s = obj_section_get(ob, pe->obj_sec_id);
    342         u32 align = s->align ? s->align : 1u;
    343         u64 ofs;
    344         LinkSection* ls;
    345         LinkSectionId lsid;
    346         int is_bss = (s->sem == SSEM_NOBITS || s->kind == SEC_BSS);
    347 
    348         if (is_bss != bss_phase) continue;
    349         if (is_bss) {
    350           u64 cursor = seg_size[bucket] + seg_bss_extra[bucket];
    351           cursor = ALIGN_UP(cursor, (u64)(align));
    352           seg_bss_extra[bucket] = cursor + (u64)pe->size - seg_size[bucket];
    353           ofs = cursor;
    354         } else {
    355           seg_size[bucket] += seg_bss_extra[bucket];
    356           seg_bss_extra[bucket] = 0;
    357           ofs = ALIGN_UP(seg_size[bucket], (u64)(align));
    358           seg_size[bucket] = ofs + (u64)pe->size;
    359         }
    360 
    361         if (align > seg_align[bucket]) seg_align[bucket] = align;
    362         seg_count[bucket]++;
    363 
    364         lsid = (LinkSectionId)(img->nsections + 1u);
    365         ls = &img->sections[img->nsections++];
    366         memset(ls, 0, sizeof(*ls));
    367         ls->id = lsid;
    368         ls->input_id = LinkInputs_at(&l->inputs, pe->input_idx)->id;
    369         ls->obj_section_id = pe->obj_sec_id;
    370         ls->obj_atom_id = pe->obj_atom_id;
    371         ls->segment_id = LINK_SEG_NONE;
    372         ls->obj_offset = pe->obj_offset;
    373         ls->input_offset = ofs;
    374         ls->file_offset = ofs;
    375         ls->vaddr = ofs;
    376         ls->size = pe->size;
    377         ls->flags = s->flags;
    378         ls->align = align;
    379         ls->name = s->name;
    380         ls->sem = (s->kind == SEC_BSS) ? SSEM_NOBITS : s->sem;
    381         ls->segment_id = (LinkSegmentId)(bucket + 1u); /* 1..3 sentinel */
    382         map_placed_unit(m, pe->obj_sec_id, pe->obj_atom_id, lsid);
    383       }
    384     }
    385   }
    386 
    387   if (total_kept) PlaceGroupHash_fini(&group_map);
    388   if (groups) h->free(h, groups, sizeof(*groups) * total_kept);
    389   if (entries) h->free(h, entries, sizeof(*entries) * total_kept);
    390 
    391   /* Materialize one LinkSegment per non-empty bucket, then assign
    392    * absolute (image-relative) vaddr/file_offset to each segment and
    393    * fix up section.{vaddr,file_offset,segment_id}. */
    394   {
    395     LinkSegmentId bucket_seg[SEG_NBUCKETS] = {0};
    396     u64 cursor = 0;
    397     u32 b;
    398     u32 nseg = 0;
    399     for (b = 0; b < SEG_NBUCKETS; ++b)
    400       if (seg_count[b]) ++nseg;
    401 
    402     img->segments =
    403         nseg ? (LinkSegment*)h->alloc(h, sizeof(*img->segments) * nseg,
    404                                       _Alignof(LinkSegment))
    405              : NULL;
    406     img->segment_bytes =
    407         nseg ? (u8**)h->alloc(h, sizeof(*img->segment_bytes) * nseg,
    408                               _Alignof(u8*))
    409              : NULL;
    410     img->segment_bytes_cap =
    411         nseg ? (size_t*)h->alloc(h, sizeof(*img->segment_bytes_cap) * nseg,
    412                                  _Alignof(size_t))
    413              : NULL;
    414     if (nseg &&
    415         (!img->segments || !img->segment_bytes || !img->segment_bytes_cap))
    416       compiler_panic(img->c, SRCLOC_NONE, "link: oom on segments");
    417     if (nseg) {
    418       memset(img->segment_bytes, 0, sizeof(*img->segment_bytes) * nseg);
    419       memset(img->segment_bytes_cap, 0, sizeof(*img->segment_bytes_cap) * nseg);
    420     }
    421 
    422     for (b = 0; b < SEG_NBUCKETS; ++b) {
    423       LinkSegment* seg;
    424       u64 file_size, mem_size, align;
    425       u32 nat_align;
    426       u32 perms;
    427       if (!seg_count[b]) continue;
    428       nat_align = seg_align[b] ? seg_align[b] : 1u;
    429       align = (u64)nat_align;
    430       if (align < link_layout_page_size(l)) align = link_layout_page_size(l);
    431       cursor = ALIGN_UP(cursor, (u64)(align));
    432 
    433       seg = &img->segments[img->nsegments];
    434       file_size = seg_size[b];
    435       mem_size = seg_size[b] + seg_bss_extra[b];
    436       perms = SF_ALLOC;
    437       if (b == SEG_RX) perms |= SF_EXEC;
    438       if (b == SEG_RW) perms |= SF_WRITE;
    439       if (b == SEG_TLS) perms |= SF_TLS;
    440 
    441       memset(seg, 0, sizeof(*seg));
    442       seg->id = (LinkSegmentId)(img->nsegments + 1u);
    443       seg->flags = perms;
    444       seg->file_offset = cursor;
    445       seg->vaddr = cursor;
    446       seg->paddr = cursor;
    447       seg->mem_size = mem_size;
    448       seg->file_size = file_size;
    449       seg->align = (u32)align;
    450       seg->nsections = seg_count[b];
    451       bucket_seg[b] = seg->id;
    452       if (b == SEG_TLS) {
    453         /* Record TLS image span for PT_TLS emission and TLSLE
    454          * reloc apply.  tls_align preserves the natural section
    455          * alignment (PT_TLS p_align), distinct from the
    456          * containing PT_LOAD's page align. */
    457         img->tls_vaddr = cursor;
    458         img->tls_filesz = file_size;
    459         img->tls_memsz = mem_size;
    460         img->tls_align = nat_align;
    461       }
    462       cursor += mem_size;
    463       img->nsegments++;
    464     }
    465 
    466     /* Allocate segment buffers and fix up section offsets/vaddrs.  The
    467      * JIT lane maps input section bytes directly into execmem, so ordinary
    468      * segment payload buffers would be copied only to be copied again. */
    469     for (b = 0; b < SEG_NBUCKETS; ++b) {
    470       if (!bucket_seg[b]) continue;
    471       {
    472         LinkSegment* seg = &img->segments[bucket_seg[b] - 1];
    473         if (seg->file_size && !l->jit_mode) {
    474           img->segment_bytes[bucket_seg[b] - 1] =
    475               (u8*)h->alloc(h, (size_t)seg->file_size, 16);
    476           if (!img->segment_bytes[bucket_seg[b] - 1])
    477             compiler_panic(img->c, SRCLOC_NONE, "link: oom on segment bytes");
    478           img->segment_bytes_cap[bucket_seg[b] - 1] = (size_t)seg->file_size;
    479           memset(img->segment_bytes[bucket_seg[b] - 1], 0,
    480                  (size_t)seg->file_size);
    481         }
    482       }
    483     }
    484 
    485     for (j = 0; j < img->nsections; ++j) {
    486       LinkSection* ls = &img->sections[j];
    487       u32 b2 = (u32)(ls->segment_id - 1u); /* sentinel-stash */
    488       LinkSegment* seg = &img->segments[bucket_seg[b2] - 1];
    489       ls->segment_id = seg->id;
    490       ls->vaddr += seg->vaddr;
    491       ls->file_offset += seg->file_offset;
    492     }
    493   }
    494 }
    495 
    496 /* ---- scripted layout (linker-script driven) ----
    497  *
    498  * Walks the KitLinkScript's output sections in declaration order,
    499  * placing matched input sections at the dot location counter. One
    500  * LinkSegment per non-DISCARD output section maps 1:1 to a PT_LOAD on
    501  * emit. Symbol assignments (top-level and in-section) materialize as
    502  * defined LinkSymbol globals via link_emit_boundary_sym.
    503  *
    504  * Discard handling: `/DISCARD/` matches input sections by glob and
    505  * leaves their per-input m->section[id] entry as LINK_SEC_NONE — the
    506  * downstream emit_reloc_records / link_assign_symbol_vaddrs passes
    507  * already treat that as "section dropped" so they're naturally
    508  * excluded from segments, gc, and reloc apply. */
    509 
    510 /* `*` is the only metachar. Supported forms in the kernel.lds-style
    511  * subset: trailing star (".text*"), leading star ("*COMMON" — not in
    512  * kernel.lds but cheap), and exact literal. */
    513 static int match_glob(const char* pat, const char* name) {
    514   size_t plen, nlen;
    515   if (!pat || !name) return 0;
    516   plen = slice_from_cstr(pat).len;
    517   nlen = slice_from_cstr(name).len;
    518   if (plen == 1 && pat[0] == '*') return 1;
    519   if (plen >= 2 && pat[plen - 1] == '*') {
    520     if (nlen + 1 < plen) return 0;
    521     return memcmp(pat, name, plen - 1) == 0;
    522   }
    523   if (plen >= 2 && pat[0] == '*') {
    524     if (nlen + 1 < plen) return 0;
    525     return memcmp(pat + 1, name + (nlen - (plen - 1)), plen - 1) == 0;
    526   }
    527   return plen == nlen && memcmp(pat, name, plen) == 0;
    528 }
    529 
    530 typedef struct ScriptOutInfo {
    531   KitSlice name;
    532   u64 vma;
    533   u64 lma;
    534   u64 size;
    535   int defined;
    536 } ScriptOutInfo;
    537 
    538 static const KitLinkRegion* script_find_region(const KitLinkScript* script,
    539                                                KitSlice name) {
    540   u32 i;
    541   if (!script || !name.s) return NULL;
    542   for (i = 0; i < script->nregions; ++i)
    543     if (slice_eq(script->regions[i].name, name)) return &script->regions[i];
    544   return NULL;
    545 }
    546 
    547 static int script_region_index(const KitLinkScript* script, KitSlice name,
    548                                u32* out) {
    549   u32 i;
    550   if (!script || !name.s) return 0;
    551   for (i = 0; i < script->nregions; ++i) {
    552     if (slice_eq(script->regions[i].name, name)) {
    553       if (out) *out = i;
    554       return 1;
    555     }
    556   }
    557   return 0;
    558 }
    559 
    560 static const ScriptOutInfo* script_find_out(const ScriptOutInfo* outs,
    561                                             u32 nouts, KitSlice name) {
    562   u32 i;
    563   if (!name.s) return NULL;
    564   for (i = 0; i < nouts; ++i)
    565     if (outs[i].defined && slice_eq(outs[i].name, name)) return &outs[i];
    566   return NULL;
    567 }
    568 
    569 static const KitLinkPhdr* script_find_phdr(const KitLinkScript* script,
    570                                            KitSlice name) {
    571   u32 i;
    572   if (!script || !name.s) return NULL;
    573   for (i = 0; i < script->nphdrs; ++i)
    574     if (slice_eq(script->phdrs[i].name, name)) return &script->phdrs[i];
    575   return NULL;
    576 }
    577 
    578 static u64 script_sizeof_headers(Linker* l) {
    579   const KitLinkScript* s = l ? l->script : NULL;
    580   u32 nph = 0;
    581   u32 i;
    582   u64 eh = (l && l->c && l->c->target.ptr_size == 4) ? 52u : 64u;
    583   u64 ph = (l && l->c && l->c->target.ptr_size == 4) ? 32u : 56u;
    584   if (!s) return eh;
    585   for (i = 0; i < s->nsections; ++i)
    586     if (!slice_eq_cstr(s->sections[i].name, "/DISCARD/")) ++nph;
    587   if (s->nphdrs > nph) nph = s->nphdrs;
    588   return eh + (u64)nph * ph;
    589 }
    590 
    591 static char script_ascii_lower(char c) {
    592   return (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : c;
    593 }
    594 
    595 static int script_slice_contains_lit_ci(KitSlice s, const char* lit) {
    596   size_t lit_len = 0;
    597   size_t i, j;
    598   if (!s.s || !lit) return 0;
    599   while (lit[lit_len]) ++lit_len;
    600   if (lit_len == 0 || s.len < lit_len) return 0;
    601   for (i = 0; i <= s.len - lit_len; ++i) {
    602     for (j = 0; j < lit_len; ++j) {
    603       if (script_ascii_lower(s.s[i + j]) != script_ascii_lower(lit[j]))
    604         break;
    605     }
    606     if (j == lit_len) return 1;
    607   }
    608   return 0;
    609 }
    610 
    611 static int script_format_name(KitSlice name, KitObjFmt* out) {
    612   if (script_slice_contains_lit_ci(name, "elf")) {
    613     *out = KIT_OBJ_ELF;
    614     return 1;
    615   }
    616   if (script_slice_contains_lit_ci(name, "mach-o") ||
    617       script_slice_contains_lit_ci(name, "macho")) {
    618     *out = KIT_OBJ_MACHO;
    619     return 1;
    620   }
    621   if (script_slice_contains_lit_ci(name, "coff") ||
    622       script_slice_contains_lit_ci(name, "pei-") ||
    623       script_slice_contains_lit_ci(name, "pe-")) {
    624     *out = KIT_OBJ_COFF;
    625     return 1;
    626   }
    627   if (script_slice_contains_lit_ci(name, "wasm")) {
    628     *out = KIT_OBJ_WASM;
    629     return 1;
    630   }
    631   return 0;
    632 }
    633 
    634 static int script_arch_name(KitSlice name, KitArchKind* out) {
    635   if (script_slice_contains_lit_ci(name, "aarch64") ||
    636       script_slice_contains_lit_ci(name, "arm64")) {
    637     *out = KIT_ARCH_ARM_64;
    638     return 1;
    639   }
    640   if (script_slice_contains_lit_ci(name, "x86_64") ||
    641       script_slice_contains_lit_ci(name, "x86-64") ||
    642       script_slice_contains_lit_ci(name, "amd64")) {
    643     *out = KIT_ARCH_X86_64;
    644     return 1;
    645   }
    646   if (script_slice_contains_lit_ci(name, "riscv64") ||
    647       script_slice_contains_lit_ci(name, "rv64")) {
    648     *out = KIT_ARCH_RV64;
    649     return 1;
    650   }
    651   if (script_slice_contains_lit_ci(name, "riscv32") ||
    652       script_slice_contains_lit_ci(name, "rv32")) {
    653     *out = KIT_ARCH_RV32;
    654     return 1;
    655   }
    656   if (script_slice_contains_lit_ci(name, "wasm")) {
    657     *out = KIT_ARCH_WASM;
    658     return 1;
    659   }
    660   if (script_slice_contains_lit_ci(name, "i386") ||
    661       script_slice_contains_lit_ci(name, "i686") ||
    662       script_slice_contains_lit_ci(name, "x86")) {
    663     *out = KIT_ARCH_X86_32;
    664     return 1;
    665   }
    666   if (script_slice_contains_lit_ci(name, "arm")) {
    667     *out = KIT_ARCH_ARM_32;
    668     return 1;
    669   }
    670   return 0;
    671 }
    672 
    673 static void validate_script_target(Linker* l, const KitLinkScript* script) {
    674   KitObjFmt fmt;
    675   KitArchKind arch;
    676   if (!l || !script) return;
    677   if (script->output_format.s && script->output_format.len &&
    678       script_format_name(script->output_format, &fmt) &&
    679       fmt != l->c->target.obj) {
    680     compiler_panic(l->c, SRCLOC_NONE,
    681                    "linker script: OUTPUT_FORMAT '%.*s' does not match target",
    682                    SLICE_ARG(script->output_format));
    683   }
    684   if (script->output_arch.s && script->output_arch.len &&
    685       script_arch_name(script->output_arch, &arch) &&
    686       arch != l->c->target.arch) {
    687     compiler_panic(l->c, SRCLOC_NONE,
    688                    "linker script: OUTPUT_ARCH '%.*s' does not match target",
    689                    SLICE_ARG(script->output_arch));
    690   }
    691 }
    692 
    693 /* Maximum link-script expression evaluation depth — mirrors the parser's
    694  * LSP_MAX_EXPR_DEPTH so eval fails cleanly (sets *err) instead of overflowing
    695  * the stack on a pathologically nested expression. */
    696 #define EVAL_LINK_MAX_DEPTH 256
    697 
    698 static u64 eval_link_expr_rec(Linker* l, LinkImage* img, u64 dot,
    699                               const ScriptOutInfo* outs, u32 nouts,
    700                               int depth, const KitLinkExpr* e, int* err) {
    701   if (!e) {
    702     *err = 1;
    703     return 0;
    704   }
    705   if (depth > EVAL_LINK_MAX_DEPTH) {
    706     *err = 1;
    707     return 0;
    708   }
    709   switch ((KitLinkExprKind)e->kind) {
    710     case KIT_LE_INT:
    711       return (u64)e->v.int_val;
    712     case KIT_LE_DOT:
    713       return dot;
    714     case KIT_LE_SYM: {
    715       Sym name = pool_intern_slice(l->c->global, e->v.name);
    716       LinkSymId id = symhash_get(&img->globals, name);
    717       if (id == LINK_SYM_NONE) {
    718         compiler_panic(l->c, SRCLOC_NONE,
    719                        "linker script: undefined symbol '%.*s' in expression",
    720                        SLICE_ARG(pool_slice(l->c->global, name)));
    721       }
    722       return LinkSyms_at(&img->syms, id - 1)->vaddr;
    723     }
    724     case KIT_LE_NEG:
    725       return (u64)(-(i64)eval_link_expr_rec(l, img, dot, outs, nouts,
    726                                             depth + 1, e->v.align.val, err));
    727     case KIT_LE_ADD:
    728       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) +
    729              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    730     case KIT_LE_SUB:
    731       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) -
    732              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    733     case KIT_LE_MUL:
    734       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) *
    735              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    736     case KIT_LE_DIV: {
    737       u64 rhs = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    738       if (rhs == 0) {
    739         *err = 1;
    740         return 0;
    741       }
    742       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) / rhs;
    743     }
    744     case KIT_LE_AND:
    745       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) &
    746              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    747     case KIT_LE_OR:
    748       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) |
    749              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    750     case KIT_LE_XOR:
    751       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) ^
    752              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    753     case KIT_LE_SHL:
    754       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err)
    755              << eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    756     case KIT_LE_SHR:
    757       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err) >>
    758              eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    759     case KIT_LE_MAX: {
    760       u64 a = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err);
    761       u64 b = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    762       return a > b ? a : b;
    763     }
    764     case KIT_LE_MIN: {
    765       u64 a = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.lhs, err);
    766       u64 b = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.bin.rhs, err);
    767       return a < b ? a : b;
    768     }
    769     case KIT_LE_ALIGN: {
    770       u64 v = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.align.val, err);
    771       u64 a =
    772           eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.align.align, err);
    773       if (a == 0) return v;
    774       return ALIGN_UP(v, a);
    775     }
    776     case KIT_LE_BLOCK: {
    777       u64 v = eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.align.val, err);
    778       u64 a =
    779           eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.align.align, err);
    780       if (a == 0) return v;
    781       return ALIGN_UP(v, a);
    782     }
    783     case KIT_LE_REGION_ORIGIN: {
    784       const KitLinkRegion* r = script_find_region(l->script, e->v.name);
    785       if (!r) {
    786         compiler_panic(l->c, SRCLOC_NONE,
    787                        "linker script: unknown MEMORY region '%.*s'",
    788                        SLICE_ARG(e->v.name));
    789       }
    790       return r->origin;
    791     }
    792     case KIT_LE_REGION_LENGTH: {
    793       const KitLinkRegion* r = script_find_region(l->script, e->v.name);
    794       if (!r) {
    795         compiler_panic(l->c, SRCLOC_NONE,
    796                        "linker script: unknown MEMORY region '%.*s'",
    797                        SLICE_ARG(e->v.name));
    798       }
    799       return r->length;
    800     }
    801     case KIT_LE_ADDR: {
    802       const ScriptOutInfo* info = script_find_out(outs, nouts, e->v.name);
    803       if (!info)
    804         compiler_panic(l->c, SRCLOC_NONE,
    805                        "linker script: ADDR of unknown output section '%.*s'",
    806                        SLICE_ARG(e->v.name));
    807       return info->vma;
    808     }
    809     case KIT_LE_LOADADDR: {
    810       const ScriptOutInfo* info = script_find_out(outs, nouts, e->v.name);
    811       if (!info)
    812         compiler_panic(
    813             l->c, SRCLOC_NONE,
    814             "linker script: LOADADDR of unknown output section '%.*s'",
    815             SLICE_ARG(e->v.name));
    816       return info->lma;
    817     }
    818     case KIT_LE_SIZEOF: {
    819       const ScriptOutInfo* info = script_find_out(outs, nouts, e->v.name);
    820       if (!info)
    821         compiler_panic(l->c, SRCLOC_NONE,
    822                        "linker script: SIZEOF of unknown output section '%.*s'",
    823                        SLICE_ARG(e->v.name));
    824       return info->size;
    825     }
    826     case KIT_LE_SIZEOF_HEADERS:
    827       return script_sizeof_headers(l);
    828     case KIT_LE_DEFINED: {
    829       Sym name = pool_intern_slice(l->c->global, e->v.name);
    830       LinkSymId id = symhash_get(&img->globals, name);
    831       const LinkSymbol* s =
    832           (id != LINK_SYM_NONE) ? LinkSyms_at(&img->syms, id - 1) : NULL;
    833       return (s && s->defined) ? 1u : 0u;
    834     }
    835     case KIT_LE_ABSOLUTE:
    836       return eval_link_expr_rec(l, img, dot, outs, nouts, depth + 1, e->v.align.val, err);
    837     default:
    838       compiler_panic(l->c, SRCLOC_NONE,
    839                      "linker script: expression kind %u not supported",
    840                      (unsigned)e->kind);
    841       return 0;
    842   }
    843 }
    844 
    845 /* Depth-tracked entry point: callers evaluate from depth 0. */
    846 static u64 eval_link_expr(Linker* l, LinkImage* img, u64 dot,
    847                           const ScriptOutInfo* outs, u32 nouts,
    848                           const KitLinkExpr* e, int* err) {
    849   return eval_link_expr_rec(l, img, dot, outs, nouts, 0, e, err);
    850 }
    851 
    852 /* Set a segment's phdr attributes (type/filehdr/phdrs/flags) from the named
    853  * PHDRS entry `name`. Panics if the name is unknown. `dot`/`outs`/`nouts` are
    854  * forwarded to the FLAGS() expression evaluation. */
    855 static void script_apply_phdr(Linker* l, LinkImage* img,
    856                               const KitLinkScript* script, LinkSegment* seg,
    857                               KitSlice name, u64 dot, const ScriptOutInfo* outs,
    858                               u32 nouts) {
    859   const KitLinkPhdr* ph = script_find_phdr(script, name);
    860   if (!ph)
    861     compiler_panic(l->c, SRCLOC_NONE, "linker script: unknown PHDR '%.*s'",
    862                    SLICE_ARG(name));
    863   seg->phdr_type = ph->type;
    864   seg->phdr_filehdr = ph->filehdr ? 1u : 0u;
    865   seg->phdr_phdrs = ph->phdrs ? 1u : 0u;
    866   if (ph->flags) {
    867     int err = 0;
    868     seg->phdr_flags = (u32)eval_link_expr(l, img, dot, outs, nouts, ph->flags,
    869                                           &err);
    870     if (err)
    871       compiler_panic(l->c, SRCLOC_NONE,
    872                      "linker script: invalid PHDR FLAGS for '%.*s'",
    873                      SLICE_ARG(ph->name));
    874     seg->phdr_flags_set = 1u;
    875   }
    876 }
    877 
    878 /* Format-aware C-symbol mangling for linker-synthesized boundaries. */
    879 static Sym boundary_name(Linker* l, const char* name) {
    880   return obj_format_c_mangle(l->c, name);
    881 }
    882 
    883 /* Upsert a global boundary symbol. Satisfies any prior undef ref in place; fans
    884  * out to per-input duplicate name slots. `section_id` is LINK_SEC_NONE for
    885  * absolute boundaries, or the owning LinkSectionId for boundaries that must
    886  * follow a format-specific section relayout. */
    887 static void link_emit_boundary_sym_ex(Linker* l, LinkImage* img,
    888                                       const char* name, u64 vaddr,
    889                                       LinkSectionId section_id, u64 value) {
    890   Sym sym = boundary_name(l, name);
    891   LinkSymId id = symhash_get(&img->globals, sym);
    892   LinkSymId canonical_id = id;
    893   LinkSymbol rec;
    894   u8 kind = SK_OBJ;
    895   int fmt_kind;
    896   u32 i, n;
    897   /* Some formats own specific boundary symbols with a fixed SymKind
    898    * (PE/COFF `__ImageBase` / `_tls_used` are SK_ABS). Ask the format
    899    * instead of matching names here. */
    900   if (obj_format_boundary_sym_kind(l->c, slice_from_cstr(name), &fmt_kind))
    901     kind = (u8)fmt_kind;
    902   memset(&rec, 0, sizeof(rec));
    903   rec.name = sym;
    904   rec.kind = kind;
    905   rec.defined = 1;
    906   rec.section_id = section_id;
    907   rec.value = value;
    908   rec.vaddr = vaddr;
    909   rec.bind = SB_GLOBAL;
    910   if (id != LINK_SYM_NONE) {
    911     *LinkSyms_at(&img->syms, id - 1) = rec;
    912     LinkSyms_at(&img->syms, id - 1)->id = id;
    913   } else {
    914     LinkSymId fresh = link_append_symbol(img, &rec);
    915     symhash_insert(&img->globals, sym, fresh, &id);
    916     canonical_id = fresh;
    917   }
    918   n = LinkSyms_count(&img->syms);
    919   for (i = 0; i < n; ++i) {
    920     LinkSymbol* s = LinkSyms_at(&img->syms, i);
    921     if (s->name != sym) continue;
    922     if (s->id == canonical_id) continue;
    923     if (s->bind == SB_LOCAL) continue;
    924     s->section_id = section_id;
    925     s->value = value;
    926     s->vaddr = vaddr;
    927     s->kind = kind;
    928     s->defined = 1;
    929     s->imported = 0;
    930   }
    931 }
    932 
    933 void link_emit_boundary_sym(Linker* l, LinkImage* img, const char* name,
    934                             u64 vaddr) {
    935   link_emit_boundary_sym_ex(l, img, name, vaddr, LINK_SEC_NONE, 0);
    936 }
    937 
    938 void link_emit_section_boundary_sym(Linker* l, LinkImage* img, const char* name,
    939                                     LinkSectionId section_id, u64 value) {
    940   const LinkSection* sec;
    941   u64 vaddr;
    942   if (section_id == LINK_SEC_NONE || section_id > img->nsections)
    943     compiler_panic(img->c, SRCLOC_NONE,
    944                    "link: boundary symbol '%.*s' has no containing section",
    945                    SLICE_ARG(slice_from_cstr(name)));
    946   sec = &img->sections[section_id - 1];
    947   vaddr = sec->vaddr + (value - sec->obj_offset);
    948   link_emit_boundary_sym_ex(l, img, name, vaddr, section_id, value);
    949 }
    950 
    951 /* link_define_boundary: public alias used by link_dyn.c. */
    952 void link_define_boundary(Linker* l, LinkImage* img, const char* name,
    953                           u64 vaddr) {
    954   link_emit_boundary_sym(l, img, name, vaddr);
    955 }
    956 
    957 /* Upsert a global symbol (mirror of emit_boundary_sym, used by apply_asn). */
    958 static void upsert_global_sym(Linker* l, LinkImage* img, KitSlice name,
    959                               u64 vaddr) {
    960   /* Script sym slices are arena-interned and NUL-terminated; the boundary
    961    * emitter mangles via obj_format_c_mangle which needs a C string. */
    962   link_emit_boundary_sym(l, img, name.s, vaddr);
    963 }
    964 
    965 /* Apply one KitLinkAssignment. */
    966 static int script_symbol_defined(Linker* l, LinkImage* img, KitSlice name) {
    967   Sym sym;
    968   LinkSymId id;
    969   const LinkSymbol* s;
    970   if (!name.s) return 0;
    971   sym = pool_intern_slice(l->c->global, name);
    972   id = symhash_get(&img->globals, sym);
    973   if (id == LINK_SYM_NONE) return 0;
    974   s = LinkSyms_at(&img->syms, id - 1);
    975   return s && s->defined;
    976 }
    977 
    978 static void apply_asn(Linker* l, LinkImage* img, u64* dot,
    979                       const ScriptOutInfo* outs, u32 nouts,
    980                       const KitLinkAssignment* asn) {
    981   int err = 0;
    982   u64 v = eval_link_expr(l, img, *dot, outs, nouts, asn->expr, &err);
    983   if (err) return;
    984   switch ((KitLinkAsnKind)asn->kind) {
    985     case KIT_LAS_DOT:
    986       if (v < *dot)
    987         compiler_panic(l->c, SRCLOC_NONE,
    988                        "linker script: dot moved backwards (%llu -> %llu)",
    989                        (unsigned long long)*dot, (unsigned long long)v);
    990       *dot = v;
    991       break;
    992     case KIT_LAS_SYM:
    993     case KIT_LAS_HIDDEN:
    994       if (asn->sym.s) upsert_global_sym(l, img, asn->sym, v);
    995       break;
    996     case KIT_LAS_PROVIDE:
    997     case KIT_LAS_PROVIDE_HIDDEN:
    998       if (asn->sym.s && !script_symbol_defined(l, img, asn->sym))
    999         upsert_global_sym(l, img, asn->sym, v);
   1000       break;
   1001   }
   1002 }
   1003 
   1004 /* Apply every KIT_LAS_DOT top-level assignment whose textual `seq` is below
   1005  * `seq_limit` and that has not yet been applied (tracked by *cursor), so the
   1006  * location counter advances at the assignment's source position. top_asns is
   1007  * already in textual order, so a single forward cursor suffices. Non-DOT
   1008  * top-level assignments (symbol defs / PROVIDE) are applied in a later pass
   1009  * once all section addresses are known. */
   1010 static void apply_dot_asns_before(Linker* l, LinkImage* img, u64* dot,
   1011                                   const ScriptOutInfo* outs, u32 nouts,
   1012                                   const KitLinkScript* script, u32* cursor,
   1013                                   u32 seq_limit) {
   1014   while (*cursor < script->ntop_asns &&
   1015          script->top_asns[*cursor].seq < seq_limit) {
   1016     const KitLinkAssignment* a = &script->top_asns[*cursor];
   1017     if (a->kind == KIT_LAS_DOT) apply_asn(l, img, dot, outs, nouts, a);
   1018     ++*cursor;
   1019   }
   1020 }
   1021 
   1022 static const char* link_basename(const char* s) {
   1023   const char* base = s;
   1024   if (!s) return NULL;
   1025   for (; *s; ++s)
   1026     if (*s == '/' || *s == '\\') base = s + 1;
   1027   return base;
   1028 }
   1029 
   1030 static int input_match_file(Linker* l, u32 ii, const KitLinkInputMatch* m) {
   1031   const LinkInput* in;
   1032   Slice name = SLICE_NULL;
   1033   const char* full;
   1034   const char* base;
   1035   u32 i;
   1036   if (!m->file_pattern.s && m->nexclude_file_patterns == 0) return 1;
   1037   if (ii >= LinkInputs_count(&l->inputs)) return 0;
   1038   in = LinkInputs_at(&l->inputs, ii);
   1039   if (in->name) name = pool_slice(l->c->global, in->name);
   1040   full = name.s;
   1041   base = link_basename(full);
   1042   if (m->file_pattern.s) {
   1043     if (!full) return 0;
   1044     if (!match_glob(m->file_pattern.s, full) &&
   1045         !match_glob(m->file_pattern.s, base))
   1046       return 0;
   1047   }
   1048   for (i = 0; i < m->nexclude_file_patterns; ++i) {
   1049     const char* pat = m->exclude_file_patterns[i].s;
   1050     if (!pat || !full) continue;
   1051     if (match_glob(pat, full) || match_glob(pat, base)) return 0;
   1052   }
   1053   return 1;
   1054 }
   1055 
   1056 static int input_match_section(Linker* l, u32 ii, const KitLinkInputMatch* m,
   1057                                const char* nm) {
   1058   /* section_pattern is an arena-interned, NUL-terminated span of the
   1059    * script text; match_glob scans it as a C string. */
   1060   return input_match_file(l, ii, m) && match_glob(m->section_pattern.s, nm);
   1061 }
   1062 
   1063 /* Fill `n` bytes of `dst` with the linker-script FILL pattern. GNU ld lays a
   1064  * fill value down as a big-endian byte pattern of `width` bytes (the value's
   1065  * significant byte count; default 4) repeated across the region. The pattern
   1066  * is phase-aligned to the start of the buffer, so gaps anywhere in a section
   1067  * see a consistent repeat. width is clamped to [1,8]. */
   1068 static void script_pattern_fill(u8* dst, size_t n, u64 value, u32 width) {
   1069   size_t i;
   1070   u8 pat[8];
   1071   u32 w = width ? (width > 8u ? 8u : width) : 1u;
   1072   u32 b;
   1073   /* big-endian: most-significant of the `w` low bytes first */
   1074   for (b = 0; b < w; ++b)
   1075     pat[b] = (u8)(value >> (8u * (w - 1u - b)));
   1076   for (i = 0; i < n; ++i) dst[i] = pat[i % w];
   1077 }
   1078 
   1079 static u32 script_output_input_align(Linker* l, LinkImage* img,
   1080                                      const GcLive* g,
   1081                                      const KitLinkOutputSection* os,
   1082                                      u8** claimed) {
   1083   u32 align_max = 1;
   1084   u32 mi, ii, j;
   1085   for (mi = 0; mi < os->ninputs; ++mi) {
   1086     const KitLinkInputMatch* im = &os->inputs[mi];
   1087     for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1088       ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1089       InputMap* m = &img->input_maps[ii];
   1090       for (j = 1; j < obj_section_count(ob); ++j) {
   1091         const Section* s;
   1092         const char* nm;
   1093         u32 align;
   1094         if (claimed[ii][j]) continue;
   1095         if (m->comdat_discarded[j]) continue;
   1096         s = obj_section_get(ob, j);
   1097         if (!s || !link_section_kept(s)) continue;
   1098         nm = pool_slice(l->c->global, s->name).s;
   1099         if (!nm) continue;
   1100         if (!input_match_section(l, ii, im, nm)) continue;
   1101         if (!live_section_units(g, m, ii, ob, j)) continue;
   1102         align = s->align ? s->align : 1u;
   1103         if (align > align_max) align_max = align;
   1104       }
   1105     }
   1106   }
   1107   return align_max;
   1108 }
   1109 
   1110 static void link_layout_sections_scripted(Linker* l, LinkImage* img,
   1111                                           const GcLive* g) {
   1112   Heap* h = img->heap;
   1113   const KitLinkScript* script = l->script;
   1114   u64 dot = 0;
   1115   u64 file_cursor = 0;
   1116   u32 ii, j, k, si;
   1117   u32 total_kept = 0;
   1118 
   1119   img->scripted = 1;
   1120   validate_script_target(l, script);
   1121 
   1122   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1123     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1124     InputMap* m = &img->input_maps[ii];
   1125     for (j = 1; j < obj_section_count(ob); ++j) {
   1126       const Section* s = obj_section_get(ob, j);
   1127       if (s && link_section_kept(s) && !m->comdat_discarded[j])
   1128         total_kept += live_section_units(g, m, ii, ob, j);
   1129     }
   1130   }
   1131 
   1132   img->sections = total_kept ? (LinkSection*)h->alloc(
   1133                                    h, sizeof(*img->sections) * total_kept,
   1134                                    _Alignof(LinkSection))
   1135                              : NULL;
   1136   if (total_kept && !img->sections)
   1137     compiler_panic(img->c, SRCLOC_NONE, "link: oom on sections");
   1138 
   1139   u8** claimed = NULL;
   1140   ScriptOutInfo* outs = NULL;
   1141   u64* region_cursor = NULL;
   1142   u64* load_region_cursor = NULL;
   1143   if (LinkInputs_count(&l->inputs)) {
   1144     u32 ni = LinkInputs_count(&l->inputs);
   1145     claimed = (u8**)h->alloc(h, sizeof(*claimed) * ni, _Alignof(u8*));
   1146     if (!claimed) compiler_panic(img->c, SRCLOC_NONE, "link: oom on claim map");
   1147     for (ii = 0; ii < ni; ++ii) {
   1148       ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1149       u32 nsec = obj_section_count(ob);
   1150       claimed[ii] = (u8*)h->alloc(h, nsec, 1);
   1151       if (!claimed[ii])
   1152         compiler_panic(img->c, SRCLOC_NONE, "link: oom on claim row");
   1153       memset(claimed[ii], 0, nsec);
   1154     }
   1155   }
   1156   if (script->nsections) {
   1157     outs = (ScriptOutInfo*)h->alloc(h, sizeof(*outs) * script->nsections,
   1158                                     _Alignof(ScriptOutInfo));
   1159     if (!outs) compiler_panic(img->c, SRCLOC_NONE, "link: oom on script map");
   1160     memset(outs, 0, sizeof(*outs) * script->nsections);
   1161   }
   1162   if (script->nregions) {
   1163     region_cursor =
   1164         (u64*)h->alloc(h, sizeof(*region_cursor) * script->nregions,
   1165                        _Alignof(u64));
   1166     load_region_cursor =
   1167         (u64*)h->alloc(h, sizeof(*load_region_cursor) * script->nregions,
   1168                        _Alignof(u64));
   1169     if (!region_cursor || !load_region_cursor)
   1170       compiler_panic(img->c, SRCLOC_NONE, "link: oom on region cursors");
   1171     for (ii = 0; ii < script->nregions; ++ii) {
   1172       region_cursor[ii] = script->regions[ii].origin;
   1173       load_region_cursor[ii] = script->regions[ii].origin;
   1174     }
   1175   }
   1176 
   1177   /* Inter-section dot assignments (`. = ALIGN(N)` / `. = expr` written
   1178    * between two output sections) must advance the location counter at their
   1179    * textual position. top_asns and sections each carry a shared `seq`; walk
   1180    * the DOT assignments in seq order, applying those that precede the next
   1181    * section right before that section is laid out. dot_cursor tracks the
   1182    * next unapplied top_asn. */
   1183   u32 dot_cursor = 0;
   1184 
   1185   /* Upper bound on segments: each non-discard section contributes at least
   1186    * one, plus one per *additional* :phdr it names (a section listing several
   1187    * phdrs appears under each as its own program header). Coalescing sections
   1188    * that share a phdr only reduces the count, so this stays an upper bound. */
   1189   u32 nseg_max = 0;
   1190   for (si = 0; si < script->nsections; ++si) {
   1191     const KitLinkOutputSection* os_ = &script->sections[si];
   1192     if (slice_eq_cstr(os_->name, "/DISCARD/")) continue;
   1193     nseg_max += os_->nphdrs ? os_->nphdrs : 1u;
   1194   }
   1195   img->segments =
   1196       nseg_max ? (LinkSegment*)h->alloc(h, sizeof(*img->segments) * nseg_max,
   1197                                         _Alignof(LinkSegment))
   1198                : NULL;
   1199   img->segment_bytes =
   1200       nseg_max ? (u8**)h->alloc(h, sizeof(*img->segment_bytes) * nseg_max,
   1201                                 _Alignof(u8*))
   1202                : NULL;
   1203   img->segment_bytes_cap =
   1204       nseg_max
   1205           ? (size_t*)h->alloc(h, sizeof(*img->segment_bytes_cap) * nseg_max,
   1206                               _Alignof(size_t))
   1207           : NULL;
   1208   if (nseg_max &&
   1209       (!img->segments || !img->segment_bytes || !img->segment_bytes_cap))
   1210     compiler_panic(img->c, SRCLOC_NONE, "link: oom on segments");
   1211   if (nseg_max) {
   1212     memset(img->segment_bytes, 0, sizeof(*img->segment_bytes) * nseg_max);
   1213     memset(img->segment_bytes_cap, 0,
   1214            sizeof(*img->segment_bytes_cap) * nseg_max);
   1215   }
   1216 
   1217   /* Per-segment primary :phdr name (parallel to img->segments while we build
   1218    * it); a new section naming the same phdr as the currently-open segment and
   1219    * vaddr-contiguous with it coalesces into that one PT_LOAD. Kept local — no
   1220    * LinkSegment field needed. open_seg is the index of the coalescable open
   1221    * segment, or (u32)-1 when none can be extended. */
   1222   KitSlice* seg_phdr_name = NULL;
   1223   u32 open_seg = (u32)-1;
   1224   if (nseg_max) {
   1225     seg_phdr_name =
   1226         (KitSlice*)h->alloc(h, sizeof(*seg_phdr_name) * nseg_max,
   1227                             _Alignof(KitSlice));
   1228     if (!seg_phdr_name)
   1229       compiler_panic(img->c, SRCLOC_NONE, "link: oom on segment phdr names");
   1230     memset(seg_phdr_name, 0, sizeof(*seg_phdr_name) * nseg_max);
   1231   }
   1232 
   1233   for (si = 0; si < script->nsections; ++si) {
   1234     const KitLinkOutputSection* os = &script->sections[si];
   1235     int is_discard = slice_eq_cstr(os->name, "/DISCARD/");
   1236     /* Apply any inter-section dot assignments that textually precede this
   1237      * output section before placing it. */
   1238     apply_dot_asns_before(l, img, &dot, outs, script->nsections, script,
   1239                           &dot_cursor, os->seq);
   1240     if (outs) outs[si].name = os->name;
   1241 
   1242     if (is_discard) {
   1243       u32 mi;
   1244       for (mi = 0; mi < os->ninputs; ++mi) {
   1245         const KitLinkInputMatch* im = &os->inputs[mi];
   1246         for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1247           ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1248           for (j = 1; j < obj_section_count(ob); ++j) {
   1249             const Section* s;
   1250             const char* nm;
   1251             if (claimed[ii][j]) continue;
   1252             s = obj_section_get(ob, j);
   1253             if (!s) continue;
   1254             {
   1255               nm = pool_slice(l->c->global, s->name).s;
   1256             }
   1257             if (!nm) continue;
   1258             if (input_match_section(l, ii, im, nm)) claimed[ii][j] = 1;
   1259           }
   1260         }
   1261       }
   1262       continue;
   1263     }
   1264 
   1265     u64 sec_start_dot;
   1266     u64 lma_start;
   1267     u64 subalign_val = 0;
   1268     /* GNU ld fill: a big-endian byte pattern repeated across gaps; a plain
   1269      * integer FILL value is 4 bytes wide. fill_value carries the full value;
   1270      * fill_width its significant byte count (default 4). */
   1271     u64 fill_value = 0;
   1272     u32 fill_width = 4;
   1273     u32 perms = 0;
   1274     LinkSegmentId seg_id = (LinkSegmentId)(img->nsegments + 1u);
   1275     LinkSegment* seg;
   1276     u64 file_size_accum = 0;
   1277     u64 mem_size_accum = 0;
   1278     u32 align_max = 1;
   1279     u32 nsec_in_seg = 0;
   1280     u32 first_section_idx = img->nsections;
   1281     u32 region_idx = 0;
   1282     u32 load_region_idx = 0;
   1283     int has_region = 0;
   1284     int has_load_region = 0;
   1285 
   1286     if (os->region.s) {
   1287       has_region = script_region_index(script, os->region, &region_idx);
   1288       if (!has_region)
   1289         compiler_panic(l->c, SRCLOC_NONE,
   1290                        "linker script: unknown MEMORY region '%.*s'",
   1291                        SLICE_ARG(os->region));
   1292     }
   1293     if (os->load_region.s) {
   1294       has_load_region =
   1295           script_region_index(script, os->load_region, &load_region_idx);
   1296       if (!has_load_region)
   1297         compiler_panic(l->c, SRCLOC_NONE,
   1298                        "linker script: unknown load MEMORY region '%.*s'",
   1299                        SLICE_ARG(os->load_region));
   1300     }
   1301 
   1302     align_max = script_output_input_align(l, img, g, os, claimed);
   1303     if (os->vma) {
   1304       int err = 0;
   1305       dot = eval_link_expr(l, img, dot, outs, script->nsections, os->vma, &err);
   1306       if (err)
   1307         compiler_panic(l->c, SRCLOC_NONE,
   1308                        "linker script: invalid VMA expression for '%.*s'",
   1309                        SLICE_ARG(os->name));
   1310     } else if (has_region) {
   1311       dot = region_cursor[region_idx];
   1312     }
   1313     dot = ALIGN_UP(dot, (u64)align_max);
   1314     if (os->subalign) {
   1315       int err = 0;
   1316       subalign_val =
   1317           eval_link_expr(l, img, dot, outs, script->nsections, os->subalign,
   1318                          &err);
   1319       if (err)
   1320         compiler_panic(l->c, SRCLOC_NONE,
   1321                        "linker script: invalid SUBALIGN for '%.*s'",
   1322                        SLICE_ARG(os->name));
   1323     }
   1324     if (os->fill) {
   1325       int err = 0;
   1326       u64 fv =
   1327           eval_link_expr(l, img, dot, outs, script->nsections, os->fill, &err);
   1328       if (err)
   1329         compiler_panic(l->c, SRCLOC_NONE,
   1330                        "linker script: invalid fill expression for '%.*s'",
   1331                        SLICE_ARG(os->name));
   1332       fill_value = fv;
   1333     }
   1334     /* Body command 0: the injected `: ALIGN(N)` header (if any) sets the
   1335      * section base before any content is placed. Apply only body_seq-0
   1336      * assignments here, then capture sec_start_dot. The remaining body
   1337      * commands (input-section globs and interior `sym = .` / `. = expr`
   1338      * assignments) execute below in true source order. */
   1339     for (k = 0; k < os->nasns; ++k) {
   1340       if (os->asns[k].body_seq == 0)
   1341         apply_asn(l, img, &dot, outs, script->nsections, &os->asns[k]);
   1342     }
   1343     sec_start_dot = dot;
   1344 
   1345     /* Interleave input-section placement and interior assignments by their
   1346      * source-order body_seq. GNU ld executes section-body commands in order,
   1347      * so a `sym = .` after a `*(.data*)` glob captures `.` AFTER the glob's
   1348      * content, and a `. = ALIGN(N)` after the last glob extends the section.
   1349      * Both os->asns (body_seq >= 1) and os->inputs are stamped in ascending
   1350      * source order, so a single forward cursor over each suffices. */
   1351     {
   1352       u32 ak = 0;
   1353       u32 mi = 0;
   1354       /* Skip past the body_seq-0 header assignments already applied above. */
   1355       while (ak < os->nasns && os->asns[ak].body_seq == 0) ++ak;
   1356       for (;;) {
   1357         int have_asn = ak < os->nasns;
   1358         int have_in = mi < os->ninputs;
   1359         if (!have_asn && !have_in) break;
   1360         /* Apply the assignment whose body_seq is strictly before the next
   1361          * pending input command (or all trailing assignments when no inputs
   1362          * remain). */
   1363         if (have_asn &&
   1364             (!have_in || os->asns[ak].body_seq < os->inputs[mi].body_seq)) {
   1365           apply_asn(l, img, &dot, outs, script->nsections, &os->asns[ak]);
   1366           /* A `. = expr` that advances dot past the placed content extends the
   1367            * section span (GNU ld grows the output section to the new dot). The
   1368            * gap also becomes interior file padding when it follows emitted
   1369            * PROGBITS bytes in a loadable section (file_size_accum already
   1370            * nonzero); a gap before any file content stays bss-like. */
   1371           if (dot - sec_start_dot > mem_size_accum)
   1372             mem_size_accum = dot - sec_start_dot;
   1373           if (!os->noload && file_size_accum &&
   1374               dot - sec_start_dot > file_size_accum)
   1375             file_size_accum = dot - sec_start_dot;
   1376           ++ak;
   1377           continue;
   1378         }
   1379         {
   1380           const KitLinkInputMatch* im = &os->inputs[mi];
   1381           for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1382             ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1383             InputMap* m = &img->input_maps[ii];
   1384             for (j = 1; j < obj_section_count(ob); ++j) {
   1385               const Section* s;
   1386               const char* nm;
   1387               u32 align;
   1388               u32 first = 0, count = 1, ai;
   1389               int has_atoms;
   1390               if (claimed[ii][j]) continue;
   1391               if (m->comdat_discarded[j]) continue;
   1392               s = obj_section_get(ob, j);
   1393               if (!s || !link_section_kept(s)) continue;
   1394               {
   1395                 nm = pool_slice(l->c->global, s->name).s;
   1396               }
   1397               if (!nm) continue;
   1398               if (!input_match_section(l, ii, im, nm)) continue;
   1399 
   1400               align =
   1401                   subalign_val ? (u32)subalign_val : (s->align ? s->align : 1u);
   1402               has_atoms = link_input_section_has_atoms(m, j);
   1403               if (has_atoms) link_input_section_atoms(m, j, &first, &count);
   1404               for (ai = 0; ai < count; ++ai) {
   1405                 ObjAtomId aid =
   1406                     has_atoms ? m->section_atom_ids[first + ai] : OBJ_ATOM_NONE;
   1407                 const ObjAtom* atom = has_atoms ? obj_atom_get(ob, aid) : NULL;
   1408                 u64 ofs;
   1409                 LinkSection* ls;
   1410                 LinkSectionId lsid;
   1411                 u64 obj_offset;
   1412                 u64 size;
   1413                 if (has_atoms) {
   1414                   if (!atom || atom->removed) continue;
   1415                   if (!link_gc_atom_live_get(g, ii, aid)) continue;
   1416                   obj_offset = atom->offset;
   1417                   size = atom->size;
   1418                 } else if (!link_gc_live_get(g, ii, j)) {
   1419                   continue;
   1420                 } else {
   1421                   obj_offset = 0u;
   1422                   size = link_section_size_for_link(s);
   1423                 }
   1424                 if (align > align_max) align_max = align;
   1425                 dot = ALIGN_UP(dot, (u64)align);
   1426                 ofs = dot;
   1427 
   1428                 lsid = (LinkSectionId)(img->nsections + 1u);
   1429                 ls = &img->sections[img->nsections++];
   1430                 memset(ls, 0, sizeof(*ls));
   1431                 ls->id = lsid;
   1432                 ls->input_id = LinkInputs_at(&l->inputs, ii)->id;
   1433                 ls->obj_section_id = j;
   1434                 ls->obj_atom_id = aid;
   1435                 ls->segment_id = seg_id;
   1436                 ls->obj_offset = obj_offset;
   1437                 ls->vaddr = ofs;
   1438                 ls->size = size;
   1439                 ls->flags = s->flags;
   1440                 ls->align = align;
   1441                 ls->name = s->name;
   1442                 /* A (NOLOAD) output section produces no file bytes regardless
   1443                  * of the input section's PROGBITS/NOBITS semantics: mark every
   1444                  * placed unit NOBITS so the byte-copy
   1445                  * (link_emit_segment_bytes) and the segment shape below both
   1446                  * treat it as bss. */
   1447                 ls->sem = (os->noload || s->kind == SEC_BSS) ? SSEM_NOBITS
   1448                                                              : s->sem;
   1449                 ls->file_offset = ofs - sec_start_dot;
   1450                 ls->input_offset = ls->file_offset;
   1451                 map_placed_unit(m, j, aid, lsid);
   1452 
   1453                 dot += ls->size;
   1454                 mem_size_accum = dot - sec_start_dot;
   1455                 if (!os->noload && ls->sem != SSEM_NOBITS)
   1456                   file_size_accum = dot - sec_start_dot;
   1457                 perms |= (s->flags & (SF_EXEC | SF_WRITE | SF_TLS));
   1458                 ++nsec_in_seg;
   1459               }
   1460               claimed[ii][j] = 1;
   1461             }
   1462           }
   1463           ++mi;
   1464         }
   1465       }
   1466     }
   1467 
   1468     if (os->lma) {
   1469       int err = 0;
   1470       lma_start =
   1471           eval_link_expr(l, img, dot, outs, script->nsections, os->lma, &err);
   1472       if (err)
   1473         compiler_panic(l->c, SRCLOC_NONE,
   1474                        "linker script: invalid AT() expression for '%.*s'",
   1475                        SLICE_ARG(os->name));
   1476     } else if (has_load_region) {
   1477       lma_start = ALIGN_UP(load_region_cursor[load_region_idx],
   1478                            (u64)(align_max ? align_max : 1u));
   1479       load_region_cursor[load_region_idx] = lma_start + file_size_accum;
   1480     } else {
   1481       lma_start = sec_start_dot;
   1482       if (has_region && load_region_cursor) {
   1483         u64 lend = lma_start + file_size_accum;
   1484         if (lend > load_region_cursor[region_idx])
   1485           load_region_cursor[region_idx] = lend;
   1486       }
   1487     }
   1488 
   1489     if (has_region) {
   1490       const KitLinkRegion* r = &script->regions[region_idx];
   1491       u64 end = dot;
   1492       /* Bug 3: a section spanning [sec_start_dot, end) fits region r iff
   1493        * sec_start_dot >= origin and (end - origin) <= length. Compute the
   1494        * end-fits test by subtraction, never `origin + length`, which wraps to
   1495        * 0 for a canonical high-half region (e.g. ORIGIN=0xFFFFFFFF80000000,
   1496        * LENGTH=0x80000000) and would falsely flag a legal placement. */
   1497       if (sec_start_dot < r->origin || end < r->origin ||
   1498           end - r->origin > r->length)
   1499         compiler_panic(
   1500             l->c, SRCLOC_NONE,
   1501             "linker script: section '%.*s' overflows MEMORY region '%.*s'",
   1502             SLICE_ARG(os->name), SLICE_ARG(r->name));
   1503       region_cursor[region_idx] = end;
   1504     }
   1505     if (has_load_region && file_size_accum) {
   1506       const KitLinkRegion* r = &script->regions[load_region_idx];
   1507       u64 lend = lma_start + file_size_accum;
   1508       /* Bug 3: same wrap-safe form for the load image [lma_start, lend). */
   1509       if (lma_start < r->origin || lend < r->origin ||
   1510           lend - r->origin > r->length)
   1511         compiler_panic(
   1512             l->c, SRCLOC_NONE,
   1513             "linker script: load image for '%.*s' overflows MEMORY region "
   1514             "'%.*s'",
   1515             SLICE_ARG(os->name), SLICE_ARG(r->name));
   1516     }
   1517     if (outs) {
   1518       outs[si].vma = sec_start_dot;
   1519       outs[si].lma = lma_start;
   1520       outs[si].size = mem_size_accum;
   1521       outs[si].defined = 1;
   1522     }
   1523 
   1524     if (nsec_in_seg == 0) {
   1525       continue;
   1526     }
   1527 
   1528     /* (a) Coalesce: if this section's PRIMARY :phdr matches the currently-open
   1529      * segment and the section is vaddr-contiguous with it, extend that segment
   1530      * into one PT_LOAD instead of opening a new one. A section listing several
   1531      * phdrs may still coalesce its primary here; its additional phdrs become
   1532      * alias program headers below covering just this section's range. FLAGS()
   1533      * / AT() seal a segment (they override the geometry) and never coalesce. */
   1534     KitSlice primary_phdr = os->nphdrs ? os->phdrs[0] : KIT_SLICE_NULL;
   1535     int can_coalesce =
   1536         open_seg != (u32)-1 && primary_phdr.s && seg_phdr_name[open_seg].s &&
   1537         slice_eq(seg_phdr_name[open_seg], primary_phdr) &&
   1538         sec_start_dot == img->segments[open_seg].vaddr +
   1539                              img->segments[open_seg].mem_size &&
   1540         /* Bug 1: never coalesce onto an open segment that ends in an internal
   1541          * NOBITS (bss) tail. Coalescing packs the follower's file bytes
   1542          * file-contiguously (after oseg->file_size) while it keeps its
   1543          * mem-contiguous vaddr; with file_size < mem_size that skews the
   1544          * follower's loaded bytes down by the tail size. Falling through to
   1545          * the else branch opens a correct, separate page-aligned PT_LOAD. */
   1546         img->segments[open_seg].file_size ==
   1547             img->segments[open_seg].mem_size &&
   1548         !os->lma && !os->flags;
   1549 
   1550     /* Per-section file geometry (used by both the primary placement and the
   1551      * alias program headers, which describe only this section's range). */
   1552     u64 this_file_off;
   1553     u64 this_vaddr = sec_start_dot;
   1554     u64 this_paddr = lma_start;
   1555 
   1556     if (can_coalesce) {
   1557       LinkSegment* oseg = &img->segments[open_seg];
   1558       u64 prev_file = oseg->file_size;
   1559       u64 new_file = prev_file + file_size_accum;
   1560       seg = oseg;
   1561       this_file_off = seg->file_offset + prev_file;
   1562       seg->mem_size += mem_size_accum;
   1563       /* This section's bytes follow the open segment's directly (no page
   1564        * break); rebase its LinkSections into the open segment. */
   1565       {
   1566         u32 fi;
   1567         for (fi = first_section_idx; fi < img->nsections; ++fi) {
   1568           LinkSection* ls = &img->sections[fi];
   1569           ls->segment_id = seg->id;
   1570           ls->file_offset = this_file_off + ls->file_offset;
   1571         }
   1572       }
   1573       file_cursor += file_size_accum;
   1574       if (file_size_accum && !l->jit_mode) {
   1575         u8* grown = (u8*)h->realloc(h, img->segment_bytes[open_seg],
   1576                                     img->segment_bytes_cap[open_seg],
   1577                                     (size_t)new_file, 16);
   1578         if (!grown)
   1579           compiler_panic(img->c, SRCLOC_NONE,
   1580                          "link: oom growing coalesced segment bytes");
   1581         img->segment_bytes[open_seg] = grown;
   1582         img->segment_bytes_cap[open_seg] = (size_t)new_file;
   1583         script_pattern_fill(grown + prev_file, (size_t)file_size_accum,
   1584                             fill_value, fill_width);
   1585       }
   1586       seg->file_size = new_file;
   1587       seg->nsections += nsec_in_seg;
   1588       /* Bug 2: a FLAGS-less PHDRS PT_LOAD maps p_flags from seg->flags
   1589        * (perms_to_pflags); a coalesced PT_LOAD must carry the UNION of all its
   1590        * mapped sections' perms (GNU-ld behavior), not just the first section's
   1591        * (set once at segment creation). Union only the p_flags-relevant bits
   1592        * (R/W/X) — a PT_LOAD's flags must never include SF_TLS, which the
   1593        * emitter treats specially (p_memsz collapses to file_size for SF_TLS
   1594        * segments); TLS sizing is owned by the dedicated PT_TLS segment.
   1595        * (Defensive: the scripted layout does not currently route SF_TLS into a
   1596        * coalesced section's perms, so this only narrows intent.) */
   1597       seg->flags |= SF_ALLOC | (perms & (SF_EXEC | SF_WRITE));
   1598     } else {
   1599       seg = &img->segments[img->nsegments];
   1600       memset(seg, 0, sizeof(*seg));
   1601       seg->id = seg_id;
   1602       seg->flags = SF_ALLOC | perms;
   1603       seg->vaddr = sec_start_dot;
   1604       seg->paddr = lma_start;
   1605       file_cursor = ALIGN_UP(file_cursor, (u64)PAGE_SIZE);
   1606       seg->file_offset = file_cursor;
   1607       this_file_off = seg->file_offset;
   1608       seg->mem_size = mem_size_accum;
   1609       seg->file_size = file_size_accum;
   1610       seg->align = align_max;
   1611       seg->nsections = nsec_in_seg;
   1612       /* Primary phdr names the segment's program-header attributes; the name
   1613        * is recorded so a following same-phdr contiguous section coalesces. */
   1614       if (os->nphdrs) {
   1615         script_apply_phdr(l, img, script, seg, os->phdrs[0], dot, outs,
   1616                           script->nsections);
   1617         seg_phdr_name[img->nsegments] = os->phdrs[0];
   1618       } else {
   1619         seg_phdr_name[img->nsegments] = KIT_SLICE_NULL;
   1620       }
   1621       if (os->flags) {
   1622         int err = 0;
   1623         seg->phdr_flags =
   1624             (u32)eval_link_expr(l, img, dot, outs, script->nsections,
   1625                                 os->flags, &err);
   1626         if (err)
   1627           compiler_panic(l->c, SRCLOC_NONE,
   1628                          "linker script: invalid FLAGS for section '%.*s'",
   1629                          SLICE_ARG(os->name));
   1630         seg->phdr_flags_set = 1u;
   1631       }
   1632       file_cursor += file_size_accum;
   1633       if (file_size_accum && !l->jit_mode) {
   1634         img->segment_bytes[img->nsegments] =
   1635             (u8*)h->alloc(h, (size_t)file_size_accum, 16);
   1636         if (!img->segment_bytes[img->nsegments])
   1637           compiler_panic(img->c, SRCLOC_NONE,
   1638                          "link: oom on scripted segment bytes");
   1639         img->segment_bytes_cap[img->nsegments] = (size_t)file_size_accum;
   1640         script_pattern_fill(img->segment_bytes[img->nsegments],
   1641                             (size_t)file_size_accum, fill_value, fill_width);
   1642       }
   1643       {
   1644         u32 fi;
   1645         for (fi = first_section_idx; fi < img->nsections; ++fi) {
   1646           LinkSection* ls = &img->sections[fi];
   1647           ls->file_offset = seg->file_offset + (ls->file_offset);
   1648         }
   1649       }
   1650       /* A primary segment (phdr-named, no FLAGS/AT) is coalescable by a
   1651        * following same-phdr contiguous section. No-phdr / FLAGS / AT segments
   1652        * are sealed (NULL name never matches a phdr-named follower, matching
   1653        * the pre-coalesce per-section behavior). */
   1654       open_seg = (os->nphdrs && !os->flags && !os->lma) ? img->nsegments
   1655                                                         : (u32)-1;
   1656       img->nsegments++;
   1657     }
   1658 
   1659     /* (b) Each ADDITIONAL :phdr the section names yields an alias program
   1660      * header covering only THIS section's vaddr/file range, with no byte
   1661      * buffer of its own (the bytes are on disk via the primary; the ELF byte
   1662      * writer skips NULL-buffer segments). */
   1663     {
   1664       u32 pi;
   1665       for (pi = 1; pi < os->nphdrs; ++pi) {
   1666         LinkSegment* aseg = &img->segments[img->nsegments];
   1667         memset(aseg, 0, sizeof(*aseg));
   1668         aseg->id = (LinkSegmentId)(img->nsegments + 1u);
   1669         aseg->flags = SF_ALLOC | perms;
   1670         aseg->vaddr = this_vaddr;
   1671         aseg->paddr = this_paddr;
   1672         aseg->file_offset = this_file_off;
   1673         aseg->mem_size = mem_size_accum;
   1674         aseg->file_size = file_size_accum;
   1675         aseg->align = align_max;
   1676         aseg->nsections = 0; /* aliases own no sections */
   1677         script_apply_phdr(l, img, script, aseg, os->phdrs[pi], dot, outs,
   1678                           script->nsections);
   1679         seg_phdr_name[img->nsegments] = os->phdrs[pi];
   1680         img->segment_bytes[img->nsegments] = NULL;
   1681         img->segment_bytes_cap[img->nsegments] = 0;
   1682         img->nsegments++;
   1683       }
   1684     }
   1685   }
   1686 
   1687   /* Apply any trailing dot assignments after the last output section. */
   1688   apply_dot_asns_before(l, img, &dot, outs, script->nsections, script,
   1689                         &dot_cursor, UINT32_MAX);
   1690 
   1691   for (k = 0; k < script->ntop_asns; ++k) {
   1692     const KitLinkAssignment* a = &script->top_asns[k];
   1693     if (a->kind != KIT_LAS_DOT)
   1694       apply_asn(l, img, &dot, outs, script->nsections, a);
   1695   }
   1696 
   1697   for (k = 0; k < script->nasserts; ++k) {
   1698     int err = 0;
   1699     u64 ok = eval_link_expr(l, img, dot, outs, script->nsections,
   1700                             script->asserts[k].expr, &err);
   1701     if (err || !ok) {
   1702       KitSlice msg = script->asserts[k].message;
   1703       compiler_panic(l->c, SRCLOC_NONE, "linker script ASSERT failed: %.*s",
   1704                      SLICE_ARG(msg));
   1705     }
   1706   }
   1707 
   1708   if (claimed) {
   1709     u32 ni = LinkInputs_count(&l->inputs);
   1710     for (ii = 0; ii < ni; ++ii) {
   1711       ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1712       h->free(h, claimed[ii], obj_section_count(ob));
   1713     }
   1714     h->free(h, claimed, sizeof(*claimed) * ni);
   1715   }
   1716   if (outs) h->free(h, outs, sizeof(*outs) * script->nsections);
   1717   if (seg_phdr_name)
   1718     h->free(h, seg_phdr_name, sizeof(*seg_phdr_name) * nseg_max);
   1719   if (region_cursor)
   1720     h->free(h, region_cursor, sizeof(*region_cursor) * script->nregions);
   1721   if (load_region_cursor)
   1722     h->free(h, load_region_cursor,
   1723             sizeof(*load_region_cursor) * script->nregions);
   1724 }
   1725 
   1726 /* ---- pass 2b: COMMON symbol BSS allocation ---- */
   1727 
   1728 void link_layout_commons(Linker* l, LinkImage* img) {
   1729   u32 i;
   1730   (void)l;
   1731   LinkSegment* rw_seg = NULL;
   1732 
   1733   for (i = 0; i < img->nsegments; ++i) {
   1734     if (img->segments[i].flags & SF_WRITE) {
   1735       rw_seg = &img->segments[i];
   1736       break;
   1737     }
   1738   }
   1739 
   1740   {
   1741     int has_common = 0;
   1742     for (i = 0; i < LinkSyms_count(&img->syms); ++i)
   1743       if (LinkSyms_at(&img->syms, i)->kind == SK_COMMON &&
   1744           LinkSyms_at(&img->syms, i)->defined) {
   1745         has_common = 1;
   1746         break;
   1747       }
   1748     if (!has_common) return;
   1749   }
   1750 
   1751   if (!rw_seg) {
   1752     /* No writable PT_LOAD yet — synthesize a zero-size BSS segment for the
   1753      * COMMON section. This is a NOBITS region (no byte buffer, file/mem
   1754      * size grow with the section below), so it adopts only the array-
   1755      * growth helper, not the fixed-size region builder. */
   1756     u32 seg_idx = link_iplt_alloc_segments(img, 1u);
   1757     u64 vaddr = 0;
   1758     for (i = 0; i < img->nsegments; ++i) {
   1759       u64 end = img->segments[i].vaddr + img->segments[i].mem_size;
   1760       if (end > vaddr) vaddr = end;
   1761     }
   1762     vaddr = ALIGN_UP(vaddr, (u64)(link_layout_page_size(l)));
   1763     rw_seg = &img->segments[seg_idx];
   1764     memset(rw_seg, 0, sizeof(*rw_seg));
   1765     rw_seg->id = (LinkSegmentId)(seg_idx + 1u);
   1766     rw_seg->flags = SF_ALLOC | SF_WRITE;
   1767     rw_seg->vaddr = vaddr;
   1768     rw_seg->paddr = vaddr;
   1769     rw_seg->file_offset = vaddr;
   1770     rw_seg->file_size = 0;
   1771     rw_seg->mem_size = 0;
   1772     rw_seg->align = (u32)link_layout_page_size(l);
   1773     img->segment_bytes[seg_idx] = NULL;
   1774     img->segment_bytes_cap[seg_idx] = 0;
   1775     img->nsegments++;
   1776   }
   1777 
   1778   {
   1779     Heap* h = img->heap;
   1780     u64 bss_start = rw_seg->vaddr + rw_seg->mem_size;
   1781     u64 bss_cursor = bss_start;
   1782     u32 max_align = 1u;
   1783     LinkSection* commsec;
   1784     LinkSectionId comm_lsid;
   1785 
   1786     for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
   1787       LinkSymbol* s = LinkSyms_at(&img->syms, i);
   1788       u32 align;
   1789       if (s->kind != SK_COMMON || !s->defined) continue;
   1790       align = s->common_align ? s->common_align : 1u;
   1791       if (align > max_align) max_align = align;
   1792       bss_cursor = ALIGN_UP(bss_cursor, (u64)(align));
   1793       s->value = bss_cursor - bss_start;
   1794       bss_cursor += s->size ? s->size : 1u;
   1795     }
   1796 
   1797     {
   1798       u32 new_nsec = img->nsections + 1u;
   1799       LinkSection* nsec = (LinkSection*)h->realloc(
   1800           h, img->sections, sizeof(*img->sections) * img->nsections,
   1801           sizeof(*img->sections) * new_nsec, _Alignof(LinkSection));
   1802       if (!nsec)
   1803         compiler_panic(img->c, SRCLOC_NONE, "link: oom on common section");
   1804       img->sections = nsec;
   1805     }
   1806     commsec = &img->sections[img->nsections];
   1807     memset(commsec, 0, sizeof(*commsec));
   1808     comm_lsid = (LinkSectionId)(img->nsections + 1u);
   1809     commsec->id = comm_lsid;
   1810     commsec->input_id = LINK_INPUT_NONE;
   1811     commsec->obj_section_id = OBJ_SEC_NONE;
   1812     commsec->segment_id = rw_seg->id;
   1813     commsec->input_offset = 0;
   1814     commsec->file_offset = bss_start;
   1815     commsec->vaddr = bss_start;
   1816     commsec->size = bss_cursor - bss_start;
   1817     commsec->flags = SF_ALLOC | SF_WRITE;
   1818     commsec->align = max_align;
   1819     commsec->name = pool_intern_slice(img->c->global, SLICE_LIT(".bss.common"));
   1820     commsec->sem = SSEM_NOBITS;
   1821     img->nsections++;
   1822 
   1823     for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
   1824       LinkSymbol* s = LinkSyms_at(&img->syms, i);
   1825       if (s->kind != SK_COMMON || !s->defined) continue;
   1826       s->section_id = comm_lsid;
   1827       s->vaddr = bss_start + s->value;
   1828       s->kind = SK_OBJ;
   1829     }
   1830 
   1831     rw_seg->mem_size = bss_cursor - rw_seg->vaddr;
   1832     rw_seg->nsections++;
   1833   }
   1834 }
   1835 
   1836 /* Copy each input section's bytes into its segment buffer. */
   1837 void link_emit_segment_bytes(Linker* l, LinkImage* img) {
   1838   u32 j;
   1839   (void)l;
   1840   for (j = 0; j < img->nsections; ++j) {
   1841     LinkSection* ls = &img->sections[j];
   1842     ObjBuilder* ob;
   1843     if (ls->input_id == LINK_INPUT_NONE) continue;
   1844     ob = LinkInputs_at(&l->inputs, ls->input_id - 1)->obj;
   1845     const Section* s = obj_section_get(ob, ls->obj_section_id);
   1846     LinkSegment* seg = &img->segments[ls->segment_id - 1];
   1847     u8* dst;
   1848     /* ls->sem carries the OUTPUT-section decision (e.g. (NOLOAD) forces
   1849      * NOBITS even for PROGBITS input), so check it too — the segment it
   1850      * lives in has no file bytes to copy into. */
   1851     if (!s || s->sem == SSEM_NOBITS || s->kind == SEC_BSS ||
   1852         ls->sem == SSEM_NOBITS)
   1853       continue;
   1854     if (ls->size == 0) continue;
   1855     dst = img->segment_bytes[seg->id - 1] +
   1856           (size_t)(ls->file_offset - seg->file_offset);
   1857     buf_read(&s->bytes, (u32)ls->obj_offset, dst, (size_t)ls->size);
   1858   }
   1859 }
   1860 
   1861 /* ---- pass 2c: file-only debug sections ----
   1862  *
   1863  * Carry every surviving .debug_* section through to the linked image as
   1864  * a non-segment, file-resident LinkSection so addr2line / gdb resolve
   1865  * file:line on the output.  Contributions of the same name are assigned
   1866  * a per-name cumulative `vaddr` (the DWARF-section-relative base: 0 for
   1867  * the first input, size0 for the second, …); the ELF emitter merges
   1868  * same-name contributions into one output .debug_X section, and the
   1869  * per-input base makes SK_SECTION cross-section R_ABS32 offsets land in
   1870  * the merged section.  Each contribution keeps its own byte buffer in
   1871  * the debug registry, with relocations applied in place at reloc-offset.
   1872  *
   1873  * Runs after link_emit_segment_bytes (so the segment-byte copy never
   1874  * sees these segment-less sections) and before link_assign_symbol_vaddrs
   1875  * (so the SK_SECTION debug symbols pick up their section_id + base). */
   1876 
   1877 /* Per-output-name cumulative base tracker. Debug section names are few
   1878  * (.debug_info/.debug_line/.debug_abbrev/.debug_str/...), so a linear
   1879  * scan is fine. */
   1880 typedef struct DbgNameAcc {
   1881   Sym name;
   1882   u64 cum; /* running total size for this name */
   1883 } DbgNameAcc;
   1884 
   1885 void link_layout_debug(Linker* l, LinkImage* img) {
   1886   Heap* h = img->heap;
   1887   u32 ii, j;
   1888   u32 ndbg = 0;
   1889 
   1890   /* Pass 0: count surviving debug contributions. */
   1891   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1892     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1893     InputMap* m = &img->input_maps[ii];
   1894     for (j = 1; j < obj_section_count(ob); ++j) {
   1895       const Section* s = obj_section_get(ob, j);
   1896       if (link_section_kept_fileonly(s) && !m->comdat_discarded[j] &&
   1897           link_section_size_for_link(s) > 0)
   1898         ++ndbg;
   1899     }
   1900   }
   1901   if (ndbg == 0) return;
   1902 
   1903   /* Grow img->sections to hold the new file-only sections appended after
   1904    * the existing allocatable + common sections. */
   1905   {
   1906     u32 new_nsec = img->nsections + ndbg;
   1907     LinkSection* ns = (LinkSection*)h->realloc(
   1908         h, img->sections, sizeof(*img->sections) * img->nsections,
   1909         sizeof(*img->sections) * new_nsec, _Alignof(LinkSection));
   1910     if (!ns) compiler_panic(img->c, SRCLOC_NONE, "link: oom on debug sections");
   1911     img->sections = ns;
   1912   }
   1913 
   1914   img->dbg_bytes =
   1915       (u8**)h->alloc(h, sizeof(*img->dbg_bytes) * ndbg, _Alignof(u8*));
   1916   img->dbg_size =
   1917       (u64*)h->alloc(h, sizeof(*img->dbg_size) * ndbg, _Alignof(u64));
   1918   DbgNameAcc* acc =
   1919       (DbgNameAcc*)h->alloc(h, sizeof(*acc) * ndbg, _Alignof(DbgNameAcc));
   1920   if (!img->dbg_bytes || !img->dbg_size || !acc)
   1921     compiler_panic(img->c, SRCLOC_NONE, "link: oom on debug registry");
   1922   memset(img->dbg_bytes, 0, sizeof(*img->dbg_bytes) * ndbg);
   1923   memset(img->dbg_size, 0, sizeof(*img->dbg_size) * ndbg);
   1924 
   1925   img->dbg_first_lsid = (LinkSectionId)(img->nsections + 1u);
   1926   img->dbg_count = 0;
   1927   u32 nacc = 0;
   1928 
   1929   /* Pass 1: append one file-only LinkSection per contribution, assign
   1930    * per-name cumulative base, and copy bytes into the registry. */
   1931   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1932     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1933     InputMap* m = &img->input_maps[ii];
   1934     for (j = 1; j < obj_section_count(ob); ++j) {
   1935       const Section* s = obj_section_get(ob, j);
   1936       u32 size;
   1937       u64 base;
   1938       u32 ai, slot;
   1939       LinkSection* ls;
   1940       LinkSectionId lsid;
   1941       u8* buf;
   1942       if (!link_section_kept_fileonly(s) || m->comdat_discarded[j]) continue;
   1943       size = link_section_size_for_link(s);
   1944       if (size == 0) continue;
   1945 
   1946       /* Per-name cumulative base. */
   1947       for (ai = 0; ai < nacc; ++ai)
   1948         if (acc[ai].name == s->name) break;
   1949       if (ai == nacc) {
   1950         acc[nacc].name = s->name;
   1951         acc[nacc].cum = 0;
   1952         ai = nacc++;
   1953       }
   1954       base = acc[ai].cum;
   1955       acc[ai].cum += size;
   1956 
   1957       slot = img->dbg_count;
   1958       lsid = (LinkSectionId)(img->nsections + 1u);
   1959       ls = &img->sections[img->nsections++];
   1960       memset(ls, 0, sizeof(*ls));
   1961       ls->id = lsid;
   1962       ls->input_id = LinkInputs_at(&l->inputs, ii)->id;
   1963       ls->obj_section_id = j;
   1964       ls->obj_atom_id = OBJ_ATOM_NONE;
   1965       ls->segment_id = LINK_SEG_NONE;
   1966       ls->obj_offset = 0;
   1967       ls->input_offset = 0;
   1968       ls->file_offset = 0; /* assigned by the ELF emitter */
   1969       ls->vaddr = base;    /* DWARF-section-relative base */
   1970       ls->size = size;
   1971       ls->flags = s->flags;
   1972       ls->align = s->align ? s->align : 1u;
   1973       ls->name = s->name;
   1974       ls->sem = SSEM_PROGBITS;
   1975       ls->file_only = 1u;
   1976 
   1977       /* Copy this contribution's bytes into its own registry buffer. */
   1978       buf = (u8*)h->alloc(h, size, 1);
   1979       if (!buf) compiler_panic(img->c, SRCLOC_NONE, "link: oom on debug bytes");
   1980       buf_read(&s->bytes, 0u, buf, (size_t)size);
   1981       img->dbg_bytes[slot] = buf;
   1982       img->dbg_size[slot] = size;
   1983       img->dbg_count++;
   1984 
   1985       /* Map the input's section id to this LinkSection so the SK_SECTION
   1986        * debug symbol resolves (assign_symbol_vaddrs) and its relocations
   1987        * route here (emit_relocations). */
   1988       map_placed_unit(m, j, OBJ_ATOM_NONE, lsid);
   1989     }
   1990   }
   1991 
   1992   h->free(h, acc, sizeof(*acc) * ndbg);
   1993 }
   1994 
   1995 u8* link_fileonly_bytes(LinkImage* img, LinkSectionId id) {
   1996   if (!img || id == LINK_SEC_NONE || id < img->dbg_first_lsid) return NULL;
   1997   {
   1998     u32 idx = (u32)(id - img->dbg_first_lsid);
   1999     if (idx >= img->dbg_count) return NULL;
   2000     return img->dbg_bytes[idx];
   2001   }
   2002 }
   2003 
   2004 /* ---- linker flags, policy, and strict validation (kernel-C work) ----
   2005  *
   2006  * These run inside link_resolve after layout (so alias targets / section
   2007  * vaddrs are known) and after symbol resolution (so dynamic-artifact and
   2008  * cross-input checks see every input). They are deliberately self-contained
   2009  * and only consult the borrowed option arrays plumbed onto the Linker. */
   2010 
   2011 /* Emit a non-fatal linker warning. With --fatal-warnings the warning is
   2012  * promoted to a hard error (compiler_panic, which unwinds the link). */
   2013 static void link_warn(Linker* l, const char* fmt, ...) {
   2014   va_list ap;
   2015   if (l->fatal_warnings) {
   2016     va_start(ap, fmt);
   2017     compiler_panicv(l->c, SRCLOC_NONE, fmt, ap);
   2018     va_end(ap); /* unreachable; compiler_panicv is _Noreturn */
   2019   }
   2020   if (l->c && l->c->ctx && l->c->ctx->diag) {
   2021     va_start(ap, fmt);
   2022     diag_emitv(l->c->ctx->diag, DIAG_WARN, SRCLOC_NONE, fmt, ap);
   2023     va_end(ap);
   2024   }
   2025 }
   2026 
   2027 /* --defsym: define each requested symbol. Two forms:
   2028  *   NAME=0x1234    — an absolute symbol carrying the literal value verbatim.
   2029  *   NAME=othersym  — an alias that resolves to othersym's address (+optional
   2030  *                    bias). The alias copies the target's kind/section/value so
   2031  *                    it rebases identically at emit (matching GNU ld, where the
   2032  *                    alias takes the target's final address). Run after layout
   2033  *                    and undef resolution so the target has a settled vaddr. */
   2034 static void link_apply_defsyms(Linker* l, LinkImage* img) {
   2035   u32 i;
   2036   for (i = 0; i < l->ndefsyms; ++i) {
   2037     const KitLinkDefsym* d = &l->defsyms[i];
   2038     char namebuf[256];
   2039     size_t nlen;
   2040     Sym sym;
   2041     LinkSymId existing;
   2042     LinkSymbol rec;
   2043     if (!d->name.s || d->name.len == 0) continue;
   2044     nlen =
   2045         d->name.len < sizeof(namebuf) - 1u ? d->name.len : sizeof(namebuf) - 1u;
   2046     memcpy(namebuf, d->name.s, nlen);
   2047     namebuf[nlen] = '\0';
   2048     memset(&rec, 0, sizeof(rec));
   2049     sym = pool_intern_slice(l->c->global, (Slice){.s = namebuf, .len = (u32)nlen});
   2050     rec.name = sym;
   2051     rec.bind = SB_GLOBAL;
   2052     rec.defined = 1;
   2053     if (d->alias.s && d->alias.len) {
   2054       Sym tgt = pool_intern_slice(l->c->global,
   2055                                   (Slice){.s = d->alias.s, .len = d->alias.len});
   2056       LinkSymId tid = symhash_get(&img->globals, tgt);
   2057       const LinkSymbol* t;
   2058       if (tid == LINK_SYM_NONE) {
   2059         compiler_panic(l->c, SRCLOC_NONE,
   2060                        "link: --defsym '%s': undefined alias target '%.*s'",
   2061                        namebuf, (int)d->alias.len, d->alias.s);
   2062       }
   2063       t = LinkSyms_at(&img->syms, tid - 1);
   2064       /* Inherit the target's placement so the alias rebases identically. */
   2065       rec.kind = t->kind;
   2066       rec.section_id = t->section_id;
   2067       rec.value = t->value;
   2068       rec.vaddr = t->vaddr + d->value;
   2069       rec.imported = t->imported;
   2070       rec.dso_input_id = t->dso_input_id;
   2071     } else {
   2072       rec.kind = SK_ABS;
   2073       rec.vaddr = d->value;
   2074       rec.value = d->value;
   2075     }
   2076     existing = symhash_get(&img->globals, sym);
   2077     if (existing != LINK_SYM_NONE) {
   2078       rec.id = existing;
   2079       *LinkSyms_at(&img->syms, existing - 1) = rec;
   2080     } else {
   2081       LinkSymId fresh = link_append_symbol(img, &rec);
   2082       symhash_insert(&img->globals, sym, fresh, &existing);
   2083     }
   2084   }
   2085 }
   2086 
   2087 /* --section-start / -Tdata / -Tbss: force a named output section's *final*
   2088  * runtime address to exactly the requested value (GNU-ld semantics). The actual
   2089  * placement is deferred to the format emitter (ELF: link_apply_section_starts
   2090  * in src/obj/elf/link.c, applied AFTER shift_image_addresses so the headers
   2091  * load-page and the runtime image base both fall out of the delta) — at layout
   2092  * time we only validate, since the image-relative vaddr here is not yet in the
   2093  * final coordinate system. Ignored under a script (which pins addresses
   2094  * itself). This pass emits the diagnostics (it has Linker / link_warn); the
   2095  * matched section's segment is shifted by the emitter to land it exactly. */
   2096 static void link_apply_section_starts(Linker* l, LinkImage* img) {
   2097   u32 i, j;
   2098   if (l->script) {
   2099     if (l->nsection_starts)
   2100       link_warn(l,
   2101                 "link: --section-start/-Tdata/-Tbss ignored under a linker "
   2102                 "script");
   2103     return;
   2104   }
   2105   for (i = 0; i < l->nsection_starts; ++i) {
   2106     const KitLinkSectionStart* ss = &l->section_starts[i];
   2107     int matched = 0;
   2108     if (!ss->name.s || ss->name.len == 0) continue;
   2109     for (j = 0; j < img->nsections; ++j) {
   2110       LinkSection* sec = &img->sections[j];
   2111       Slice nm = sec->name ? pool_slice(l->c->global, sec->name) : SLICE_NULL;
   2112       if (!nm.s || nm.len != ss->name.len ||
   2113           memcmp(nm.s, ss->name.s, nm.len) != 0)
   2114         continue;
   2115       matched = 1;
   2116     }
   2117     if (!matched)
   2118       link_warn(l, "link: --section-start: no output section named '%.*s'",
   2119                 (int)ss->name.len, ss->name.s);
   2120   }
   2121 }
   2122 
   2123 /* Strict freestanding validation: reject dynamic-link artifacts on a
   2124  * freestanding/static-non-PIE link. Runs after symbol resolution so every
   2125  * input (and any DT_NEEDED-style import) is visible. The DSO-input and
   2126  * dynamic-section checks generalize the existing relocatable-only DSO guard
   2127  * to the freestanding executable case. --allow-undefined remains the escape
   2128  * hatch for undefined references, but it does not relax these structural
   2129  * rejections (a freestanding image has no dynamic loader to honor them). */
   2130 static int link_section_name_is_dynamic(Slice nm) {
   2131   /* The dynamic-link sections a freestanding static image must not carry. */
   2132   static const char* const dyn[] = {".dynamic", ".dynsym", ".dynstr",
   2133                                      ".rela.plt", ".rel.plt", ".plt",
   2134                                      ".got.plt", ".rela.dyn", ".rel.dyn",
   2135                                      ".interp"};
   2136   u32 i;
   2137   if (!nm.s || nm.len == 0) return 0;
   2138   for (i = 0; i < (u32)(sizeof(dyn) / sizeof(dyn[0])); ++i) {
   2139     size_t dl = strlen(dyn[i]);
   2140     if (nm.len == dl && memcmp(nm.s, dyn[i], dl) == 0) return 1;
   2141   }
   2142   return 0;
   2143 }
   2144 
   2145 static void link_validate_freestanding(Linker* l) {
   2146   u32 ii;
   2147   if (!l->freestanding_strict) return;
   2148 
   2149   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   2150     LinkInput* in = LinkInputs_at(&l->inputs, ii);
   2151     ObjBuilder* ob = in->obj;
   2152     Slice inname = in->name ? pool_slice(l->c->global, in->name) : SLICE_NULL;
   2153     const char* label = inname.s ? inname.s : "<input>";
   2154     u32 j;
   2155 
   2156     /* (c) DSO inputs: a shared object has no place in a freestanding image. */
   2157     if (in->kind == LINK_INPUT_DSO_BYTES) {
   2158       compiler_panic(l->c, SRCLOC_NONE,
   2159                      "link: freestanding link rejects shared-object input "
   2160                      "'%s' (no dynamic loader)",
   2161                      label);
   2162     }
   2163     if (!ob) continue;
   2164 
   2165     /* (a)+(b) dynamic-interpreter / dynamic-section / PLT-GOT-import inputs.
   2166      * A freestanding static image must not pull in .interp (PT_INTERP) or any
   2167      * .dynamic/.dynsym/.plt/.got.plt artifact. */
   2168     for (j = 1; j < obj_section_count(ob); ++j) {
   2169       const Section* s = obj_section_get(ob, j);
   2170       Slice nm;
   2171       if (!s || s->removed) continue;
   2172       nm = s->name ? pool_slice(l->c->global, s->name) : SLICE_NULL;
   2173       if (link_section_name_is_dynamic(nm)) {
   2174         compiler_panic(l->c, SRCLOC_NONE,
   2175                        "link: freestanding link rejects dynamic section '%.*s' "
   2176                        "from input '%s'",
   2177                        (int)nm.len, nm.s, label);
   2178       }
   2179     }
   2180   }
   2181 }
   2182 
   2183 /* ---- public orchestration ---- */
   2184 
   2185 LinkImage* link_resolve(Linker* l) {
   2186   LinkImage* img;
   2187   Heap* h;
   2188 
   2189   metrics_scope_begin(l->c, "link.resolve.total");
   2190   /* Inject any format-owned synthetic input before symbol resolution.
   2191    * Only the COFF format registers a hook (link_synth_coff_ctor_dtor_list);
   2192    * the dispatcher is a no-op for every other format. */
   2193   obj_format_synth_inputs(l->c, l);
   2194   metrics_scope_begin(l->c, "link.ingest_archives");
   2195   link_ingest_archives(l);
   2196   metrics_scope_end(l->c, "link.ingest_archives");
   2197 
   2198   img = link_image_alloc(l->c);
   2199   h = img->heap;
   2200   img->linker = l;
   2201   img->text_base_set = l->text_base_set;
   2202   img->text_base = l->text_base;
   2203   img->shared = l->emit_shared;
   2204   img->elf_e_flags = l->elf_e_flags;
   2205   img->have_elf_e_flags = l->have_elf_e_flags;
   2206 
   2207   img->ninput_maps = LinkInputs_count(&l->inputs);
   2208   metrics_count(l->c, "link.inputs", img->ninput_maps);
   2209   img->input_maps =
   2210       LinkInputs_count(&l->inputs)
   2211           ? (InputMap*)h->alloc(
   2212                 h, sizeof(*img->input_maps) * LinkInputs_count(&l->inputs),
   2213                 _Alignof(InputMap))
   2214           : NULL;
   2215   if (LinkInputs_count(&l->inputs) && !img->input_maps)
   2216     compiler_panic(l->c, SRCLOC_NONE, "link: oom on input maps");
   2217   if (LinkInputs_count(&l->inputs))
   2218     memset(img->input_maps, 0,
   2219            sizeof(*img->input_maps) * LinkInputs_count(&l->inputs));
   2220 
   2221   metrics_scope_begin(l->c, "link.resolve_symbols");
   2222   link_resolve_symbols(l, img);
   2223   metrics_scope_end(l->c, "link.resolve_symbols");
   2224   /* Strict freestanding policy: reject dynamic-link artifacts and cross-input
   2225    * mismatches before laying anything out. DSO inputs / dynamic sections have
   2226    * no home in a freestanding static image (no dynamic loader). */
   2227   link_validate_freestanding(l);
   2228   {
   2229     GcLive g = {0};
   2230     metrics_scope_begin(l->c, "link.gc");
   2231     link_gc_live_alloc(&g, l, h);
   2232     link_gc_compute(l, img, &g);
   2233     metrics_scope_end(l->c, "link.gc");
   2234     metrics_scope_begin(l->c, "link.layout_sections");
   2235     link_layout_sections(l, img, &g);
   2236     link_layout_commons(l, img);
   2237     metrics_count(l->c, "link.sections", img->nsections);
   2238     metrics_count(l->c, "link.segments", img->nsegments);
   2239     metrics_scope_end(l->c, "link.layout_sections");
   2240     metrics_scope_begin(l->c, "link.emit_segment_bytes");
   2241     if (!l->jit_mode) link_emit_segment_bytes(l, img);
   2242     metrics_scope_end(l->c, "link.emit_segment_bytes");
   2243     /* Carry .debug_* through as file-only sections. ELF places them in
   2244      * non-alloc sections; Mach-O in a __DWARF segment. The JIT path
   2245      * serves debug via kit_jit_view instead, and COFF emit doesn't yet
   2246      * handle file-only sections. */
   2247     metrics_scope_begin(l->c, "link.layout_debug");
   2248     if (!l->strip_debug && !l->jit_mode &&
   2249         obj_format_carries_file_only_debug(l->c))
   2250       link_layout_debug(l, img);
   2251     metrics_scope_end(l->c, "link.layout_debug");
   2252     /* --section-start / -Tdata / -Tbss: pin chosen output sections to fixed
   2253      * addresses before symbol vaddrs are derived from them. */
   2254     link_apply_section_starts(l, img);
   2255     metrics_scope_begin(l->c, "link.assign_vaddrs");
   2256     link_assign_symbol_vaddrs(l, img);
   2257     metrics_scope_end(l->c, "link.assign_vaddrs");
   2258     metrics_scope_begin(l->c, "link.emit_boundaries");
   2259     link_emit_array_boundaries(l, img);
   2260     link_emit_tls_boundaries(l, img);
   2261     link_emit_encoding_section_boundaries(l, img);
   2262     link_emit_boundary_sym(l, img, "__dso_handle", 0);
   2263     /* `_DYNAMIC` marks the dynamic section; in a static image it must be
   2264      * absolute 0 so libc's static-vs-dynamic probe (FreeBSD's __libc_start1
   2265      * gates _init_tls() on `&_DYNAMIC != NULL`) takes the static path. Only
   2266      * define it for dynamic output, where layout_dyn places it at the real
   2267      * .dynamic vaddr; for static, the weak undef from crt/libc already
   2268      * resolved to SK_ABS 0, and defining it here as a rebased SK_OBJ symbol
   2269      * would wrongly make `&_DYNAMIC` non-zero. */
   2270     if (l->emit_pie || l->emit_shared)
   2271       link_emit_boundary_sym(l, img, "_DYNAMIC", 0);
   2272     link_emit_boundary_sym(l, img, "_GLOBAL_OFFSET_TABLE_", 0);
   2273     /* PE/COFF: mingw CRT references `__ImageBase` for ASLR-relative
   2274      * addressing and base-relocation bookkeeping.  The PE emitter
   2275      * writes LINK_PE_IMAGE_BASE into the optional header; expose the
   2276      * same value as a linker-defined symbol so input objects resolve.
   2277      * Driven by the format claiming `__ImageBase` (the same hook that
   2278      * fixes its SymKind) rather than a target.obj switch. */
   2279     {
   2280       int fmt_kind;
   2281       if (obj_format_boundary_sym_kind(l->c, SLICE_LIT("__ImageBase"),
   2282                                        &fmt_kind)) {
   2283         link_emit_boundary_sym(l, img, "__ImageBase", LINK_PE_IMAGE_BASE);
   2284         if (img->tls_memsz) link_emit_boundary_sym(l, img, "_tls_used", 0);
   2285       }
   2286     }
   2287     {
   2288       const LinkArchDesc* arch = link_arch_desc_for(l->c);
   2289       u32 si;
   2290       u64 gp_vaddr = 0;
   2291       if (arch && arch->global_pointer_symbol) {
   2292         for (si = 0; si < img->nsegments; ++si) {
   2293           if (img->segments[si].flags & SF_WRITE) {
   2294             gp_vaddr = img->segments[si].vaddr + arch->global_pointer_rw_offset;
   2295             break;
   2296           }
   2297         }
   2298         link_emit_boundary_sym(l, img, arch->global_pointer_symbol, gp_vaddr);
   2299       }
   2300     }
   2301     metrics_scope_end(l->c, "link.emit_boundaries");
   2302     metrics_scope_begin(l->c, "link.resolve_undefs");
   2303     link_resolve_undefs(l, img);
   2304     metrics_scope_end(l->c, "link.resolve_undefs");
   2305     /* --defsym: define absolute / alias linker symbols now that layout and
   2306      * undef resolution are done (an alias target's vaddr is known). Runs
   2307      * before relocations so a defsym can satisfy a reference, and before
   2308      * entry resolution so a defsym can name the entry point. */
   2309     link_apply_defsyms(l, img);
   2310     metrics_scope_begin(l->c, "link.gc_drop_dead");
   2311     link_gc_drop_dead_globals(l, img, &g);
   2312     metrics_scope_end(l->c, "link.gc_drop_dead");
   2313     metrics_scope_begin(l->c, "link.layout_iplt");
   2314     link_layout_iplt(l, img);
   2315     if (img->niplt) link_emit_array_boundaries(l, img);
   2316     metrics_scope_end(l->c, "link.layout_iplt");
   2317     {
   2318       LinkSymId* got_map = NULL;
   2319       LinkSymId* stub_map = NULL;
   2320       u32 map_size = LinkSyms_count(&img->syms) + 1u;
   2321       metrics_scope_begin(l->c, "link.layout_jit_stubs");
   2322       link_layout_jit_stubs(l, img, map_size, &stub_map);
   2323       metrics_scope_end(l->c, "link.layout_jit_stubs");
   2324       metrics_scope_begin(l->c, "link.layout_got");
   2325       /* Skip the link-time static GOT only for formats that build their own
   2326        * static GOT / non-lazy pointer table (Mach-O) in a static image. */
   2327       if (!obj_format_builds_own_static_got(l->c) || !l->emit_static_exe)
   2328         link_layout_got(l, img, map_size, &got_map);
   2329       metrics_scope_end(l->c, "link.layout_got");
   2330       metrics_scope_begin(l->c, "link.emit_relocations");
   2331       link_emit_relocations(l, img, got_map, stub_map);
   2332       metrics_count(l->c, "link.syms", LinkSyms_count(&img->syms));
   2333       metrics_count(l->c, "link.relocs", LinkRelocs_count(&img->relocs));
   2334       metrics_scope_end(l->c, "link.emit_relocations");
   2335       if (got_map) h->free(h, got_map, sizeof(*got_map) * map_size);
   2336       if (stub_map) h->free(h, stub_map, sizeof(*stub_map) * map_size);
   2337     }
   2338     {
   2339       const ObjFormatImpl* fmt = obj_format_lookup(l->c->target.obj);
   2340       metrics_scope_begin(l->c, "link.layout_dyn");
   2341       if (fmt && fmt->layout_dyn) fmt->layout_dyn(l, img);
   2342       metrics_scope_end(l->c, "link.layout_dyn");
   2343     }
   2344     metrics_scope_begin(l->c, "link.resolve_entry");
   2345     link_resolve_entry(l, img);
   2346     metrics_scope_end(l->c, "link.resolve_entry");
   2347     link_gc_live_free(&g, h);
   2348   }
   2349 
   2350   metrics_scope_begin(l->c, "link.capture_debug");
   2351   link_capture_debug_inputs(l, img);
   2352   metrics_scope_end(l->c, "link.capture_debug");
   2353 
   2354   metrics_scope_end(l->c, "link.resolve.total");
   2355   return img;
   2356 }