kit

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

emit.c (26378B)


      1 /* PE/COFF relocatable .obj writer. Walks a finalized ObjBuilder and
      2  * emits a 64-bit little-endian relocatable object via the supplied
      3  * Writer. Counterpart to emit_elf / emit_macho.
      4  *
      5  * Layout strategy:
      6  *   1. plan COFF sections (one per kept obj section), assigning
      7  *      Characteristics, alignment, raw size, and per-section reloc
      8  *      counts;
      9  *   2. build the symbol table (synthesized per-section static symbols
     10  *      with section-definition aux records, plus file symbols and
     11  *      every ObjSym kept after sweep);
     12  *   3. build per-section relocation records via the per-arch
     13  *      translator (arch_for_compiler(c)->coff->reloc_to);
     14  *   4. assign file offsets:
     15  *        file header | section headers | (bytes + relocs)* | symtab | strtab
     16  *   5. write the file in that order.
     17  *
     18  * 64-bit little-endian only — IMAGE_FILE_MACHINE_AMD64 (x86_64) and
     19  * IMAGE_FILE_MACHINE_ARM64 (aarch64). Big-endian / ptr_size != 8 panic
     20  * at entry.
     21  *
     22  * Section name mapping policy: we pass the kit Section.name through
     23  * verbatim to the COFF Name field. Callers / readers are expected to
     24  * have stored COFF-shaped names (".text", ".rdata", ".tls$", etc.) at
     25  * the obj layer; emit_coff does not rewrite ELF-style spellings like
     26  * ".rodata" -> ".rdata". Names longer than 8 bytes spill into the
     27  * string table with the "/<decimal-offset>" encoding.
     28  *
     29  * Addend handling: COFF stores the addend inline in the patched bytes
     30  * (there is no addend field in IMAGE_RELOCATION). The ObjBuilder
     31  * caller is responsible for having written the addend into the section
     32  * bytes already — matching how MSVC / mingw emit. A nonzero
     33  * Reloc::addend with has_explicit_addend set is rejected here as a
     34  * known v1 limitation. */
     35 
     36 #include <string.h>
     37 
     38 #include "core/arena.h"
     39 #include "core/buf.h"
     40 #include "core/heap.h"
     41 #include "core/pool.h"
     42 #include "core/slice.h"
     43 #include "core/util.h"
     44 #include "obj/coff/coff.h"
     45 #include "obj/format.h"
     46 
     47 static int coff_rel32_absorbs_minus4(KitArchKind arch, RelocKind kind,
     48                                      i64 addend) {
     49   if (arch != KIT_ARCH_X86_64 || addend != -4) return 0;
     50   switch (kind) {
     51     case R_PC32:
     52     case R_REL32:
     53     case R_PLT32:
     54     case R_X64_PLT32:
     55     case R_X64_GOTPCREL:
     56     case R_X64_GOTPCRELX:
     57     case R_X64_REX_GOTPCRELX:
     58       return 1;
     59     default:
     60       return 0;
     61   }
     62 }
     63 
     64 /* ---- per-COFF-section plan record ---- */
     65 
     66 typedef struct CSec {
     67   /* IMAGE_SECTION_HEADER fields (little-endian-encoded at write time). */
     68   char name8[8];        /* Name field bytes; "/N" form if long name */
     69   u32 virtual_size;     /* 0 for .obj (VirtualSize is image-only) */
     70   u32 size_of_raw_data; /* section size, incl. NOBITS (bss) size */
     71   u32 pointer_to_raw_data;
     72   u32 pointer_to_relocations;
     73   u16 number_of_relocations;
     74   u32 characteristics; /* IMAGE_SCN_* | ALIGN nibble */
     75 
     76   /* Planning state. */
     77   u32 align;   /* in bytes, power of two */
     78   u32 obj_sec; /* originating ObjSecId */
     79   int is_nobits;
     80   const Buf* obj_bytes; /* NULL when nobits */
     81   u8* reloc_bytes;      /* arena-allocated, nreloc * 10 bytes */
     82   ObjGroupId group_id;  /* OBJ_GROUP_NONE if not in a group */
     83 } CSec;
     84 
     85 /* ---- emit ---- */
     86 
     87 static u32 log2_align(u32 a) {
     88   u32 r = 0;
     89   while ((1u << r) < a) ++r;
     90   return r;
     91 }
     92 
     93 /* Map kit section flags/sem to IMAGE_SCN_* Characteristics, leaving
     94  * the alignment nibble for the caller to OR in. */
     95 static u32 sec_characteristics(const Section* s, int in_group) {
     96   u32 r = 0;
     97   int is_bss = (s->kind == SEC_BSS) || (s->sem == SSEM_NOBITS);
     98   if (s->flags & SF_EXEC) {
     99     r |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
    100   } else if (is_bss) {
    101     r |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
    102   } else if (s->flags & SF_WRITE) {
    103     r |= IMAGE_SCN_CNT_INITIALIZED_DATA;
    104   } else if (s->flags & SF_ALLOC) {
    105     /* Read-only allocated data (.rdata). */
    106     r |= IMAGE_SCN_CNT_INITIALIZED_DATA;
    107   }
    108   if (s->flags & SF_ALLOC) r |= IMAGE_SCN_MEM_READ;
    109   if (s->flags & SF_WRITE) r |= IMAGE_SCN_MEM_WRITE;
    110   if (in_group) r |= IMAGE_SCN_LNK_COMDAT;
    111   /* When a reader stashed format-specific flag bits on a COFF-origin
    112    * section, OR them back in here. ext_type carries the raw
    113    * Characteristics value (or zero if no override); ext_flags is a
    114    * sibling bag for any bits the canonical mapping above would lose. */
    115   if (s->ext_kind == OBJ_EXT_COFF) {
    116     if (s->ext_type) {
    117       /* Preserve the raw characteristics verbatim — overrides the
    118        * canonical mapping. Keeps round-trip byte-stable for sections
    119        * carrying CNT_INFO / LNK_REMOVE / MEM_DISCARDABLE / etc. */
    120       r = s->ext_type & ~IMAGE_SCN_ALIGN_MASK;
    121     }
    122     r |= s->ext_flags;
    123   }
    124   return r;
    125 }
    126 
    127 /* The string-table dedup + build moved to the shared ObjStrtab (obj.h); the
    128  * former per-add buf_flatten + linear substring scan here was the same O(n^2)
    129  * the ELF writer had. The COFF strtab keeps its leading 4-byte size field (a
    130  * raw prefix the dedup never targets, see obj_strtab_put_raw). */
    131 
    132 /* Encode an 8-byte Name field. If the name fits in 8 bytes, copy
    133  * verbatim and zero-pad. Otherwise allocate the name in `strtab` and
    134  * write "/<decimal-offset>" (NUL-padded to 8 bytes). */
    135 static void encode_name8(char out[8], const char* name, u32 nlen,
    136                          ObjStrtab* strtab) {
    137   memset(out, 0, 8);
    138   if (nlen <= 8) {
    139     if (nlen) memcpy(out, name, nlen);
    140     return;
    141   }
    142   u32 off = obj_strtab_add(strtab, name, nlen);
    143   /* "/<decimal-offset>" — up to 7 decimal digits leaves room for the
    144    * leading slash within 8 bytes. COFF .obj strtabs are < 1 MiB in
    145    * practice, so 7 digits is plenty. */
    146   char tmp[16];
    147   int n = 0;
    148   tmp[n++] = '/';
    149   /* Decimal-format off into tmp+1. */
    150   char dig[12];
    151   int d = 0;
    152   u32 v = off;
    153   if (v == 0) {
    154     dig[d++] = '0';
    155   } else {
    156     while (v) {
    157       dig[d++] = (char)('0' + (v % 10u));
    158       v /= 10u;
    159     }
    160   }
    161   while (d > 0 && n < (int)sizeof tmp) tmp[n++] = dig[--d];
    162   if (n > 8) n = 8;
    163   memcpy(out, tmp, (size_t)n);
    164 }
    165 
    166 /* Write one 18-byte IMAGE_SYMBOL record into `dst`. */
    167 static void wr_sym(u8* dst, const char ShortName[8], u32 Zeroes, u32 Offset,
    168                    u32 Value, i16 SectionNumber, u16 Type, u8 StorageClass,
    169                    u8 NumberOfAuxSymbols) {
    170   if (Zeroes == 0 && Offset != 0) {
    171     /* LongName form: 4 zero bytes then 4-byte LE strtab offset. */
    172     memset(dst, 0, 4);
    173     wr_u32_le(dst + 4, Offset);
    174   } else {
    175     memcpy(dst, ShortName, 8);
    176   }
    177   wr_u32_le(dst + 8, Value);
    178   wr_u16_le(dst + 12, (u16)SectionNumber);
    179   wr_u16_le(dst + 14, Type);
    180   dst[16] = StorageClass;
    181   dst[17] = NumberOfAuxSymbols;
    182 }
    183 
    184 /* Write a section-definition aux record (18 bytes). */
    185 static void wr_aux_secdef(u8* dst, u32 Length, u16 NumberOfRelocations,
    186                           u16 NumberOfLinenumbers, u32 CheckSum, u16 Number,
    187                           u8 Selection) {
    188   wr_u32_le(dst + 0, Length);
    189   wr_u16_le(dst + 4, NumberOfRelocations);
    190   wr_u16_le(dst + 6, NumberOfLinenumbers);
    191   wr_u32_le(dst + 8, CheckSum);
    192   wr_u16_le(dst + 12, Number);
    193   dst[14] = Selection;
    194   dst[15] = 0;
    195   dst[16] = 0;
    196   dst[17] = 0;
    197 }
    198 
    199 /* Write a weak-externals aux record (18 bytes). */
    200 static void wr_aux_weak(u8* dst, u32 TagIndex, u32 Characteristics) {
    201   wr_u32_le(dst + 0, TagIndex);
    202   wr_u32_le(dst + 4, Characteristics);
    203   memset(dst + 8, 0, 10);
    204 }
    205 
    206 /* Look up the pool-interned string for a Sym. */
    207 static const char* sym_to_str(Compiler* c, Sym n, u32* len_out) {
    208   Slice sl = pool_slice(c->global, n);
    209   const char* s = sl.s;
    210   if (!s) {
    211     *len_out = 0;
    212     return "";
    213   }
    214   *len_out = (u32)sl.len;
    215   return s;
    216 }
    217 
    218 void emit_coff(Compiler* c, ObjBuilder* ob, Writer* w) {
    219   Heap* h = (Heap*)c->ctx->heap;
    220 
    221   /* Tombstone sweep — see obj_sweep_dead. */
    222   obj_sweep_dead(ob);
    223 
    224   /* ---- target validation ----------------------------------------- */
    225   const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_COFF);
    226   const ObjCoffArchOps* coff =
    227       fmt && fmt->coff_arch ? fmt->coff_arch(c->target.arch) : NULL;
    228   if (!coff || !coff->reloc_to) {
    229     compiler_panic(c, SRCLOC_NONE, "emit_coff: unsupported target arch %u",
    230                    (u32)c->target.arch);
    231   }
    232   u16 machine = coff->machine;
    233   u32 (*reloc_to)(u32) = coff->reloc_to;
    234   if (c->target.big_endian) {
    235     compiler_panic(c, SRCLOC_NONE, "emit_coff: big-endian COFF not supported");
    236   }
    237   if (c->target.ptr_size != 8) {
    238     compiler_panic(c, SRCLOC_NONE, "emit_coff: ptr_size %u (expected 8)",
    239                    (u32)c->target.ptr_size);
    240   }
    241 
    242   /* ---- pass 1: plan sections ------------------------------------- */
    243   u32 nobjsec = obj_section_count(ob);
    244   CSec* secs = arena_zarray(c->scratch, CSec, nobjsec ? nobjsec : 1);
    245   u32* obj_to_coff = arena_zarray(c->scratch, u32, nobjsec ? nobjsec : 1);
    246   u32 nsecs = 0;
    247 
    248   /* String table — leading 4-byte size placeholder. Real strings start
    249    * at offset 4. */
    250   ObjStrtab strtab;
    251   obj_strtab_init(&strtab, h, /*dedup=*/1);
    252   {
    253     u8 zero4[COFF_STRTAB_SIZE_FIELD_BYTES] = {0, 0, 0, 0};
    254     obj_strtab_put_raw(&strtab, zero4, COFF_STRTAB_SIZE_FIELD_BYTES);
    255   }
    256 
    257   for (u32 i = 1; i < nobjsec; ++i) {
    258     const Section* s = obj_section_get(ob, i);
    259     if (s->removed) continue;
    260     /* Skip ELF-style synthetic sections (a reader from another format
    261      * may have surfaced them) — COFF stores symtab/strtab/relocs
    262      * out-of-band, not as named sections. */
    263     if (s->sem == SSEM_SYMTAB || s->sem == SSEM_STRTAB || s->sem == SSEM_RELA ||
    264         s->sem == SSEM_REL || s->sem == SSEM_GROUP) {
    265       continue;
    266     }
    267 
    268     CSec* cs = &secs[nsecs];
    269     u32 nlen;
    270     const char* nm = sym_to_str(c, s->name, &nlen);
    271     encode_name8(cs->name8, nm, nlen, &strtab);
    272 
    273     cs->obj_sec = i;
    274     cs->group_id = s->group_id;
    275     cs->align = s->align ? s->align : 1;
    276 
    277     int in_group = (s->group_id != OBJ_GROUP_NONE);
    278     u32 ch = sec_characteristics(s, in_group);
    279     /* Alignment lives in bits 20..23. Cap at log2(8192)=13 -> nibble
    280      * value 14 (IMAGE_SCN_ALIGN_8192BYTES). */
    281     u32 lg = log2_align(cs->align);
    282     if (lg > 13) lg = 13;
    283     ch &= ~IMAGE_SCN_ALIGN_MASK;
    284     ch |= IMAGE_SCN_ALIGN_FROM_LOG2(lg);
    285     cs->characteristics = ch;
    286 
    287     if (s->sem == SSEM_NOBITS || s->kind == SEC_BSS) {
    288       cs->is_nobits = 1;
    289       /* COFF object files carry the section size in SizeOfRawData (with
    290        * PointerToRawData=0 / no file bytes for uninitialized data) and
    291        * keep VirtualSize=0 — VirtualSize is meaningful only for linked
    292        * images. lld / link.exe treat a section with SizeOfRawData==0 as
    293        * empty and discard it, orphaning every symbol defined there and
    294        * breaking relocations that target them ("relocation against
    295        * symbol in discarded section"). Match clang/MSVC: size in
    296        * SizeOfRawData, VirtualSize 0, no raw data. */
    297       cs->virtual_size = 0;
    298       cs->size_of_raw_data = s->bss_size;
    299       cs->obj_bytes = NULL;
    300     } else {
    301       cs->is_nobits = 0;
    302       cs->virtual_size = 0;
    303       cs->size_of_raw_data = s->bytes.total;
    304       cs->obj_bytes = &s->bytes;
    305     }
    306 
    307     obj_to_coff[i] = nsecs + 1; /* 1-based; matches SectionNumber. */
    308     nsecs++;
    309   }
    310 
    311   /* ---- pass 2: count and assign per-section reloc counts --------- */
    312   /* COFF stores NumberOfRelocations as u16; sections with > 65535
    313    * relocs use the IMAGE_SCN_LNK_NRELOC_OVFL extension which we don't
    314    * implement in v1. Panic if any single section exceeds the limit. */
    315   for (u32 ci = 0; ci < nsecs; ++ci) {
    316     CSec* cs = &secs[ci];
    317     u32 nr = obj_reloc_count(ob, cs->obj_sec);
    318     if (nr > 0xFFFFu) {
    319       compiler_panic(c, SRCLOC_NONE,
    320                      "emit_coff: section %u has %u relocs (max 65535)",
    321                      (u32)cs->obj_sec, nr);
    322     }
    323     cs->number_of_relocations = (u16)nr;
    324   }
    325 
    326   /* ---- pass 3: build the symbol table ---------------------------- */
    327   /* Count ObjSyms (incl. tombstoned — we'll skip those when emitting). */
    328   u32 nobjsym = 0;
    329   {
    330     ObjSymIter* it = obj_symiter_new(ob);
    331     ObjSymEntry e;
    332     while (obj_symiter_next(it, &e)) ++nobjsym;
    333     obj_symiter_free(it);
    334   }
    335 
    336   /* Upper bound on symbol-table records (including aux slots):
    337    *   - 2 records per section symbol (primary + 1 aux secdef)
    338    *   - 2 records per ObjSym (primary + up to 1 weak aux)
    339    *   - +2 spare for safety
    340    * Worst case is generous; we trim by tracking nrecords as we emit. */
    341   u32 max_records = 2u * nsecs + 2u * nobjsym + 4u;
    342   u8* symtab =
    343       (u8*)arena_zarray(c->scratch, u8, (size_t)COFF_SYMBOL_SIZE * max_records);
    344   u32 nrecords = 0;
    345 
    346   /* obj_id -> COFF symbol index (including aux slots). Index 0 is
    347    * reserved as "none" in our internal map (a real COFF symbol may
    348    * legitimately live at index 0, but no ObjSym ever maps there since
    349    * we never put OBJ_SYM_NONE through). */
    350   u32* sym_to_coff = arena_zarray(c->scratch, u32, nobjsym + 2);
    351 
    352   /* Section symbols first — one STATIC per kept obj section, each
    353    * followed by a SECTION DEFINITION aux. Reloc-against-section in
    354    * other tools' output uses these; emitting them unconditionally
    355    * matches what clang / mingw emit and gives readers a stable target. */
    356   u32* secsym_index = arena_zarray(c->scratch, u32, nsecs + 1);
    357   for (u32 ci = 0; ci < nsecs; ++ci) {
    358     CSec* cs = &secs[ci];
    359     char short_name[8];
    360     /* The section symbol's name is the section's own name (truncated
    361      * to 8 bytes — section symbols never use the strtab spill form in
    362      * MSVC/clang output). */
    363     memcpy(short_name, cs->name8, 8);
    364 
    365     u8* slot = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    366     wr_sym(slot, short_name, /*Zeroes*/ 1, /*Offset*/ 0,
    367            /*Value*/ 0,
    368            /*SectionNumber*/ (i16)(ci + 1),
    369            /*Type*/ IMAGE_SYM_TYPE_NULL,
    370            /*StorageClass*/ IMAGE_SYM_CLASS_STATIC,
    371            /*NumberOfAuxSymbols*/ 1);
    372     secsym_index[ci] = nrecords;
    373     nrecords++;
    374 
    375     /* Section-definition aux. For COMDAT members we encode the
    376      * Selection from the group; default to SELECT_ANY which is what
    377      * gcc/clang emit unless the user requests a specific selection
    378      * mode. The associated-section Number is left at 0 (kit does
    379      * not produce associative-COMDAT chains today). */
    380     u8 selection = 0;
    381     if (cs->group_id != OBJ_GROUP_NONE) {
    382       const ObjGroup* g = obj_group_get(ob, cs->group_id);
    383       if (g && !g->removed) {
    384         selection = g->flags ? (u8)IMAGE_COMDAT_SELECT_ANY
    385                              : (u8)IMAGE_COMDAT_SELECT_ANY;
    386       }
    387     }
    388     u8* aux = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    389     wr_aux_secdef(aux, /*Length*/ cs->size_of_raw_data,
    390                   /*NumberOfRelocations*/ cs->number_of_relocations,
    391                   /*NumberOfLinenumbers*/ 0,
    392                   /*CheckSum*/ 0,
    393                   /*Number*/ 0,
    394                   /*Selection*/ selection);
    395     nrecords++;
    396   }
    397 
    398   /* File / regular symbols. */
    399   {
    400     ObjSymIter* it = obj_symiter_new(ob);
    401     ObjSymEntry e;
    402     while (obj_symiter_next(it, &e)) {
    403       const ObjSym* s = e.sym;
    404       if (s->removed) continue;
    405       if (s->kind == SK_IFUNC) {
    406         compiler_panic(c, SRCLOC_NONE,
    407                        "emit_coff: SK_IFUNC has no PE/COFF representation");
    408       }
    409       /* Don't re-emit SK_SECTION symbols — section symbols are
    410        * synthesized above. Map any input-side SK_SECTION onto the
    411        * already-emitted one. */
    412       if (s->kind == SK_SECTION) {
    413         if (s->section_id && s->section_id < nobjsec) {
    414           u32 ci = obj_to_coff[s->section_id];
    415           if (ci) sym_to_coff[e.id] = secsym_index[ci - 1];
    416         }
    417         continue;
    418       }
    419 
    420       u32 nlen;
    421       const char* nm = sym_to_str(c, s->name, &nlen);
    422 
    423       if (s->kind == SK_FILE) {
    424         /* File symbol: name ".file" (short), section IMAGE_SYM_DEBUG,
    425          * storage class FILE, followed by aux records carrying the
    426          * NUL-padded file path (18 bytes per aux). */
    427         u32 file_len = nlen;
    428         u32 naux =
    429             file_len ? (file_len + COFF_AUX_FILE_SIZE - 1u) / COFF_AUX_FILE_SIZE
    430                      : 1u;
    431         char short_name[8] = {'.', 'f', 'i', 'l', 'e', 0, 0, 0};
    432         u8* slot = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    433         wr_sym(slot, short_name, 1, 0, /*Value*/ 0,
    434                /*SectionNumber*/ (i16)IMAGE_SYM_DEBUG,
    435                /*Type*/ IMAGE_SYM_TYPE_NULL,
    436                /*StorageClass*/ IMAGE_SYM_CLASS_FILE,
    437                /*NumberOfAuxSymbols*/ (u8)naux);
    438         sym_to_coff[e.id] = nrecords;
    439         nrecords++;
    440         for (u32 a = 0; a < naux; ++a) {
    441           u8* aux = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    442           memset(aux, 0, COFF_AUX_FILE_SIZE);
    443           u32 off = a * COFF_AUX_FILE_SIZE;
    444           u32 copy = file_len > off ? file_len - off : 0;
    445           if (copy > COFF_AUX_FILE_SIZE) copy = COFF_AUX_FILE_SIZE;
    446           if (copy) memcpy(aux, nm + off, copy);
    447           nrecords++;
    448         }
    449         continue;
    450       }
    451 
    452       /* Regular symbol. */
    453       char short_name[8];
    454       u32 zeroes = 1, offset = 0;
    455       memset(short_name, 0, 8);
    456       if (nlen <= 8) {
    457         if (nlen) memcpy(short_name, nm, nlen);
    458       } else {
    459         zeroes = 0;
    460         offset = obj_strtab_add(&strtab, nm, nlen);
    461       }
    462 
    463       i16 section_number = 0;
    464       u32 value = 0;
    465       u8 storage_class = IMAGE_SYM_CLASS_NULL;
    466       u16 type = IMAGE_SYM_TYPE_NULL;
    467       u8 naux = 0;
    468       int emit_weak_aux = 0;
    469 
    470       switch (s->kind) {
    471         case SK_ABS:
    472           section_number = (i16)IMAGE_SYM_ABSOLUTE;
    473           value = (u32)s->value;
    474           break;
    475         case SK_COMMON:
    476           /* COFF lacks a per-common alignment field; encode size in
    477            * Value with SectionNumber=UNDEFINED and rely on the linker
    478            * to pick a default alignment. (kit's frontend uses
    479            * COMMON only via __attribute__((common)) which is rare on
    480            * PE/COFF targets.) */
    481           section_number = (i16)IMAGE_SYM_UNDEFINED;
    482           value = (u32)s->size;
    483           break;
    484         default:
    485           if (s->section_id == OBJ_SEC_NONE) {
    486             section_number = (i16)IMAGE_SYM_UNDEFINED;
    487             value = 0;
    488           } else if (s->section_id < nobjsec && obj_to_coff[s->section_id]) {
    489             section_number = (i16)obj_to_coff[s->section_id];
    490             value = (u32)s->value;
    491           } else {
    492             section_number = (i16)IMAGE_SYM_UNDEFINED;
    493             value = 0;
    494           }
    495           break;
    496       }
    497 
    498       if (s->kind == SK_FUNC) type = (u16)COFF_SYM_TYPE_FUNCTION;
    499 
    500       switch (s->bind) {
    501         case SB_LOCAL:
    502           storage_class = IMAGE_SYM_CLASS_STATIC;
    503           break;
    504         case SB_GLOBAL:
    505           storage_class = IMAGE_SYM_CLASS_EXTERNAL;
    506           break;
    507         case SB_WEAK:
    508           /* mingw / clang spell weak as EXTERNAL with a WeakExternal
    509            * aux that points at the fallback symbol. kit's obj layer
    510            * doesn't carry a separate fallback symbol today, so we emit
    511            * a self-referential weak aux (TagIndex=0) which the linker
    512            * treats as "weak, no fallback" — equivalent to ELF STB_WEAK. */
    513           storage_class = IMAGE_SYM_CLASS_WEAK_EXTERNAL;
    514           emit_weak_aux = 1;
    515           naux = 1;
    516           break;
    517         default:
    518           storage_class = IMAGE_SYM_CLASS_STATIC;
    519           break;
    520       }
    521 
    522       u8* slot = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    523       wr_sym(slot, short_name, zeroes, offset, value, section_number, type,
    524              storage_class, naux);
    525       sym_to_coff[e.id] = nrecords;
    526       nrecords++;
    527       if (emit_weak_aux) {
    528         u8* aux = symtab + (size_t)nrecords * COFF_SYMBOL_SIZE;
    529         wr_aux_weak(aux, /*TagIndex*/ 0,
    530                     /*Characteristics*/ IMAGE_WEAK_EXTERN_SEARCH_LIBRARY);
    531         nrecords++;
    532       }
    533     }
    534     obj_symiter_free(it);
    535   }
    536 
    537   /* ---- pass 4: build per-section relocation tables --------------- */
    538   for (u32 ci = 0; ci < nsecs; ++ci) {
    539     CSec* cs = &secs[ci];
    540     u32 nr;
    541     const u32* rix = obj_reloc_section(ob, cs->obj_sec, &nr);
    542     if (!nr) continue;
    543     u8* buf = (u8*)arena_alloc(c->scratch, (size_t)COFF_RELOC_SIZE * nr,
    544                                _Alignof(u32));
    545     u32 j = 0;
    546     /* rix[] lists this section's live relocs in ascending global order. */
    547     for (u32 k = 0; k < nr; ++k) {
    548       const Reloc* r = obj_reloc_at(ob, rix[k]);
    549       if (r->sym == OBJ_SYM_NONE) {
    550         compiler_panic(c, SRCLOC_NONE,
    551                        "emit_coff: reloc without symbol not supported "
    552                        "(sec=%u offset=%u kind=%u)",
    553                        (u32)r->section_id, (u32)r->offset, (u32)r->kind);
    554       }
    555       if (r->has_explicit_addend && r->addend != 0 &&
    556           !coff_rel32_absorbs_minus4(c->target.arch, (RelocKind)r->kind,
    557                                      r->addend)) {
    558         /* v1 limitation: COFF carries the addend in the patched bytes,
    559          * and we don't currently mutate the obj's section bytes to
    560          * encode a separate explicit addend. kit's MCEmitter writes
    561          * the addend inline for COFF targets, so this branch only
    562          * fires for inputs synthesized by external tools. */
    563         compiler_panic(c, SRCLOC_NONE,
    564                        "emit_coff: explicit nonzero addend not supported "
    565                        "(sec=%u offset=%u kind=%u addend=%lld)",
    566                        (u32)r->section_id, (u32)r->offset, (u32)r->kind,
    567                        (long long)r->addend);
    568       }
    569       u32 wire = reloc_to(r->kind);
    570       /* Both arch translators use 0 (IMAGE_REL_*_ABSOLUTE) as the
    571        * unsupported-input sentinel; treat that as a panic unless the
    572        * input really is R_NONE. */
    573       if (wire == 0 && r->kind != R_NONE) {
    574         compiler_panic(c, SRCLOC_NONE,
    575                        "emit_coff: unsupported relocation kind %u for arch %u",
    576                        (u32)r->kind, (u32)c->target.arch);
    577       }
    578       u32 sym_idx = sym_to_coff[r->sym];
    579       u8* slot = buf + (size_t)j * COFF_RELOC_SIZE;
    580       wr_u32_le(slot + 0, r->offset);
    581       wr_u32_le(slot + 4, sym_idx);
    582       wr_u16_le(slot + 8, (u16)wire);
    583       ++j;
    584     }
    585     cs->reloc_bytes = buf;
    586     /* If a tombstoned reloc was skipped between count and emit, j may
    587      * be less than nr; trust the latter count for the wire field. */
    588     if (j != nr) cs->number_of_relocations = (u16)j;
    589   }
    590 
    591   /* ---- pass 5: assign file offsets ------------------------------- */
    592   /* Layout:
    593    *   [file header] [section headers] [per-section: bytes, relocs]*
    594    *   [symbol table] [string table] */
    595   u64 cur =
    596       (u64)COFF_FILE_HEADER_SIZE + (u64)COFF_SECTION_HEADER_SIZE * (u64)nsecs;
    597 
    598   for (u32 ci = 0; ci < nsecs; ++ci) {
    599     CSec* cs = &secs[ci];
    600     /* Raw data offset. NOBITS contributes nothing on disk. */
    601     if (cs->is_nobits || cs->size_of_raw_data == 0) {
    602       cs->pointer_to_raw_data = 0;
    603     } else {
    604       cur = ALIGN_UP(cur, (u64)cs->align);
    605       cs->pointer_to_raw_data = (u32)cur;
    606       cur += cs->size_of_raw_data;
    607     }
    608     /* Reloc table. COFF doesn't mandate alignment for the reloc array,
    609      * but llvm and binutils emit them naturally byte-packed; we 4-align
    610      * for tidiness. */
    611     if (cs->number_of_relocations) {
    612       cur = ALIGN_UP(cur, (u64)4);
    613       cs->pointer_to_relocations = (u32)cur;
    614       cur += (u64)cs->number_of_relocations * COFF_RELOC_SIZE;
    615     } else {
    616       cs->pointer_to_relocations = 0;
    617     }
    618   }
    619 
    620   cur = ALIGN_UP(cur, (u64)4);
    621   u64 symtab_off = cur;
    622   cur += (u64)nrecords * COFF_SYMBOL_SIZE;
    623 
    624   /* String table starts immediately after the symtab. Patch the 4-byte
    625    * size prefix (inclusive). */
    626   u32 strtab_size = obj_strtab_size(&strtab);
    627   /* The size field is part of the on-disk strtab and is the total
    628    * inclusive byte count. Patch it now. */
    629   {
    630     u8 sz_le[4];
    631     wr_u32_le(sz_le, strtab_size);
    632     /* Buf doesn't expose in-place patch; flatten, patch, re-emit when
    633      * we write. Just remember the value. */
    634     (void)sz_le;
    635   }
    636   u64 strtab_off = cur;
    637   cur += strtab_size;
    638 
    639   /* ---- pass 6: write the file ------------------------------------ */
    640   kit_writer_seek(w, 0);
    641 
    642   /* IMAGE_FILE_HEADER */
    643   coff_wr_u16(w, machine);
    644   coff_wr_u16(w, (u16)nsecs);
    645   coff_wr_u32(w, 0); /* TimeDateStamp: reproducible */
    646   coff_wr_u32(w, (u32)symtab_off);
    647   coff_wr_u32(w, nrecords);
    648   coff_wr_u16(w, 0); /* SizeOfOptionalHeader: 0 for .obj */
    649   coff_wr_u16(w, IMAGE_FILE_LARGE_ADDRESS_AWARE);
    650 
    651   /* Section headers — one 40-byte block immediately after the file
    652    * header. */
    653   for (u32 ci = 0; ci < nsecs; ++ci) {
    654     const CSec* cs = &secs[ci];
    655     kit_writer_write(w, cs->name8, 8);
    656     coff_wr_u32(w, cs->virtual_size);
    657     coff_wr_u32(w, 0); /* VirtualAddress: 0 for .obj */
    658     coff_wr_u32(w, cs->size_of_raw_data);
    659     coff_wr_u32(w, cs->pointer_to_raw_data);
    660     coff_wr_u32(w, cs->pointer_to_relocations);
    661     coff_wr_u32(w, 0); /* PointerToLinenumbers: 0 */
    662     coff_wr_u16(w, cs->number_of_relocations);
    663     coff_wr_u16(w, 0); /* NumberOfLinenumbers: 0 */
    664     coff_wr_u32(w, cs->characteristics);
    665   }
    666 
    667   /* Section bytes + relocs (interleaved). */
    668   for (u32 ci = 0; ci < nsecs; ++ci) {
    669     const CSec* cs = &secs[ci];
    670     if (!cs->is_nobits && cs->size_of_raw_data && cs->obj_bytes) {
    671       kit_writer_seek(w, cs->pointer_to_raw_data);
    672       u32 sz = cs->obj_bytes->total;
    673       u8* tmp = (u8*)h->alloc(h, sz ? sz : 1, 1);
    674       if (sz) buf_flatten(cs->obj_bytes, tmp);
    675       kit_writer_write(w, tmp, sz);
    676       h->free(h, tmp, sz ? sz : 1);
    677     }
    678     if (cs->number_of_relocations && cs->reloc_bytes) {
    679       kit_writer_seek(w, cs->pointer_to_relocations);
    680       kit_writer_write(w, cs->reloc_bytes,
    681                        (size_t)cs->number_of_relocations * COFF_RELOC_SIZE);
    682     }
    683   }
    684 
    685   /* Symbol table. */
    686   kit_writer_seek(w, symtab_off);
    687   kit_writer_write(w, symtab, (size_t)nrecords * COFF_SYMBOL_SIZE);
    688 
    689   /* String table: 4-byte total size (inclusive) followed by the body.
    690    * `strtab` was initialized with 4 placeholder zero bytes; rewrite
    691    * them with the real size before flushing. */
    692   {
    693     u8* flat = (u8*)arena_alloc(c->scratch, strtab_size ? strtab_size : 1, 1);
    694     if (strtab_size) memcpy(flat, obj_strtab_data(&strtab), strtab_size);
    695     /* Patch the 4-byte size prefix in place. */
    696     if (strtab_size >= COFF_STRTAB_SIZE_FIELD_BYTES) {
    697       wr_u32_le(flat, strtab_size);
    698     }
    699     kit_writer_seek(w, strtab_off);
    700     kit_writer_write(w, flat, strtab_size);
    701   }
    702   obj_strtab_fini(&strtab);
    703 }