kit

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

mc.c (30031B)


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