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 }