kit

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

mc.c (32281B)


      1 /* Generic MCEmitter implementation.
      2  *
      3  * MCEmitter sits between CGTarget (or asm_parse) and ObjBuilder. It owns
      4  * the current section, byte position, machine label table, and forwards
      5  * relocations / source-location stamps. Encoding is the caller's job —
      6  * MCEmitter writes whatever bytes it's handed.
      7  *
      8  * One MCEmitter serves every supported arch. Label fixup encoding delegates
      9  * through ArchImpl so MCEmitter owns label bookkeeping only.
     10  *
     11  * MCLabel handling: ids are 1-based (0 = MC_LABEL_NONE). Each label
     12  * carries either a placement (sec_id, offset) or a list of pending
     13  * fixups for forward references. emit_label_ref records a fixup; on
     14  * label_place, all pending fixups for that label are applied. Fixup
     15  * application uses RelocKind to choose how to encode the resolved
     16  * displacement into the already-emitted bytes.
     17  *
     18  * For v1 we support these label-ref reloc kinds for intra-section
     19  * fixups:
     20  *   R_PC32          — write 32-bit signed displacement at fixup ofs
     21  *   R_AARCH64_CALL26 — 26-bit imm26 << 2 in the BL instruction
     22  *   R_AARCH64_JUMP26 — same encoding as CALL26 (B vs BL only differs
     23  *                       in the parent opcode the caller already emitted)
     24  *
     25  * Anything else for a label_ref panics; cross-section references go
     26  * through emit_reloc against an ObjSymId instead. */
     27 
     28 #include <string.h>
     29 
     30 #include "arch/arch.h"
     31 #include "core/arena.h"
     32 #include "core/buf.h"
     33 #include "core/bytes.h"
     34 #include "core/heap.h"
     35 #include "core/pool.h"
     36 #include "core/strbuf.h"
     37 #include "debug/dwarf_defs.h"
     38 #include "obj/obj.h"
     39 
     40 typedef struct MCFixup {
     41   u32 sec_id;
     42   u32 offset;
     43   u32 width; /* bytes the encoding occupies */
     44   RelocKind kind;
     45   i64 addend;
     46   struct MCFixup* next;
     47 } MCFixup;
     48 
     49 typedef struct MCDataLabelRef {
     50   /* Where in the data section to write the relocation. */
     51   u32 data_sec;
     52   u32 data_offset;
     53   RelocKind kind;
     54   u32 width;
     55   i64 extra_addend;
     56   struct MCDataLabelRef* next;
     57   /* func_sym + func_start are read from MCEmitter at label_place time
     58    * (when the label's offset becomes known). Under -O1 the queue-time
     59    * call comes during opt IR recording — before any backend func_begin
     60    * has set cur_func_* — so capturing them here would be wrong. The
     61    * label is always placed inside its owning function's emit, so the
     62    * MCEmitter's current function tracks the right symbol at that
     63    * moment. */
     64 } MCDataLabelRef;
     65 
     66 typedef struct MCLabelInfo {
     67   u8 placed;
     68   u8 pad[3];
     69   u32 sec_id;
     70   u32 offset;
     71   MCFixup* pending;
     72   MCDataLabelRef* pending_data;
     73   /* Lazily-minted SB_LOCAL symbol for this label, for code-location
     74    * references that must survive a re-encoding assembler: switch jump-table
     75    * entries (.quad <sym>) and `&&label` address-takes (a PC-relative reloc
     76    * against <sym>). OBJ_SYM_NONE until first requested via mc_label_symbol;
     77    * defined at the label's offset in mc_label_place (forward-ref safe). */
     78   ObjSymId block_sym;
     79 } MCLabelInfo;
     80 
     81 /* ---- CFI buffering (.eh_frame producer) ----
     82  *
     83  * Each cfi_startproc opens a new FDE record; the per-arch backend then
     84  * calls cfi_def_cfa / cfi_offset as the prologue is laid
     85  * down. Each directive snapshots either the current section offset or the
     86  * override set by cfi_set_next_pc_offset — which is STICKY until cfi_endproc
     87  * (used by backends that emit the whole CFI batch in func_end, after the
     88  * epilogue, but want every rule pinned to the post-prologue PC). The
     89  * .eh_frame section is synthesised at mc_emit_eh_frame() time. */
     90 typedef enum CfiOpKind {
     91   CFI_OP_DEF_CFA,
     92   CFI_OP_OFFSET,
     93 } CfiOpKind;
     94 
     95 typedef struct CfiDirective {
     96   u32 pc_offset; /* offset within the function from func_start */
     97   u8 kind;       /* CfiOpKind */
     98   u8 pad[3];
     99   u32 reg;
    100   i32 imm;
    101 } CfiDirective;
    102 
    103 typedef struct CfiFde {
    104   ObjSymId func_sym;
    105   u32 func_section;
    106   u32 func_start;
    107   u32 func_end;
    108   u32 dir_start; /* index of this FDE's first directive in MCImpl.dirs */
    109   u32 ndir;
    110 } CfiFde;
    111 
    112 typedef struct MCImpl {
    113   MCEmitter base;
    114   /* Per-function scratch: labels, forward-reference fixups, and data-label
    115    * refs. All are resolved within the function that produced them (immediately
    116    * for a backward ref, at mc_label_place for a forward one); cross-function
    117    * references go through per-label SB_LOCAL object symbols, and the emitted
    118    * bytes/relocations live in the ObjBuilder, not here. So this arena is reset
    119    * at every mc_begin_function — it grows only to the largest single function's
    120    * scratch instead of accumulating the whole TU's in c->tu. The whole-object
    121    * CFI vectors (fdes/dirs) are heap-realloc'd separately, not from here. */
    122   Arena func_arena;
    123   /* `loc` lives on MCEmitter base now (so per-arch emit hooks can read it
    124    * to feed debug_emit_row). Use base.loc through impl_of(...)->base.loc
    125    * or directly mc->base.loc. */
    126   MCLabelInfo* labels; /* index 0 unused (MC_LABEL_NONE) */
    127   u32 nlabels;
    128   u32 cap;
    129   CfiFde* fdes;
    130   u32 nfdes;
    131   u32 fdes_cap;
    132   i32 cur_fde;
    133   /* All FDEs' CFI directives share one growable vector; each FDE owns the
    134    * contiguous range [dir_start, dir_start+ndir). Single-pass emission keeps a
    135    * function's directives contiguous, so a function never costs its own heap
    136    * block -- the vector doubles a handful of times for the whole object. */
    137   CfiDirective* dirs;
    138   u32 ndirs;
    139   u32 dirs_cap;
    140   u8 eh_frame_emitted;
    141   u8 has_pc_override;
    142   u8 pad_cfi[2];
    143   u32 pc_override;
    144 } MCImpl;
    145 
    146 /* ---- helpers ---- */
    147 
    148 static MCImpl* impl_of(MCEmitter* m) { return (MCImpl*)m; }
    149 
    150 static void labels_grow(MCImpl* mc, u32 want) {
    151   if (want <= mc->cap) return;
    152   u32 ncap = mc->cap ? mc->cap * 2 : 16;
    153   while (ncap < want) ncap *= 2;
    154   MCLabelInfo* nbuf = arena_array(&mc->func_arena, MCLabelInfo, ncap);
    155   if (mc->labels) memcpy(nbuf, mc->labels, sizeof(MCLabelInfo) * mc->nlabels);
    156   /* The grown tail is left uninitialized: mc_label_new fully assigns every
    157    * field of the one slot it hands out before any consumer indexes it, and
    158    * nothing ever reads labels[i] for i >= nlabels (every access guards on
    159    * id < nlabels and rejects MC_LABEL_NONE). */
    160   mc->labels = nbuf;
    161   mc->cap = ncap;
    162 }
    163 
    164 static void emit_label_data_reloc_now(MCImpl* mc, MCLabel label,
    165                                       const MCDataLabelRef* r) {
    166   /* Reference the label's per-block local symbol (its value IS the label's
    167    * offset) rather than the enclosing function symbol + a baked byte offset.
    168    * That makes the entry genuinely relocatable: a third-party assembler that
    169    * re-encodes the function to different instruction lengths still resolves it
    170    * to the right address (a fixed fn+offset would point into the wrong
    171    * instruction). */
    172   ObjSymId sym = mc_label_symbol(&mc->base, label);
    173   i64 addend = r->extra_addend;
    174   u8 bytes[8];
    175   u32 i;
    176   int big_endian = mc->base.c->target.big_endian;
    177   /* Patch the inline addend (Mach-O ARM64_RELOC_UNSIGNED reads only the inline
    178    * value) and also pass it in the reloc record (ELF RELA / the JIT linker's
    179    * link_reloc_apply, where the inline gets overwritten by S + A). Both paths
    180    * converge on sym + addend at runtime. */
    181   memset(bytes, 0, sizeof bytes);
    182   for (i = 0; i < r->width && i < sizeof bytes; ++i) {
    183     u32 shift = big_endian ? (r->width - 1u - i) * 8u : i * 8u;
    184     bytes[i] = (u8)((u64)addend >> shift);
    185   }
    186   obj_patch(mc->base.obj, r->data_sec, r->data_offset, bytes, r->width);
    187   mc_emit_reloc_at(&mc->base, r->data_sec, r->data_offset, r->kind, sym,
    188                          addend, /*explicit_addend=*/1, /*pair=*/0);
    189 }
    190 
    191 static void apply_fixup(MCImpl* mc, const MCFixup* fx, u32 target_offset) {
    192   /* signed displacement from end-of-instruction position to target. */
    193   ArchLabelFixup desc;
    194   const ArchImpl* arch;
    195 
    196   memset(&desc, 0, sizeof desc);
    197   desc.obj = mc->base.obj;
    198   desc.sec_id = fx->sec_id;
    199   desc.offset = fx->offset;
    200   desc.width = fx->width;
    201   desc.kind = fx->kind;
    202   desc.disp = (i64)target_offset - (i64)fx->offset + fx->addend;
    203   desc.cur_func_sym = mc->base.cur_func_sym;
    204   desc.cur_func_start = mc->base.cur_func_start;
    205 
    206   arch = arch_for_compiler(mc->base.c);
    207   if (!arch || !arch->apply_label_fixup ||
    208       arch->apply_label_fixup(mc->base.c, &desc) != 0) {
    209     compiler_panic(mc->base.c, mc->base.loc,
    210                    "MCEmitter: unsupported label-ref reloc kind %d",
    211                    (int)fx->kind);
    212   }
    213 }
    214 
    215 /* Lazily mint (and return) a per-label SB_LOCAL symbol defined at the label's
    216  * placement, for code-location references an encoding-divergent assembler must
    217  * be able to recompute: switch jump-table entries and `&&label` address-takes
    218  * relocate against it instead of baking a fixed offset. Created undefined if
    219  * the label is not yet placed (a forward reference) and defined in
    220  * mc_label_place; defined immediately otherwise. The name is per-object-unique
    221  * (MCLabel ids are monotonic within a TU). */
    222 ObjSymId mc_label_symbol(MCEmitter* m, MCLabel id) {
    223   MCImpl* mc = impl_of(m);
    224   MCLabelInfo* li;
    225   char buf[40];
    226   StrBuf sb;
    227   Sym name;
    228   if (id == MC_LABEL_NONE || id >= mc->nlabels) {
    229     compiler_panic(m->c, m->loc, "MCEmitter: bad label %u for symbol",
    230                    (unsigned)id);
    231   }
    232   li = &mc->labels[id];
    233   if (li->block_sym != OBJ_SYM_NONE) return li->block_sym;
    234   strbuf_init(&sb, buf, sizeof buf);
    235   strbuf_put_slice(&sb, SLICE_LIT(".Lcfblk."));
    236   strbuf_put_u64(&sb, (u64)id);
    237   name = pool_intern_slice(m->c->global, strbuf_slice(&sb));
    238   li->block_sym = obj_symbol(m->obj, name, SB_LOCAL, SK_NOTYPE,
    239                              li->placed ? li->sec_id : OBJ_SEC_NONE,
    240                              li->placed ? (u64)li->offset : 0u, 0);
    241   /* A block-address symbol is a relocation anchor within its function, not a
    242    * separately collectible/reorderable content unit. This distinction is
    243    * material on Mach-O, where every ordinary symbol under
    244    * MH_SUBSECTIONS_VIA_SYMBOLS otherwise starts a new atom. */
    245   obj_symbol_set_atom_subordinate(m->obj, li->block_sym, 1);
    246   return li->block_sym;
    247 }
    248 
    249 /* ---- emission ops (called directly by the arch backends) ---- */
    250 
    251 /* (Re)point the typed-store cursor at the active section's tail chunk write
    252  * frontier. Called after set_section and after any non-cursor write path
    253  * (mc_emit_bytes / fills) that may have grown or replaced the tail chunk. The
    254  * cursor is left NULL/empty (forcing the slow path) when there is no PROGBITS
    255  * buffer or the tail chunk is full or absent — buf_write_slow then mints/grows
    256  * the chunk and the next repoint picks it up. The invariant maintained
    257  * everywhere: cur == cur_chunk->data + cur_chunk->used (when cur_chunk != NULL),
    258  * with cur_chunk->used / cur_bytes->total kept coherent by mc_emit32. */
    259 static void mc_cursor_repoint(MCEmitter* m) {
    260   Buf* b = m->cur_bytes;
    261   BufChunk* t = b ? b->tail : NULL;
    262   if (t) {
    263     m->cur_chunk = t;
    264     m->cur = t->data + t->used;
    265     m->cur_end = t->data + t->cap;
    266   } else {
    267     m->cur_chunk = NULL;
    268     m->cur = NULL;
    269     m->cur_end = NULL;
    270   }
    271 }
    272 
    273 void mc_emit_bytes(MCEmitter* m, const u8* data, size_t n) {
    274   /* Fast path: append straight to the cached section buffer (inlined
    275    * buf_write). cur_bytes is NULL for NOBITS/.bss/none, where obj_write does
    276    * the bss_size accounting instead. */
    277   if (m->cur_bytes)
    278     buf_write(m->cur_bytes, data, n);
    279   else
    280     obj_write(m->obj, m->section_id, data, n);
    281   /* buf_write may have grown/replaced the tail chunk and bumped used/total;
    282    * re-point the typed-store cursor so the next mc_emit32 stays coherent. */
    283   mc_cursor_repoint(m);
    284 }
    285 
    286 /* Out-of-line slow path for mc_emit32: the tail chunk is full, absent, or the
    287  * section is NOBITS. Route through the normal byte sink (which keeps used/total
    288  * coherent and re-points the cursor), so the fast path can branch on a simple
    289  * pointer-bounds test. */
    290 static void mc_emit32_slow(MCEmitter* m, u32 word) {
    291   u8 b[4];
    292   wr_u32_le(b, word);
    293   mc_emit_bytes(m, b, sizeof b);
    294 }
    295 
    296 void mc_emit32(MCEmitter* m, u32 word) {
    297   /* Typed-store fast path: one const-width 4-byte store through the cursor,
    298    * then bump the chunk/buffer lengths to keep the Buf coherent. No staging
    299    * buffer, no out-of-line memcpy call. */
    300   u8* cur = m->cur;
    301   if (cur && cur + 4 <= m->cur_end) {
    302     wr_u32_le(cur, word);
    303     m->cur = cur + 4;
    304     m->cur_chunk->used += 4u;
    305     m->cur_bytes->total += 4u;
    306   } else {
    307     mc_emit32_slow(m, word);
    308   }
    309 }
    310 
    311 u32 mc_pos(MCEmitter* m) { return obj_pos(m->obj, m->section_id); }
    312 
    313 void mc_set_loc(MCEmitter* m, SrcLoc loc) { m->loc = loc; }
    314 
    315 void mc_set_section(MCEmitter* m, u32 section_id) {
    316   m->section_id = section_id;
    317   /* Cache the active section's byte buffer so the hot emit path avoids the
    318    * per-instruction Sections_at deref + nobits branch. NULL for NOBITS/.bss
    319    * (or none): emit then falls back to obj_write for bss_size accounting. */
    320   m->cur_bytes = obj_section_bytes(m->obj, section_id);
    321   mc_cursor_repoint(m);
    322 }
    323 
    324 MCLabel mc_label_new(MCEmitter* m) {
    325   MCImpl* mc = impl_of(m);
    326   if (mc->nlabels == 0) {
    327     labels_grow(mc, 1);
    328     mc->nlabels = 1;
    329   } /* skip 0 */
    330   labels_grow(mc, mc->nlabels + 1);
    331   u32 id = mc->nlabels++;
    332   MCLabelInfo* li = &mc->labels[id];
    333   li->placed = 0;
    334   li->sec_id = 0;
    335   li->offset = 0;
    336   li->pending = NULL;
    337   li->pending_data = NULL;
    338   li->block_sym = OBJ_SYM_NONE;
    339   return (MCLabel)id;
    340 }
    341 
    342 void mc_label_place(MCEmitter* m, MCLabel id) {
    343   MCImpl* mc = impl_of(m);
    344   if (id == MC_LABEL_NONE || id >= mc->nlabels) {
    345     compiler_panic(m->c, mc->base.loc, "MCEmitter: bad label %u", (unsigned)id);
    346   }
    347   MCLabelInfo* li = &mc->labels[id];
    348   if (li->placed) {
    349     compiler_panic(m->c, mc->base.loc, "MCEmitter: label %u placed twice",
    350                    (unsigned)id);
    351   }
    352   li->placed = 1;
    353   li->sec_id = m->section_id;
    354   li->offset = obj_pos(m->obj, m->section_id);
    355   /* Define the lazily-minted block symbol (if any) now that the offset is
    356    * known — resolves the forward-reference case for jump-table / &&label
    357    * relocations emitted before the label was placed. */
    358   if (li->block_sym != OBJ_SYM_NONE)
    359     obj_symbol_define(m->obj, li->block_sym, li->sec_id, (u64)li->offset, 0);
    360   /* Apply pending intra-section fixups. */
    361   for (MCFixup* fx = li->pending; fx; fx = fx->next) {
    362     apply_fixup(mc, fx, li->offset);
    363   }
    364   li->pending = NULL;
    365   /* Resolve any deferred data-section relocations referencing this label.
    366    * MCEmitter's cur_func_sym/cur_func_start track the function whose
    367    * body is currently being emitted; the label is always placed inside
    368    * its owning function's emit, so the active function context matches. */
    369   for (MCDataLabelRef* r = li->pending_data; r; r = r->next) {
    370     emit_label_data_reloc_now(mc, id, r);
    371   }
    372   li->pending_data = NULL;
    373 }
    374 
    375 void mc_emit_fill(MCEmitter* m, size_t n, u8 byte) {
    376   u8 buf[64];
    377   memset(buf, byte, sizeof buf);
    378   while (n > 0) {
    379     size_t k = n < sizeof buf ? n : sizeof buf;
    380     obj_write(m->obj, m->section_id, buf, k);
    381     n -= k;
    382   }
    383   /* obj_write grew the tail directly (bypassing the typed-store cursor);
    384    * re-point so a following mc_emit32 stays coherent. */
    385   mc_cursor_repoint(m);
    386 }
    387 
    388 void mc_emit_align(MCEmitter* m, u32 align, u8 fill) {
    389   const Section* s;
    390   if (align <= 1) return;
    391   s = obj_section_get(m->obj, m->section_id);
    392   if (s && align > s->align)
    393     obj_section_set_align(m->obj, m->section_id, align);
    394   u32 cur = obj_pos(m->obj, m->section_id);
    395   u32 misalign = cur & (align - 1);
    396   if (misalign == 0) return;
    397   mc_emit_fill(m, align - misalign, fill);
    398 }
    399 
    400 void mc_emit_reloc(MCEmitter* m, RelocKind k, ObjSymId sym, i64 addend) {
    401   obj_reloc(m->obj, m->section_id, obj_pos(m->obj, m->section_id), k, sym,
    402             addend);
    403 }
    404 
    405 void mc_emit_reloc_at(MCEmitter* m, u32 section_id, u32 offset,
    406                             RelocKind k, ObjSymId sym, i64 addend,
    407                             int explicit_addend, int pair) {
    408   obj_reloc_ex(m->obj, section_id, offset, k, sym, addend, explicit_addend,
    409                pair);
    410 }
    411 
    412 void mc_emit_label_ref(MCEmitter* m, MCLabel id, RelocKind kind,
    413                              u32 width, i64 addend) {
    414   MCImpl* mc = impl_of(m);
    415   if (id == MC_LABEL_NONE || id >= mc->nlabels) {
    416     compiler_panic(m->c, mc->base.loc, "MCEmitter: bad label %u", (unsigned)id);
    417   }
    418   MCLabelInfo* li = &mc->labels[id];
    419   MCFixup* fx = arena_new(&mc->func_arena, MCFixup);
    420   fx->sec_id = m->section_id;
    421   fx->offset = obj_pos(m->obj, m->section_id) -
    422                width; /* fixup site is the just-emitted insn */
    423   fx->width = width;
    424   fx->kind = kind;
    425   fx->addend = addend;
    426   fx->next = NULL;
    427   if (li->placed) {
    428     apply_fixup(mc, fx, li->offset);
    429   } else {
    430     fx->next = li->pending;
    431     li->pending = fx;
    432   }
    433 }
    434 
    435 void mc_emit_label_data_reloc(MCEmitter* m, u32 data_sec, u32 data_offset,
    436                                     MCLabel id, RelocKind kind, u32 width,
    437                                     i64 extra_addend) {
    438   MCImpl* mc = impl_of(m);
    439   MCLabelInfo* li;
    440   if (id == MC_LABEL_NONE || id >= mc->nlabels) {
    441     compiler_panic(m->c, m->loc, "MCEmitter: bad label %u", (unsigned)id);
    442   }
    443   li = &mc->labels[id];
    444   if (li->placed) {
    445     MCDataLabelRef tmp;
    446     tmp.data_sec = data_sec;
    447     tmp.data_offset = data_offset;
    448     tmp.kind = kind;
    449     tmp.width = width;
    450     tmp.extra_addend = extra_addend;
    451     tmp.next = NULL;
    452     emit_label_data_reloc_now(mc, id, &tmp);
    453     return;
    454   }
    455   {
    456     MCDataLabelRef* r = arena_new(&mc->func_arena, MCDataLabelRef);
    457     r->data_sec = data_sec;
    458     r->data_offset = data_offset;
    459     r->kind = kind;
    460     r->width = width;
    461     r->extra_addend = extra_addend;
    462     r->next = li->pending_data;
    463     li->pending_data = r;
    464   }
    465 }
    466 
    467 
    468 /* CFI: buffered for .eh_frame emission. Backend calls cfi_startproc to
    469  * open a per-function FDE record, then cfi_def_cfa / cfi_offset / ...
    470  * around the prologue; mc_emit_eh_frame builds the section at TU
    471  * finalize. */
    472 
    473 static void fde_push(MCImpl* mc, u8 kind, u32 reg, i32 imm) {
    474   CfiFde* fde;
    475   CfiDirective* d;
    476   Heap* heap;
    477   u32 pc_off;
    478   if (mc->cur_fde < 0) {
    479     compiler_panic(mc->base.c, mc->base.loc,
    480                    "MCEmitter: CFI directive outside cfi_startproc");
    481   }
    482   fde = &mc->fdes[mc->cur_fde];
    483   if (mc->base.section_id != fde->func_section) {
    484     compiler_panic(mc->base.c, mc->base.loc,
    485                    "MCEmitter: CFI directive in wrong section");
    486   }
    487   heap = mc->base.c->ctx->heap;
    488   /* The current FDE is always the last one, so its directives sit at the tail
    489    * of the shared vector (dir_start + ndir == ndirs); append there. */
    490   if (mc->ndirs == mc->dirs_cap) {
    491     u32 new_cap = mc->dirs_cap ? mc->dirs_cap * 2u : 64u;
    492     CfiDirective* nbuf = (CfiDirective*)heap->alloc(
    493         heap, sizeof(CfiDirective) * new_cap, _Alignof(CfiDirective));
    494     if (!nbuf) compiler_panic(mc->base.c, mc->base.loc, "MCEmitter: CFI OOM");
    495     if (mc->dirs) {
    496       memcpy(nbuf, mc->dirs, sizeof(CfiDirective) * mc->ndirs);
    497       heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap);
    498     }
    499     mc->dirs = nbuf;
    500     mc->dirs_cap = new_cap;
    501   }
    502   if (mc->has_pc_override) {
    503     /* Sticky until cfi_endproc: every directive in a func_end prologue batch
    504      * shares the post-prologue PC set once before cfi_def_cfa. (A one-shot
    505      * override only covered the first directive, so the saved-register and
    506      * return-address offset rules fell back to obj_pos() — the function END —
    507      * making mid-function unwind unable to recover them.) */
    508     pc_off = mc->pc_override;
    509   } else {
    510     pc_off = obj_pos(mc->base.obj, mc->base.section_id) - fde->func_start;
    511   }
    512   d = &mc->dirs[mc->ndirs++];
    513   fde->ndir++;
    514   d->pc_offset = pc_off;
    515   d->kind = kind;
    516   d->reg = reg;
    517   d->imm = imm;
    518 }
    519 
    520 void mc_cfi_startproc(MCEmitter* m) {
    521   MCImpl* mc = impl_of(m);
    522   Heap* heap = m->c->ctx->heap;
    523   if (mc->cur_fde >= 0) {
    524     compiler_panic(m->c, m->loc, "MCEmitter: nested cfi_startproc");
    525   }
    526   if (m->cur_func_sym == OBJ_SYM_NONE) {
    527     /* Backend must call mc_begin_function before cfi_startproc; tolerate
    528      * the no-op for stand-ins. */
    529     return;
    530   }
    531   if (mc->nfdes == mc->fdes_cap) {
    532     u32 new_cap = mc->fdes_cap ? mc->fdes_cap * 2u : 8u;
    533     CfiFde* nbuf =
    534         (CfiFde*)heap->alloc(heap, sizeof(CfiFde) * new_cap, _Alignof(CfiFde));
    535     if (!nbuf) compiler_panic(m->c, m->loc, "MCEmitter: CFI OOM");
    536     if (mc->fdes) {
    537       memcpy(nbuf, mc->fdes, sizeof(CfiFde) * mc->nfdes);
    538       heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap);
    539     }
    540     mc->fdes = nbuf;
    541     mc->fdes_cap = new_cap;
    542   }
    543   mc->cur_fde = (i32)mc->nfdes;
    544   mc->has_pc_override = 0; /* no override carries across an FDE boundary */
    545   {
    546     CfiFde* fde = &mc->fdes[mc->nfdes++];
    547     fde->func_sym = m->cur_func_sym;
    548     fde->func_section = m->section_id;
    549     fde->func_start = obj_pos(m->obj, m->section_id);
    550     fde->func_end = fde->func_start;
    551     fde->dir_start = mc->ndirs;
    552     fde->ndir = 0;
    553   }
    554 }
    555 
    556 void mc_cfi_endproc(MCEmitter* m) {
    557   MCImpl* mc = impl_of(m);
    558   CfiFde* fde;
    559   if (mc->cur_fde < 0) return;
    560   fde = &mc->fdes[mc->cur_fde];
    561   fde->func_end = obj_pos(m->obj, m->section_id);
    562   mc->cur_fde = -1;
    563   mc->has_pc_override =
    564       0; /* the sticky prologue-PC override ends with the FDE */
    565 }
    566 
    567 void mc_cfi_def_cfa(MCEmitter* m, u32 r, i32 o) {
    568   MCImpl* mc = impl_of(m);
    569   if (mc->cur_fde < 0) return;
    570   fde_push(mc, CFI_OP_DEF_CFA, r, o);
    571 }
    572 void mc_cfi_offset(MCEmitter* m, u32 r, i32 o) {
    573   MCImpl* mc = impl_of(m);
    574   if (mc->cur_fde < 0) return;
    575   fde_push(mc, CFI_OP_OFFSET, r, o);
    576 }
    577 void mc_cfi_set_next_pc_offset(MCEmitter* m, u32 pc_offset) {
    578   MCImpl* mc = impl_of(m);
    579   mc->has_pc_override = 1;
    580   mc->pc_override = pc_offset;
    581 }
    582 
    583 /* ---- construction ---- */
    584 
    585 static void mc_cleanup(void* arg) { mc_free((MCEmitter*)arg); }
    586 
    587 MCEmitter* mc_new(Compiler* c, ObjBuilder* o) {
    588   MCImpl* mc = arena_new(c->tu, MCImpl);
    589   memset(mc, 0, sizeof *mc);
    590 
    591   MCEmitter* base = &mc->base;
    592   base->c = c;
    593   base->obj = o;
    594   base->section_id = OBJ_SEC_NONE;
    595   base->cur_func_sym = OBJ_SYM_NONE;
    596   base->cur_func_section = 0;
    597   base->cur_func_start = 0;
    598 
    599   arena_init(&mc->func_arena, c->ctx->heap, 0);
    600   mc->labels = NULL;
    601   mc->nlabels = 0;
    602   mc->cap = 0;
    603   mc->fdes = NULL;
    604   mc->nfdes = 0;
    605   mc->fdes_cap = 0;
    606   mc->cur_fde = -1;
    607   mc->dirs = NULL;
    608   mc->ndirs = 0;
    609   mc->dirs_cap = 0;
    610   mc->eh_frame_emitted = 0;
    611   mc->has_pc_override = 0;
    612   mc->pc_override = 0;
    613 
    614   compiler_defer(c, mc_cleanup, base);
    615   return base;
    616 }
    617 
    618 void mc_free(MCEmitter* m) {
    619   MCImpl* mc;
    620   Heap* heap;
    621   if (!m) return;
    622   mc = impl_of(m);
    623   /* Release any CFI directive buffers when the caller never invoked
    624    * mc_emit_eh_frame (e.g. test harness or early teardown). */
    625   if (!mc->eh_frame_emitted && mc->fdes) {
    626     heap = m->c->ctx->heap;
    627     if (mc->dirs) {
    628       heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap);
    629       mc->dirs = NULL;
    630       mc->dirs_cap = 0;
    631       mc->ndirs = 0;
    632     }
    633     heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap);
    634     mc->fdes = NULL;
    635     mc->fdes_cap = 0;
    636     mc->nfdes = 0;
    637   }
    638   arena_fini(&mc->func_arena);
    639 }
    640 
    641 void mc_begin_function(MCEmitter* m, ObjSymId sym, u32 section_id,
    642                        u32 start_offset) {
    643   if (!m) return;
    644   /* Reclaim the previous function's label/fixup/dataref scratch (see the
    645    * func_arena note on MCImpl). The labels vector is re-grown from the reset
    646    * arena on demand, so drop the stale pointer/count here. */
    647   {
    648     MCImpl* mc = impl_of(m);
    649     arena_reset(&mc->func_arena);
    650     mc->labels = NULL;
    651     mc->nlabels = 0;
    652     mc->cap = 0;
    653   }
    654   m->cur_func_sym = sym;
    655   m->cur_func_section = section_id;
    656   m->cur_func_start = start_offset;
    657 }
    658 
    659 void mc_end_function(MCEmitter* m) {
    660   if (!m) return;
    661   m->cur_func_sym = OBJ_SYM_NONE;
    662   m->cur_func_section = 0;
    663   m->cur_func_start = 0;
    664 }
    665 
    666 /* ============================================================
    667  * .eh_frame emitter
    668  * ============================================================ */
    669 
    670 static void buf_uleb(Buf* b, u64 v) {
    671   u8 tmp[10];
    672   u32 n = 0;
    673   do {
    674     u8 byte = (u8)(v & 0x7fu);
    675     v >>= 7;
    676     if (v) byte |= 0x80u;
    677     tmp[n++] = byte;
    678   } while (v);
    679   buf_write(b, tmp, n);
    680 }
    681 
    682 static void buf_sleb(Buf* b, i64 v) {
    683   u8 tmp[10];
    684   u32 n = 0;
    685   int more = 1;
    686   while (more) {
    687     u8 byte = (u8)(v & 0x7fu);
    688     v >>= 7;
    689     if ((v == 0 && (byte & 0x40u) == 0) || (v == -1 && (byte & 0x40u) != 0)) {
    690       more = 0;
    691     } else {
    692       byte |= 0x80u;
    693     }
    694     tmp[n++] = byte;
    695   }
    696   buf_write(b, tmp, n);
    697 }
    698 
    699 static void buf_u8(Buf* b, u8 v) { buf_write(b, &v, 1); }
    700 
    701 static void buf_u32le(Buf* b, u32 v) {
    702   u8 t[4];
    703   t[0] = (u8)v;
    704   t[1] = (u8)(v >> 8);
    705   t[2] = (u8)(v >> 16);
    706   t[3] = (u8)(v >> 24);
    707   buf_write(b, t, 4);
    708 }
    709 
    710 static void buf_pad_to(Buf* b, u32 entry_start, u32 align) {
    711   u32 cur = buf_pos(b);
    712   u32 rel = cur - entry_start;
    713   u32 mis = rel & (align - 1u);
    714   u32 pad;
    715   if (mis == 0) return;
    716   pad = align - mis;
    717   while (pad--) buf_u8(b, 0);
    718 }
    719 
    720 static void encode_cfi_directive(Buf* prog, const CfiDirective* d, u32* cur_loc,
    721                                  i32 code_align, i32 data_align) {
    722   u32 delta = d->pc_offset - *cur_loc;
    723   if (delta) {
    724     u32 fac = (code_align > 0) ? (delta / (u32)code_align) : delta;
    725     if (fac < 0x40u) {
    726       buf_u8(prog, DW_CFA_advance_loc | (u8)fac);
    727     } else if (fac < 0x100u) {
    728       buf_u8(prog, DW_CFA_advance_loc1);
    729       buf_u8(prog, (u8)fac);
    730     } else if (fac < 0x10000u) {
    731       buf_u8(prog, DW_CFA_advance_loc2);
    732       buf_u8(prog, (u8)(fac & 0xff));
    733       buf_u8(prog, (u8)(fac >> 8));
    734     } else {
    735       buf_u8(prog, DW_CFA_advance_loc4);
    736       buf_u32le(prog, fac);
    737     }
    738     *cur_loc = d->pc_offset;
    739   }
    740   switch ((CfiOpKind)d->kind) {
    741     case CFI_OP_DEF_CFA:
    742       buf_u8(prog, DW_CFA_def_cfa);
    743       buf_uleb(prog, d->reg);
    744       buf_uleb(prog, (u64)(d->imm < 0 ? 0 : d->imm));
    745       break;
    746     case CFI_OP_OFFSET: {
    747       i64 fac;
    748       if (data_align == 0)
    749         fac = d->imm;
    750       else
    751         fac = (i64)d->imm / (i64)data_align;
    752       if (d->reg < 0x40u && fac >= 0) {
    753         buf_u8(prog, DW_CFA_offset | (u8)d->reg);
    754         buf_uleb(prog, (u64)fac);
    755       } else {
    756         buf_u8(prog, DW_CFA_offset_extended_sf);
    757         buf_uleb(prog, d->reg);
    758         buf_sleb(prog, fac);
    759       }
    760     } break;
    761   }
    762 }
    763 
    764 void mc_emit_eh_frame(MCEmitter* m) {
    765   MCImpl* mc;
    766   const ArchImpl* arch;
    767   Heap* heap;
    768   Buf body;
    769   ObjSecId eh_sec;
    770   Sym sec_name;
    771   u32 cie_offset_in_buf;
    772   u32 cie_len;
    773   u32 entry_start;
    774   u32 i;
    775   u8 fde_pe;
    776   if (!m) return;
    777   mc = impl_of(m);
    778   if (mc->eh_frame_emitted) return;
    779   if (mc->nfdes == 0) {
    780     mc->eh_frame_emitted = 1;
    781     return;
    782   }
    783   arch = arch_for_compiler(m->c);
    784   if (!arch || arch->cfi_return_addr_reg == 0u) {
    785     mc->eh_frame_emitted = 1;
    786     return;
    787   }
    788   /* Freestanding (bare-metal): emit no .eh_frame. kit marks .eh_frame
    789    * SF_ALLOC so a hosted unwinder can consume it, but a bare-metal link
    790    * (e.g. riscv32-none-elf) has no unwinder and would otherwise have to
    791    * /DISCARD/ the orphaned ALLOC section. emits_eh_frame is 0 exactly for
    792    * those freestanding targets and 1 for hosted output (linux/macos/windows/
    793    * freebsd/wasi), which is unaffected and byte-identical. */
    794   if (!m->c->target.emits_eh_frame) {
    795     heap = m->c->ctx->heap;
    796     if (mc->dirs) {
    797       heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap);
    798       mc->dirs = NULL;
    799       mc->dirs_cap = 0;
    800       mc->ndirs = 0;
    801     }
    802     if (mc->fdes) {
    803       heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap);
    804       mc->fdes = NULL;
    805       mc->fdes_cap = 0;
    806       mc->nfdes = 0;
    807     }
    808     mc->eh_frame_emitted = 1;
    809     return;
    810   }
    811   heap = m->c->ctx->heap;
    812   fde_pe = (u8)(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
    813 
    814   buf_init(&body, heap);
    815 
    816   /* CIE */
    817   cie_offset_in_buf = buf_pos(&body);
    818   buf_u32le(&body, 0);
    819   entry_start = buf_pos(&body);
    820   buf_u32le(&body, 0); /* CIE_id */
    821   buf_u8(&body, 1);    /* version */
    822   buf_u8(&body, 'z');
    823   buf_u8(&body, 'R');
    824   buf_u8(&body, 0);
    825   buf_uleb(&body, (u64)(u32)arch->cfi_code_align_factor);
    826   buf_sleb(&body, (i64)arch->cfi_data_align_factor);
    827   buf_uleb(&body, (u64)arch->cfi_return_addr_reg);
    828   buf_uleb(&body, 1);
    829   buf_u8(&body, fde_pe);
    830   buf_u8(&body, DW_CFA_def_cfa);
    831   buf_uleb(&body, (u64)arch->cfi_cfa_init_reg);
    832   buf_uleb(
    833       &body,
    834       (u64)(arch->cfi_cfa_init_offset < 0 ? 0 : arch->cfi_cfa_init_offset));
    835   buf_pad_to(&body, entry_start, 4u);
    836   cie_len = buf_pos(&body) - entry_start;
    837   {
    838     u8 lbytes[4];
    839     lbytes[0] = (u8)cie_len;
    840     lbytes[1] = (u8)(cie_len >> 8);
    841     lbytes[2] = (u8)(cie_len >> 16);
    842     lbytes[3] = (u8)(cie_len >> 24);
    843     buf_patch(&body, cie_offset_in_buf, lbytes, 4);
    844   }
    845 
    846   {
    847     u32* pc_slot_rels =
    848         (u32*)heap->alloc(heap, sizeof(u32) * mc->nfdes, _Alignof(u32));
    849     ObjSymId* fde_syms = (ObjSymId*)heap->alloc(
    850         heap, sizeof(ObjSymId) * mc->nfdes, _Alignof(ObjSymId));
    851     if (!pc_slot_rels || !fde_syms) {
    852       if (pc_slot_rels) heap->free(heap, pc_slot_rels, sizeof(u32) * mc->nfdes);
    853       if (fde_syms) heap->free(heap, fde_syms, sizeof(ObjSymId) * mc->nfdes);
    854       buf_fini(&body);
    855       compiler_panic(m->c, m->loc, "MCEmitter: CFI OOM");
    856     }
    857     for (i = 0; i < mc->nfdes; ++i) {
    858       const CfiFde* fde = &mc->fdes[i];
    859       u32 fde_offset_in_buf = buf_pos(&body);
    860       u32 fde_entry_start;
    861       u32 fde_len;
    862       u32 pc_slot;
    863       u32 cur_loc = 0;
    864       u32 j;
    865       i64 cie_back_off;
    866       buf_u32le(&body, 0);
    867       fde_entry_start = buf_pos(&body);
    868       cie_back_off = (i64)fde_entry_start - (i64)cie_offset_in_buf;
    869       buf_u32le(&body, (u32)cie_back_off);
    870       pc_slot = buf_pos(&body);
    871       pc_slot_rels[i] = pc_slot;
    872       fde_syms[i] = fde->func_sym;
    873       buf_u32le(&body, 0); /* initial_location (reloc) */
    874       buf_u32le(&body, fde->func_end - fde->func_start); /* range */
    875       buf_uleb(&body, 0);                                /* aug_data_len = 0 */
    876       for (j = 0; j < fde->ndir; ++j) {
    877         encode_cfi_directive(&body, &mc->dirs[fde->dir_start + j], &cur_loc,
    878                              arch->cfi_code_align_factor,
    879                              arch->cfi_data_align_factor);
    880       }
    881       buf_pad_to(&body, fde_entry_start, 4u);
    882       fde_len = buf_pos(&body) - fde_entry_start;
    883       {
    884         u8 lbytes[4];
    885         lbytes[0] = (u8)fde_len;
    886         lbytes[1] = (u8)(fde_len >> 8);
    887         lbytes[2] = (u8)(fde_len >> 16);
    888         lbytes[3] = (u8)(fde_len >> 24);
    889         buf_patch(&body, fde_offset_in_buf, lbytes, 4);
    890       }
    891     }
    892     /* Terminator zero-length entry. */
    893     buf_u32le(&body, 0);
    894 
    895     /* Section name: Mach-O wants "__TEXT,__eh_frame", ELF wants
    896      * ".eh_frame". The Mach-O emitter splits on comma; the ELF emitter
    897      * uses the literal as section name. */
    898     if (m->c->target.obj == KIT_OBJ_MACHO) {
    899       sec_name =
    900           pool_intern_slice(m->c->global, SLICE_LIT("__TEXT,__eh_frame"));
    901     } else {
    902       sec_name = pool_intern_slice(m->c->global, SLICE_LIT(".eh_frame"));
    903     }
    904     eh_sec = obj_section(m->obj, sec_name, SEC_OTHER, SF_ALLOC, 8);
    905     {
    906       u32 total = buf_pos(&body);
    907       u8* bytes = (u8*)heap->alloc(heap, total, 1);
    908       if (!bytes) {
    909         heap->free(heap, pc_slot_rels, sizeof(u32) * mc->nfdes);
    910         heap->free(heap, fde_syms, sizeof(ObjSymId) * mc->nfdes);
    911         buf_fini(&body);
    912         compiler_panic(m->c, m->loc, "MCEmitter: CFI OOM");
    913       }
    914       buf_flatten(&body, bytes);
    915       obj_write(m->obj, eh_sec, bytes, total);
    916       heap->free(heap, bytes, total);
    917     }
    918     for (i = 0; i < mc->nfdes; ++i) {
    919       /* R_PC32 against the function symbol: linker writes
    920        * (S + A - P) into the 4-byte slot, yielding a pc-relative
    921        * displacement that the unwinder can decode via DW_EH_PE_pcrel
    922        * | DW_EH_PE_sdata4. */
    923       obj_reloc_ex(m->obj, eh_sec, pc_slot_rels[i], R_PC32, fde_syms[i],
    924                    /*addend=*/0, /*explicit_addend=*/1, /*pair=*/0);
    925     }
    926     heap->free(heap, pc_slot_rels, sizeof(u32) * mc->nfdes);
    927     heap->free(heap, fde_syms, sizeof(ObjSymId) * mc->nfdes);
    928   }
    929 
    930   buf_fini(&body);
    931 
    932   if (mc->dirs) {
    933     heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap);
    934     mc->dirs = NULL;
    935     mc->dirs_cap = 0;
    936     mc->ndirs = 0;
    937   }
    938   if (mc->fdes) {
    939     heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap);
    940     mc->fdes = NULL;
    941     mc->fdes_cap = 0;
    942     mc->nfdes = 0;
    943   }
    944   mc->eh_frame_emitted = 1;
    945 }