link.c (102226B)
1 /* link_emit_macho — write a dyld-loadable arm64 MH_EXECUTE. 2 * 3 * Mach-O peer of link_emit_elf. Produces a position-independent 4 * MH_EXECUTE that links against libSystem.B.dylib (or any other 5 * dylib/.tbd input) via LC_LOAD_DYLIB + LC_DYLD_CHAINED_FIXUPS. The 6 * binary is ad-hoc codesigned at the tail so the kernel will exec it 7 * on macOS 11+. 8 * 9 * Layout (Apple's stock arm64 layout): 10 * 11 * __PAGEZERO vmaddr 0, vmsize 0x100000000, no file bytes 12 * __TEXT (R-X) 13 * mach_header_64 14 * load commands 15 * [SF_EXEC sections — .text] 16 * [SF_ALLOC R-only sections — .rodata, init/fini_array, etc.] 17 * __stubs (12B per import-func) 18 * __DATA_CONST (RW initially, dyld marks R-only after fixups) 19 * __got (8B per import — both data and func imports) 20 * __DATA (R-W) 21 * [SF_WRITE sections — .data, .bss] 22 * __LINKEDIT (R) 23 * dyld_chained_fixups blob 24 * dyld_exports_trie blob 25 * function starts (empty) 26 * data in code (empty) 27 * symtab 28 * indirect symbol table (one entry per __stubs and __got slot) 29 * strtab 30 * code signature 31 * 32 * Imports are routed: 33 * CALL26/JUMP26 against an imported function -> __stubs entry 34 * GOT_LOAD_PAGE21/PAGEOFF12 against any import -> __got slot 35 * ABS64 against an imported symbol -> chained-bind at site 36 * ABS64 against a defined internal symbol -> chained-rebase at site 37 * 38 * arm64-only. x86_64-macos arrives with x64 codegen. */ 39 40 #include "link/link.h" 41 42 #include <string.h> 43 44 #include "core/bytes.h" 45 #include "core/heap.h" 46 #include "core/pool.h" 47 #include "core/sha256.h" 48 #include "core/slice.h" 49 #include "core/util.h" 50 #include "core/vec.h" 51 #include "link/link_arch.h" 52 #include "link/link_internal.h" 53 #include "link/link_reloc_desc.h" 54 #include "obj/bytebuf.h" 55 #include "obj/format.h" 56 #include "obj/macho/macho.h" 57 58 /* ---- constants ---- */ 59 #define MZ_PAGEZERO 0x100000000ULL 60 #define MZ_PAGE 0x4000ULL 61 #define MZ_GOT_SIZE 8u 62 /* __DATA,__thread_ptrs slot size — one pointer per unique TLV referenced 63 * via TLVP_LOAD_PAGE21/PAGEOFF12. Each slot holds the address of the 64 * matching TLV descriptor in __DATA,__thread_vars. */ 65 #define MZ_TLVP_SIZE 8u 66 67 #define DYLD_CHAINED_PTR_64 2u 68 #define DYLD_CHAINED_IMPORT 1u 69 70 #define VM_PROT_READ 0x1u 71 #define VM_PROT_WRITE 0x2u 72 #define VM_PROT_EXECUTE 0x4u 73 74 #define CS_MAGIC_EMBEDDED_SIGNATURE 0xfade0cc0u 75 #define CS_MAGIC_CODEDIRECTORY 0xfade0c02u 76 #define CSSLOT_CODEDIRECTORY 0u 77 #define CS_HASHTYPE_SHA256 2u 78 #define CS_SHA256_LEN SHA256_DIGEST_LEN 79 /* Default code-signing hash page size (log2). Apple's tools sign with a page 80 * size that matches the target's native VM page: 4 KiB (12) on x86_64, but 81 * 16 KiB (14) on Apple-Silicon arm64. The kernel's loader validates the main 82 * executable against its own (16 KiB) pages, so a 4 KiB-page signature is 83 * rejected as invalid on arm64 once the image is large enough to span the 84 * mismatch — small images can slip through, large ones SIGKILL with "invalid 85 * signature". MCtx.cs_page_log2 carries the per-target choice; this is the 86 * x86_64 fallback. */ 87 #define CS_PAGE_SIZE_LOG2 12u 88 #define CS_EXECSEG_MAIN_BINARY 1u 89 90 /* extra LC ids */ 91 #define LC_DYLD_INFO_ONLY (0x22u | 0x80000000u) 92 #define LC_FUNCTION_STARTS_C 0x26u 93 #define LC_DATA_IN_CODE_C 0x29u 94 #define LC_CODE_SIGNATURE_C 0x1du 95 96 /* ---- byte buffer ---- 97 * Linkedit tables / load commands / the output file are staged in the shared 98 * ObjByteBuf (objbb_*). */ 99 100 /* ---- imports + dylibs ---- */ 101 102 typedef struct MachImp { 103 LinkSymId sym; 104 Sym name; 105 u32 dylib_ord; /* 1-based ordinal into LC_LOAD_DYLIB list */ 106 u32 stub_idx; /* 1-based index into __stubs (0 if data import) */ 107 u32 got_idx; /* 1-based index into __got */ 108 u32 imports_strx; /* offset into chained-fixups symbol pool */ 109 u8 is_func; 110 u8 weak; 111 /* internal=1 means this entry is an in-image symbol that's referenced 112 * via GOT_LOAD_PAGE21 / LD64_GOT_LO12_NC (clang emits these for any 113 * extern global so a single static-link can later become PIC). The 114 * GOT slot stores the symbol's image-relative vaddr and gets a 115 * chained-fixup rebase entry (or no entry at all for a weak-undef 116 * resolving to NULL). No dylib_ord / stub_idx / chained-fixup bind. */ 117 u8 internal; 118 } MachImp; 119 120 typedef struct MachDylib { 121 Sym install; 122 } MachDylib; 123 124 /* One slot in the synthetic __DATA,__thread_ptrs section per unique TLV 125 * descriptor referenced via TLVP_LOAD_PAGE21/PAGEOFF12. Modeled after 126 * MachImp's internal-GOT entries: the slot holds the descriptor address 127 * (REBASE for internal-to-image descriptors, BIND for dylib-imported 128 * ones). The descriptor itself is laid out in __DATA,__thread_vars by 129 * either the input objects (internal) or the providing dylib (imported). */ 130 typedef struct MachTlv { 131 LinkSymId sym; /* canonical descriptor LinkSymId */ 132 u32 tlv_idx; /* 1-based slot index in __thread_ptrs */ 133 u8 imported; /* 1 == descriptor lives in a dylib (BIND), 0 == internal 134 (REBASE) */ 135 u8 pad[3]; 136 u32 import_idx; /* 1-based MachImp index when imported (for chained-bind 137 ordinal) */ 138 } MachTlv; 139 140 /* ---- planned section ---- */ 141 142 typedef struct MSec { 143 /* Source: either a LinkSection (link_sec_id != 0) or a synthetic 144 * pre-built byte buffer (data + size). */ 145 LinkSectionId link_sec_id; 146 const u8* synth_data; 147 u32 synth_size; 148 /* Mach-O placement */ 149 const char* segname; 150 const char* sectname; 151 /* Inline storage for segname/sectname when split from a Mach-O 152 * `__SEG,__sect`-form LinkSection name. Names from string literals 153 * (synth sections, derived-from-flags defaults) point at .rodata 154 * and don't use these. 17 bytes: the on-disk field is a fixed 16 155 * (no NUL needed there), but these are read as C strings, so a full 156 * 16-char name (e.g. __debug_line_str) needs the extra NUL slot. */ 157 char segname_buf[17]; 158 char sectname_buf[17]; 159 u64 vaddr; 160 u64 file_offset; 161 u64 size; 162 u32 align; 163 u32 flags; /* S_TYPE | S_ATTR_* */ 164 u32 reserved1; 165 u32 reserved2; 166 u8 segidx; /* 1=__TEXT, 2=__DATA_CONST, 3=__DATA */ 167 u8 is_zerofill; 168 u8 pad[6]; 169 } MSec; 170 171 static void msec_repair_name_ptrs(MSec* m) { 172 if (m->segname_buf[0]) m->segname = m->segname_buf; 173 if (m->sectname_buf[0]) m->sectname = m->sectname_buf; 174 } 175 176 /* Segment slot indices in MCtx.segs[]. __DWARF carries the file-only 177 * .debug_* sections (debug-info retention); it sits before __LINKEDIT so 178 * the ad-hoc code signature stays the last bytes of the file. */ 179 enum { 180 MSEG_PAGEZERO = 0, 181 MSEG_TEXT = 1, 182 MSEG_DATA_CONST = 2, 183 MSEG_DATA = 3, 184 MSEG_DWARF = 4, 185 MSEG_LINKEDIT = 5, 186 MSEG_COUNT = 6, 187 }; 188 189 typedef struct MSeg { 190 const char* name; 191 u32 maxprot; 192 u32 initprot; 193 u64 vmaddr; 194 u64 vmsize; 195 u64 fileoff; 196 u64 filesize; 197 u32 nsects; /* MSec count in segment — internal layout */ 198 u32 first_sec; /* first index into MSec[] */ 199 u32 nouts; /* OutSec count in segment — what hits the file */ 200 u32 first_out; /* first index into OutSec[] */ 201 } MSeg; 202 203 /* On-disk section view: one record per (segname, sectname) within a 204 * segment. Mach-O requires this — emitting one section_64 per input 205 * MSec yields sibling __TEXT,__text records that violate the spec. 206 * Built from MSec[] after vaddr placement; reloc-apply still uses 207 * MSec[] for byte-buffer addressing. */ 208 typedef struct OutSec { 209 const char* segname; 210 const char* sectname; 211 u64 vaddr; 212 u64 file_offset; 213 u64 size; 214 u32 align; 215 u32 flags; 216 u32 reserved1; 217 u32 reserved2; 218 u8 segidx; 219 u8 is_zerofill; 220 } OutSec; 221 222 /* ---- main context ---- */ 223 224 typedef struct MCtx { 225 LinkImage* img; 226 Compiler* c; 227 Heap* h; 228 Writer* w; 229 Linker* linker; 230 const LinkArchDesc* link_arch; 231 const ObjMachoArchOps* macho; 232 233 /* imports */ 234 MachImp* imports; 235 u32 nimports; 236 u32 nimports_real; /* count of imports with internal=0 (== prefix length; 237 * collect_imports appends internal=1 entries last) */ 238 u32 nimport_funcs; 239 MachDylib* dylibs; 240 u32 ndylibs; 241 /* sym->import index, 1-based, 0 = not an import. Sized to LinkSymId space 242 * + 1. */ 243 u32* sym_to_imp; 244 u32 sym_to_imp_size; 245 246 /* sections + segments */ 247 MSec* secs; 248 u32 nsecs; 249 /* link_sec_id (1-based) -> backing MSec, NULL where none. Built once after 250 * plan_layout so shift_sections / patch_ptr map a section id to its MSec in 251 * O(1) instead of a linear scan of secs per reloc (was O(nrelocs*nsecs)). */ 252 MSec** by_link_sec; 253 u32 by_link_sec_cap; /* img->nsections + 1 */ 254 OutSec* outs; 255 u32 nouts; 256 MSeg segs[MSEG_COUNT]; /* PAGEZERO, TEXT, DATA_CONST, DATA, DWARF, LINKEDIT */ 257 u32 nsegs; 258 259 /* Synthetic byte buffers, owned. */ 260 u8* stubs_bytes; 261 u32 stubs_size; 262 u8* got_bytes; 263 u32 got_size; 264 /* TLV pointer slots — one entry in __DATA,__thread_ptrs per unique 265 * descriptor referenced via TLVP_LOAD_PAGE21/PAGEOFF12. sym_to_tlv 266 * maps LinkSymId → 1-based slot index (parallel to sym_to_imp). Slot 267 * bytes are populated at apply_relocs time once shift_sections has 268 * pinned descriptor vaddrs. */ 269 MachTlv* tlv_slots; 270 u32 ntlv; 271 u32* sym_to_tlv; 272 u32 sym_to_tlv_size; 273 u8* tlv_ptrs_bytes; 274 u32 tlv_ptrs_size; 275 u64 tlv_ptrs_vaddr; 276 /* Vaddr of the first thread-local-storage section 277 * (__thread_data / __thread_bss). Each TLV descriptor's word 2 278 * stores the symbol's offset within this image rather than an 279 * absolute address — see apply_relocs's S_THREAD_LOCAL_VARIABLES 280 * ABS64 special case. */ 281 u64 tls_image_vaddr; 282 u8 has_tls_image; 283 284 /* Final layout (computed during plan) */ 285 u64 text_vaddr; 286 u64 stubs_vaddr; 287 u64 got_vaddr; 288 u64 linkedit_vaddr; 289 u64 linkedit_fileoff; 290 u32 entry_offset; /* offset of entry within __TEXT segment */ 291 292 u64 headers_size; /* header + loadcmds */ 293 294 /* LINKEDIT contents */ 295 ObjByteBuf chained_fixups; 296 ObjByteBuf exports_trie; 297 ObjByteBuf symtab; /* binary nlist_64 array */ 298 ObjByteBuf strtab; 299 ObjByteBuf indirect; /* u32 array */ 300 ObjByteBuf fn_starts; 301 ObjByteBuf data_in_code; 302 ObjByteBuf codesig; 303 304 u32 chained_fixups_off; 305 u32 exports_trie_off; 306 u32 fn_starts_off; 307 u32 data_in_code_off; 308 u32 symtab_off; 309 u32 indirect_off; 310 u32 strtab_off; 311 u32 codesig_off; 312 u32 codesig_size; 313 u8 cs_page_log2; /* code-signing hash page size, log2 (12=4K x64, 14=16K arm64) */ 314 u32 nsyms; 315 316 u8 uuid[16]; 317 } MCtx; 318 319 /* ---- helpers for finding LinkSymbol vaddr ---- */ 320 321 static LinkSymbol* sym_at(LinkImage* img, LinkSymId id) { 322 if (id == LINK_SYM_NONE || id > LinkSyms_count(&img->syms)) return NULL; 323 return LinkSyms_at(&img->syms, id - 1); 324 } 325 326 /* ---- pass: collect imports ---- */ 327 328 static u32 dylib_ordinal_of(MCtx* x, Sym install) { 329 for (u32 j = 0; j < x->ndylibs; ++j) 330 if (x->dylibs[j].install == install) return j + 1u; 331 return 0; 332 } 333 334 static void collect_imports(MCtx* x) { 335 LinkImage* img = x->img; 336 Heap* h = x->h; 337 338 x->sym_to_imp_size = LinkSyms_count(&img->syms) + 1u; 339 x->sym_to_imp = 340 (u32*)h->alloc(h, sizeof(u32) * x->sym_to_imp_size, _Alignof(u32)); 341 if (!x->sym_to_imp) 342 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on sym_to_imp"); 343 memset(x->sym_to_imp, 0, sizeof(u32) * x->sym_to_imp_size); 344 345 u32 cap = 0, cap_d = 0; 346 for (u32 i = 0; i < LinkSyms_count(&img->syms); ++i) { 347 LinkSymbol* s = LinkSyms_at(&img->syms, i); 348 if (!s->imported) continue; 349 if (s->name == 0) continue; 350 LinkSymId canon = symhash_get(&img->globals, s->name); 351 if (canon != LINK_SYM_NONE && canon != s->id) continue; 352 if (VEC_GROW(h, x->imports, cap, x->nimports + 1u)) 353 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on imports"); 354 MachImp* mi = &x->imports[x->nimports++]; 355 memset(mi, 0, sizeof(*mi)); 356 mi->sym = s->id; 357 mi->name = s->name; 358 mi->is_func = (s->kind == SK_FUNC || s->kind == SK_IFUNC) ? 1 : 0; 359 mi->weak = (s->bind == SB_WEAK) ? 1 : 0; 360 x->sym_to_imp[s->id] = x->nimports; 361 } 362 363 /* Back-classify: any CALL26/JUMP26 reloc target -> function. */ 364 for (u32 i = 0; i < LinkRelocs_count(&img->relocs); ++i) { 365 LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 366 if (!reloc_kind_is_branch(x->c, r->kind)) continue; 367 if (r->target == LINK_SYM_NONE || r->target >= x->sym_to_imp_size) continue; 368 u32 idx = x->sym_to_imp[r->target]; 369 if (!idx) { 370 /* Resolve through canonical. */ 371 LinkSymbol* tgt = LinkSyms_at(&img->syms, r->target - 1); 372 if (tgt->name == 0) continue; 373 LinkSymId canon = symhash_get(&img->globals, tgt->name); 374 if (canon == LINK_SYM_NONE || canon >= x->sym_to_imp_size) continue; 375 idx = x->sym_to_imp[canon]; 376 if (!idx) continue; 377 /* Stash so future lookups skip this loop. */ 378 x->sym_to_imp[r->target] = idx; 379 } 380 x->imports[idx - 1].is_func = 1; 381 } 382 383 /* Build dylib ordinal table. Pull soname from the providing DSO. */ 384 for (u32 i = 0; i < x->nimports; ++i) { 385 MachImp* mi = &x->imports[i]; 386 LinkSymbol* s = sym_at(img, mi->sym); 387 LinkInputId dso_id = s ? s->dso_input_id : LINK_INPUT_NONE; 388 Sym install = 0; 389 if (dso_id != LINK_INPUT_NONE && x->linker && 390 dso_id - 1u < LinkInputs_count(&x->linker->inputs)) { 391 LinkInput* in = LinkInputs_at(&x->linker->inputs, dso_id - 1u); 392 if (in->kind == LINK_INPUT_DSO_BYTES) install = in->soname; 393 } 394 if (install == 0) 395 install = pool_intern_slice(x->c->global, 396 SLICE_LIT("/usr/lib/libSystem.B.dylib")); 397 u32 ord = dylib_ordinal_of(x, install); 398 if (!ord) { 399 if (VEC_GROW(h, x->dylibs, cap_d, x->ndylibs + 1u)) 400 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on dylibs"); 401 x->dylibs[x->ndylibs].install = install; 402 ++x->ndylibs; 403 ord = x->ndylibs; 404 } 405 mi->dylib_ord = ord; 406 } 407 408 /* Always include every DSO input's install-name. */ 409 if (x->linker) { 410 for (u32 ii = 0; ii < LinkInputs_count(&x->linker->inputs); ++ii) { 411 LinkInput* in = LinkInputs_at(&x->linker->inputs, ii); 412 if (in->kind != LINK_INPUT_DSO_BYTES) continue; 413 if (in->soname == 0) continue; 414 if (dylib_ordinal_of(x, in->soname)) continue; 415 if (VEC_GROW(h, x->dylibs, cap_d, x->ndylibs + 1u)) 416 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on dylibs"); 417 x->dylibs[x->ndylibs].install = in->soname; 418 ++x->ndylibs; 419 } 420 } 421 422 /* All entries so far are real imports; remember the partition point 423 * so import/symtab table emit loops can skip the appended internals. */ 424 x->nimports_real = x->nimports; 425 426 /* Internal GOT pass. clang on Mach-O routes every extern-global 427 * reference through the GOT (GOT_LOAD_PAGE21 / LD64_GOT_LO12_NC), so 428 * even a common symbol or weak-undef that ends up resolved within the 429 * image still needs a __got slot. For each such reloc whose target 430 * isn't an existing import, materialize a MachImp with internal=1. 431 * The slot's contents are filled at write time and a chained-fixup 432 * REBASE entry (or none, for weak undef → NULL) keeps it valid 433 * post-ASLR. */ 434 for (u32 i = 0; i < LinkRelocs_count(&img->relocs); ++i) { 435 LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 436 if (!reloc_kind_is_got_load(x->c, r->kind)) continue; 437 if (r->target == LINK_SYM_NONE || r->target >= x->sym_to_imp_size) continue; 438 if (x->sym_to_imp[r->target]) continue; 439 LinkSymbol* t = sym_at(img, r->target); 440 if (!t) continue; 441 /* Resolve through canonical so we share a single slot per symbol. */ 442 LinkSymId canon = r->target; 443 if (t->name != 0) { 444 LinkSymId hit = symhash_get(&img->globals, t->name); 445 if (hit != LINK_SYM_NONE) { 446 canon = hit; 447 if (x->sym_to_imp[canon]) { 448 x->sym_to_imp[r->target] = x->sym_to_imp[canon]; 449 continue; 450 } 451 t = sym_at(img, canon); 452 if (!t) continue; 453 } 454 } 455 if (VEC_GROW(h, x->imports, cap, x->nimports + 1u)) 456 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on internal got"); 457 MachImp* mi = &x->imports[x->nimports++]; 458 memset(mi, 0, sizeof(*mi)); 459 mi->sym = canon; 460 mi->name = t->name; 461 mi->is_func = (t->kind == SK_FUNC || t->kind == SK_IFUNC) ? 1 : 0; 462 mi->weak = (t->bind == SB_WEAK) ? 1 : 0; 463 mi->internal = 1; 464 x->sym_to_imp[canon] = x->nimports; 465 if (canon != r->target) x->sym_to_imp[r->target] = x->nimports; 466 } 467 468 /* Assign stub_idx + got_idx. Internal entries get a slot but no stub: 469 * the call site (CALL26) on internal funcs goes direct, not via stub. */ 470 u32 stub_run = 0; 471 for (u32 i = 0; i < x->nimports; ++i) { 472 MachImp* mi = &x->imports[i]; 473 mi->got_idx = i + 1u; 474 if (mi->is_func && !mi->internal) mi->stub_idx = ++stub_run; 475 } 476 x->nimport_funcs = stub_run; 477 } 478 479 /* ---- pass: collect TLV pointer slots ---- 480 * 481 * Mirror of collect_imports' internal-GOT pass, but for TLV descriptors: 482 * each unique descriptor referenced via ARM64_RELOC_TLVP_LOAD_PAGE21 / 483 * PAGEOFF12 gets one slot in the synthetic __DATA,__thread_ptrs section. 484 * The slot's runtime value is the descriptor's address; we patch it at 485 * apply_relocs time (REBASE for in-image descriptors, BIND for ones in 486 * a dylib). 487 * 488 * Slots are deduplicated by canonical LinkSymId so a single descriptor 489 * referenced from N call sites shares one __thread_ptrs entry. */ 490 static void collect_tlv(MCtx* x) { 491 LinkImage* img = x->img; 492 Heap* h = x->h; 493 x->sym_to_tlv_size = LinkSyms_count(&img->syms) + 1u; 494 x->sym_to_tlv = 495 (u32*)h->alloc(h, sizeof(u32) * x->sym_to_tlv_size, _Alignof(u32)); 496 if (!x->sym_to_tlv) 497 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on sym_to_tlv"); 498 memset(x->sym_to_tlv, 0, sizeof(u32) * x->sym_to_tlv_size); 499 500 u32 cap = 0; 501 for (u32 i = 0; i < LinkRelocs_count(&img->relocs); ++i) { 502 LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 503 if (!reloc_kind_is_tlvp(x->c, r->kind)) continue; 504 if (r->target == LINK_SYM_NONE || r->target >= x->sym_to_tlv_size) continue; 505 /* Resolve through canonical so multiple per-input duplicate undefs 506 * collapse onto one __thread_ptrs slot. */ 507 LinkSymId canon = r->target; 508 LinkSymbol* t = sym_at(img, r->target); 509 if (!t) continue; 510 if (t->name != 0) { 511 LinkSymId hit = symhash_get(&img->globals, t->name); 512 if (hit != LINK_SYM_NONE) { 513 canon = hit; 514 t = sym_at(img, canon); 515 if (!t) continue; 516 } 517 } 518 if (x->sym_to_tlv[canon]) { 519 if (canon != r->target) x->sym_to_tlv[r->target] = x->sym_to_tlv[canon]; 520 continue; 521 } 522 if (VEC_GROW(h, x->tlv_slots, cap, x->ntlv + 1u)) 523 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on tlv_slots"); 524 MachTlv* ts = &x->tlv_slots[x->ntlv++]; 525 memset(ts, 0, sizeof(*ts)); 526 ts->sym = canon; 527 ts->tlv_idx = x->ntlv; 528 ts->imported = t->imported ? 1u : 0u; 529 /* If the descriptor is imported we route the bind through the 530 * symbol's MachImp slot — that's where dyld's chained-import index 531 * comes from. When this loop fires the imp pass has already 532 * materialized the entry (real imports were processed first); the 533 * lookup may also have stashed an alias for non-canonical ids. */ 534 if (ts->imported) { 535 u32 idx = (canon < x->sym_to_imp_size) ? x->sym_to_imp[canon] : 0u; 536 if (!idx && t->name != 0) { 537 LinkSymId hit2 = symhash_get(&img->globals, t->name); 538 if (hit2 != LINK_SYM_NONE && hit2 < x->sym_to_imp_size) 539 idx = x->sym_to_imp[hit2]; 540 } 541 ts->import_idx = idx; 542 } 543 x->sym_to_tlv[canon] = x->ntlv; 544 if (canon != r->target) x->sym_to_tlv[r->target] = x->ntlv; 545 } 546 } 547 548 /* ---- pass: plan Mach-O sections ---- 549 * 550 * Walks LinkImage sections. Each non-zero-size LinkSection becomes one 551 * MSec. Synthetic __stubs and __got are appended at the right segment 552 * boundaries. Vaddr and file_offset are assigned in a single forward 553 * pass starting at __TEXT base; __PAGEZERO and __LINKEDIT are special. */ 554 555 static void seg_init(MSeg* s, const char* name, u32 maxp, u32 initp) { 556 memset(s, 0, sizeof(*s)); 557 s->name = name; 558 s->maxprot = maxp; 559 s->initprot = initp; 560 } 561 562 static int sec_is_writable(const LinkSection* ls) { 563 return (ls->flags & SF_WRITE) != 0u; 564 } 565 static int sec_is_exec(const LinkSection* ls) { 566 return (ls->flags & SF_EXEC) != 0u; 567 } 568 static int sec_is_zerofill(const LinkSection* ls) { 569 return ls->sem == SSEM_NOBITS; 570 } 571 572 static int section_has_abs64_reloc(const LinkImage* img, LinkSectionId id) { 573 for (u32 i = 0; i < LinkRelocs_count(&img->relocs); ++i) { 574 const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 575 if (r->link_section_id == id && r->kind == R_ABS64) return 1; 576 } 577 return 0; 578 } 579 580 static int sec_needs_data_const(const LinkImage* img, const LinkSection* ls) { 581 if (!ls || !ls->size || sec_is_exec(ls) || sec_is_writable(ls) || 582 sec_is_zerofill(ls)) { 583 return 0; 584 } 585 return section_has_abs64_reloc(img, ls->id); 586 } 587 588 /* Pick (segname, sectname) for a LinkSection. Comma-form Mach-O names 589 * round-trip into MSec's inline 16-byte buffers; literal defaults point 590 * at .rodata strings. Caller passes the MSec for per-section storage — 591 * a previous version used a shared static buffer which aliased all 592 * sections to whichever name was set last. */ 593 static void pick_macho_names(const LinkSection* ls, Compiler* c, MSec* m) { 594 Slice nm_s = pool_slice(c->global, ls->name); 595 const char* nm = nm_s.s; 596 size_t nlen = nm_s.len; 597 if (nm) { 598 /* Comma-form: "__SEG,__sect" round-tripped from a Mach-O input. */ 599 for (size_t i = 0; i < nlen; ++i) { 600 if (nm[i] == ',') { 601 u32 seg_n = (u32)(i > 16 ? 16 : i); 602 memcpy(m->segname_buf, nm, seg_n); 603 m->segname_buf[seg_n] = 0; 604 u32 sect_n = (u32)((nlen - i - 1) > 16 ? 16 : (nlen - i - 1)); 605 memcpy(m->sectname_buf, nm + i + 1, sect_n); 606 m->sectname_buf[sect_n] = 0; 607 m->segname = m->segname_buf; 608 m->sectname = m->sectname_buf; 609 return; 610 } 611 } 612 } 613 /* Derive from flags. */ 614 if (sec_is_exec(ls)) { 615 m->segname = "__TEXT"; 616 m->sectname = "__text"; 617 } else if (sec_is_writable(ls)) { 618 m->segname = "__DATA"; 619 m->sectname = sec_is_zerofill(ls) ? "__bss" : "__data"; 620 } else { 621 m->segname = "__TEXT"; 622 m->sectname = "__const"; 623 } 624 } 625 626 static void plan_layout(MCtx* x) { 627 LinkImage* img = x->img; 628 Heap* h = x->h; 629 630 /* PAGEZERO */ 631 seg_init(&x->segs[0], "__PAGEZERO", 0, 0); 632 x->segs[0].vmaddr = 0; 633 x->segs[0].vmsize = MZ_PAGEZERO; 634 x->segs[0].fileoff = 0; 635 x->segs[0].filesize = 0; 636 x->segs[0].nsects = 0; 637 x->segs[0].first_sec = 0; 638 639 /* Segments 1..4 */ 640 seg_init(&x->segs[1], "__TEXT", VM_PROT_READ | VM_PROT_EXECUTE, 641 VM_PROT_READ | VM_PROT_EXECUTE); 642 seg_init(&x->segs[2], "__DATA_CONST", VM_PROT_READ | VM_PROT_WRITE, 643 VM_PROT_READ | VM_PROT_WRITE); 644 seg_init(&x->segs[3], "__DATA", VM_PROT_READ | VM_PROT_WRITE, 645 VM_PROT_READ | VM_PROT_WRITE); 646 /* __DWARF holds the file-only .debug_* sections; mapped R but never 647 * referenced at runtime. Empty (nsects 0) when there's no debug info. */ 648 seg_init(&x->segs[MSEG_DWARF], "__DWARF", VM_PROT_READ, VM_PROT_READ); 649 seg_init(&x->segs[MSEG_LINKEDIT], "__LINKEDIT", VM_PROT_READ, VM_PROT_READ); 650 x->nsegs = MSEG_COUNT; 651 652 /* Pre-allocate MSec capacity: every LinkSection + 2 synth (__stubs, 653 * __got). (LinkSections from the dynamic-link layer — .dynsym / .plt 654 * etc. — were synthesized by layout_dyn for ELF; we won't have them 655 * since pie wasn't set on this Linker. Still, oversize by a few.) */ 656 u32 cap = LinkRelocs_count(&img->relocs) + img->nsections + 4u; 657 x->secs = (MSec*)h->alloc(h, sizeof(MSec) * cap, _Alignof(MSec)); 658 if (!x->secs) compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on MSec"); 659 memset(x->secs, 0, sizeof(MSec) * cap); 660 x->nsecs = 0; 661 662 /* Pass 1: __TEXT segment. Header + loadcmds reserve front. */ 663 /* We need the exact header_size to set first sec's file_offset. We'll 664 * compute it later, but reserve a placeholder; for now use 0 and patch 665 * in pass 4 (offsets get bumped). */ 666 667 u64 text_vaddr = MZ_PAGEZERO; 668 /* We'll compute headers_size after plan; stash starting vaddr only. */ 669 x->segs[1].vmaddr = text_vaddr; 670 x->segs[1].fileoff = 0; 671 x->text_vaddr = text_vaddr; 672 673 /* Collect: (a) exec sections, (b) read-only allocatable sections. */ 674 /* (cursor advances per-segment in pass 2; nothing to track here) */ 675 676 /* We don't know the header size yet; walk sections first to enumerate 677 * MSec entries, then back-fill file_offset/vaddr after we know the 678 * load-command count. */ 679 680 u32 first_text_sec = x->nsecs; 681 682 for (u32 i = 0; i < img->nsections; ++i) { 683 LinkSection* ls = &img->sections[i]; 684 if (!ls->size) continue; 685 if (ls->file_only) continue; /* .debug_* → __DWARF segment below */ 686 if (sec_is_writable(ls)) continue; 687 if (sec_is_zerofill(ls)) continue; /* placed in __DATA */ 688 if (sec_needs_data_const(img, ls)) continue; 689 MSec* m = &x->secs[x->nsecs++]; 690 memset(m, 0, sizeof(*m)); 691 m->link_sec_id = ls->id; 692 pick_macho_names(ls, x->c, m); 693 /* Force into __TEXT. */ 694 if (!slice_eq_cstr(slice_from_cstr(m->segname), "__TEXT")) 695 m->segname = "__TEXT"; 696 m->align = ls->align ? ls->align : 1u; 697 m->size = ls->size; 698 m->segidx = 1; 699 m->flags = sec_is_exec(ls) ? (0x80000000u /*S_ATTR_PURE_INSTRUCTIONS*/ | 700 0x00000400u /*S_ATTR_SOME_INSTRUCTIONS*/) 701 : 0u; 702 } 703 704 /* __stubs synthetic */ 705 if (x->nimport_funcs) { 706 x->stubs_size = x->nimport_funcs * x->macho->stub_size; 707 x->stubs_bytes = (u8*)h->alloc(h, x->stubs_size, 4); 708 if (!x->stubs_bytes) 709 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on stubs"); 710 memset(x->stubs_bytes, 0, x->stubs_size); 711 MSec* m = &x->secs[x->nsecs++]; 712 memset(m, 0, sizeof(*m)); 713 m->synth_data = x->stubs_bytes; 714 m->synth_size = x->stubs_size; 715 m->segname = "__TEXT"; 716 m->sectname = "__stubs"; 717 m->align = 4u; 718 m->size = x->stubs_size; 719 m->segidx = 1; 720 m->flags = 0x80000000u | 0x00000400u | 0x00000008u /*S_SYMBOL_STUBS*/; 721 m->reserved1 = 0; /* fill in later: indirect-symtab base */ 722 m->reserved2 = x->macho->stub_size; 723 } 724 x->segs[1].nsects = x->nsecs - first_text_sec; 725 x->segs[1].first_sec = first_text_sec; 726 727 /* __DATA_CONST: __got synth */ 728 u32 first_dc = x->nsecs; 729 if (x->nimports) { 730 x->got_size = x->nimports * MZ_GOT_SIZE; 731 x->got_bytes = (u8*)h->alloc(h, x->got_size, 8); 732 if (!x->got_bytes) 733 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on got"); 734 memset(x->got_bytes, 0, x->got_size); 735 MSec* m = &x->secs[x->nsecs++]; 736 memset(m, 0, sizeof(*m)); 737 m->synth_data = x->got_bytes; 738 m->synth_size = x->got_size; 739 m->segname = "__DATA_CONST"; 740 m->sectname = "__got"; 741 m->align = 8u; 742 m->size = x->got_size; 743 m->segidx = 2; 744 m->flags = 0x00000006u /*S_NON_LAZY_SYMBOL_POINTERS*/; 745 m->reserved1 = 0; /* indirect-symtab base */ 746 } 747 for (u32 i = 0; i < img->nsections; ++i) { 748 LinkSection* ls = &img->sections[i]; 749 if (ls->file_only) continue; /* .debug_* → __DWARF (has ABS64 relocs) */ 750 if (!sec_needs_data_const(img, ls)) continue; 751 MSec* m = &x->secs[x->nsecs++]; 752 memset(m, 0, sizeof(*m)); 753 m->link_sec_id = ls->id; 754 pick_macho_names(ls, x->c, m); 755 m->segname = "__DATA_CONST"; 756 m->align = ls->align ? ls->align : 1u; 757 m->size = ls->size; 758 m->segidx = 2; 759 m->flags = 0; 760 } 761 x->segs[2].nsects = x->nsecs - first_dc; 762 x->segs[2].first_sec = first_dc; 763 764 /* __DATA segment: writable sections + zerofill. */ 765 u32 first_d = x->nsecs; 766 for (u32 i = 0; i < img->nsections; ++i) { 767 LinkSection* ls = &img->sections[i]; 768 if (ls->file_only) continue; /* .debug_* → __DWARF */ 769 if (!ls->size && !sec_is_zerofill(ls)) continue; 770 if (!sec_is_writable(ls)) continue; 771 MSec* m = &x->secs[x->nsecs++]; 772 memset(m, 0, sizeof(*m)); 773 m->link_sec_id = ls->id; 774 pick_macho_names(ls, x->c, m); 775 if (!slice_eq_cstr(slice_from_cstr(m->segname), "__DATA")) 776 m->segname = "__DATA"; 777 m->align = ls->align ? ls->align : 1u; 778 m->size = ls->size; 779 m->segidx = 3; 780 m->is_zerofill = sec_is_zerofill(ls) ? 1 : 0; 781 m->flags = m->is_zerofill ? 0x00000001u /*S_ZEROFILL*/ : 0; 782 /* dyld dispatches on the section type byte (low 8 bits of flags). 783 * __mod_init_func / __mod_term_func sections must carry the 784 * S_MOD_INIT_FUNC_POINTERS / S_MOD_TERM_FUNC_POINTERS type or dyld 785 * skips them entirely — leaving constructors unrun at startup. */ 786 if (slice_eq_cstr(slice_from_cstr(m->sectname), "__mod_init_func")) 787 m->flags = 0x00000009u /*S_MOD_INIT_FUNC_POINTERS*/; 788 else if (slice_eq_cstr(slice_from_cstr(m->sectname), "__mod_term_func")) 789 m->flags = 0x0000000au /*S_MOD_TERM_FUNC_POINTERS*/; 790 else if (ls->flags & SF_TLS) { 791 /* TLV sections: dyld dispatches by section type, not name. Map 792 * __thread_vars → S_THREAD_LOCAL_VARIABLES (descriptor records), 793 * __thread_data → S_THREAD_LOCAL_REGULAR (initial data), 794 * __thread_bss → S_THREAD_LOCAL_ZEROFILL (zero-init data). Done 795 * by sectname so per-TU inputs without a Mach-O ext_type still 796 * get the right section type. */ 797 if (slice_eq_cstr(slice_from_cstr(m->sectname), "__thread_vars")) { 798 m->flags = S_THREAD_LOCAL_VARIABLES; 799 /* Each descriptor is three pointers (24B) whose first word is 800 * dyld's _tlv_bootstrap thunk pointer. Clang/llvm emit 801 * __thread_vars with on-disk alignment 1 (relying on layout to 802 * land it on 8); force 8-alignment here so the descriptor 803 * pointers fall on 8-byte boundaries — dyld's chained-fixup 804 * processing assumes that. */ 805 if (m->align < 8u) m->align = 8u; 806 } else if (m->is_zerofill) 807 m->flags = S_THREAD_LOCAL_ZEROFILL; 808 else 809 m->flags = S_THREAD_LOCAL_REGULAR; 810 } 811 } 812 /* __thread_ptrs synthetic (TLV pointer slots). Emitted into __DATA 813 * after the user's TLV input sections so descriptors and their 814 * pointers share the same segment. Each slot's runtime initial 815 * value (= TLV descriptor address) is patched during apply_relocs. */ 816 if (x->ntlv) { 817 x->tlv_ptrs_size = x->ntlv * MZ_TLVP_SIZE; 818 x->tlv_ptrs_bytes = (u8*)h->alloc(h, x->tlv_ptrs_size, 8); 819 if (!x->tlv_ptrs_bytes) 820 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on tlv_ptrs"); 821 memset(x->tlv_ptrs_bytes, 0, x->tlv_ptrs_size); 822 MSec* m = &x->secs[x->nsecs++]; 823 memset(m, 0, sizeof(*m)); 824 m->synth_data = x->tlv_ptrs_bytes; 825 m->synth_size = x->tlv_ptrs_size; 826 m->segname = "__DATA"; 827 m->sectname = "__thread_ptrs"; 828 m->align = 8u; 829 m->size = x->tlv_ptrs_size; 830 m->segidx = 3; 831 m->flags = S_THREAD_LOCAL_VARIABLE_POINTERS; 832 } 833 x->segs[3].nsects = x->nsecs - first_d; 834 x->segs[3].first_sec = first_d; 835 836 /* __DWARF: file-only .debug_* sections (debug-info retention). Each 837 * contribution becomes a synth MSec whose bytes are the per-image 838 * debug registry buffer (relocs applied in place at apply_relocs). 839 * Iterated in registry order (= input order) so same-name runs land 840 * adjacent and the per-input DWARF-relative bases line up with the 841 * coalesced section's byte layout. */ 842 u32 first_dw = x->nsecs; 843 for (u32 i = 0; i < img->nsections; ++i) { 844 LinkSection* ls = &img->sections[i]; 845 u8* dbg; 846 if (!ls->file_only || !ls->size) continue; 847 dbg = link_fileonly_bytes(img, ls->id); 848 if (!dbg) continue; 849 MSec* m = &x->secs[x->nsecs++]; 850 memset(m, 0, sizeof(*m)); 851 m->synth_data = dbg; 852 m->synth_size = (u32)ls->size; 853 /* Section name: a Mach-O input already carries "__DWARF,__debug_*"; 854 * an in-process .debug_* maps via obj_macho_debug_sectname. */ 855 { 856 Slice nm = pool_slice(x->c->global, ls->name); 857 const char* comma = nm.s ? memchr(nm.s, ',', nm.len) : NULL; 858 char sect[17]; 859 if (comma) { 860 u32 sgn = (u32)(comma - nm.s); 861 if (sgn > 16u) sgn = 16u; 862 memcpy(m->segname_buf, nm.s, sgn); 863 m->segname_buf[sgn] = 0; 864 u32 stn = (u32)(nm.len - (comma - nm.s) - 1); 865 if (stn > 16u) stn = 16u; 866 memcpy(m->sectname_buf, comma + 1, stn); 867 m->sectname_buf[stn] = 0; 868 } else if (obj_macho_debug_sectname(nm.s, nm.len, sect)) { 869 memcpy(m->segname_buf, "__DWARF", 8); 870 memcpy(m->sectname_buf, sect, slice_from_cstr(sect).len + 1); 871 } else { 872 memcpy(m->segname_buf, "__DWARF", 8); 873 u32 stn = nm.len > 16u ? 16u : (u32)nm.len; 874 memcpy(m->sectname_buf, nm.s, stn); 875 m->sectname_buf[stn] = 0; 876 } 877 m->segname = m->segname_buf; 878 m->sectname = m->sectname_buf; 879 } 880 /* align 1: contributions concatenate gap-free so the DWARF-relative 881 * bases (assigned without padding in link_layout_debug) stay valid. */ 882 m->align = 1u; 883 m->size = ls->size; 884 m->segidx = MSEG_DWARF; 885 m->flags = 0; /* S_REGULAR */ 886 } 887 x->segs[MSEG_DWARF].nsects = x->nsecs - first_dw; 888 x->segs[MSEG_DWARF].first_sec = first_dw; 889 890 /* Group MSecs by (segname, sectname) within each segment so vaddr 891 * placement keeps same-named runs contiguous. Otherwise Phase B's 892 * adjacency-based coalescing splits a single Mach-O section into 893 * multiple OutSecs (e.g. `.text` from an in-memory ObjBuilder and 894 * `__TEXT,__text` from a Mach-O .o input both map to `__TEXT,__text` 895 * but arrive in separate link_layout groups, interleaved with other 896 * sections from each input). Stable insertion sort preserves input 897 * order within a name, which matters for synth __stubs/__thread_ptrs 898 * order relative to peers. */ 899 for (u32 i = 0; i < x->nsegs; ++i) { 900 MSeg* sg = &x->segs[i]; 901 if (sg->nsects < 2) continue; 902 u32 base = sg->first_sec; 903 u32 n = sg->nsects; 904 for (u32 a = 1; a < n; ++a) { 905 MSec key = x->secs[base + a]; 906 msec_repair_name_ptrs(&key); 907 u32 j = a; 908 while (j > 0) { 909 MSec* prev = &x->secs[base + j - 1]; 910 /* Ordering compare for stable sort: slices don't order, keep strcmp. */ 911 int cmp = strcmp(prev->segname, key.segname); /* ordering */ 912 if (cmp == 0) cmp = strcmp(prev->sectname, key.sectname); /* ordering */ 913 if (cmp <= 0) break; 914 x->secs[base + j] = x->secs[base + j - 1]; 915 msec_repair_name_ptrs(&x->secs[base + j]); 916 --j; 917 } 918 x->secs[base + j] = key; 919 msec_repair_name_ptrs(&x->secs[base + j]); 920 } 921 } 922 923 /* Phase A: count OutSecs per segment (distinct sectnames) so we can 924 * size the load commands before placing vaddrs. Phase B builds the 925 * actual OutSec[] after placement, when vaddrs are final. */ 926 for (u32 i = 0; i < x->nsegs; ++i) { 927 MSeg* sg = &x->segs[i]; 928 u32 cnt = 0; 929 for (u32 a = sg->first_sec; a < sg->first_sec + sg->nsects; ++a) { 930 int seen = 0; 931 for (u32 b = sg->first_sec; b < a; ++b) { 932 if (slice_eq_cstr(slice_from_cstr(x->secs[a].sectname), 933 x->secs[b].sectname) && 934 slice_eq_cstr(slice_from_cstr(x->secs[a].segname), 935 x->secs[b].segname)) { 936 seen = 1; 937 break; 938 } 939 } 940 if (!seen) ++cnt; 941 } 942 sg->nouts = cnt; 943 sg->first_out = 0; /* assigned in Phase B */ 944 } 945 946 /* Compute load-command count + sizeofcmds, then back-fill section 947 * offsets. Layout pass 2. */ 948 u32 nseg_real = 0; 949 for (u32 i = 0; i < x->nsegs; ++i) { 950 /* Skip __DATA_CONST or __DATA if no sections (edge case). */ 951 if (i == 0) { 952 ++nseg_real; 953 continue; 954 } /* PAGEZERO */ 955 if (i == MSEG_LINKEDIT) { 956 ++nseg_real; 957 continue; 958 } /* LINKEDIT always */ 959 if (x->segs[i].nsects > 0) ++nseg_real; /* incl. __DWARF when present */ 960 } 961 /* Each LC_SEGMENT_64 carries 72 + 80*nouts bytes (one section_64 962 * record per coalesced (segname,sectname), not per MSec). */ 963 u32 sizeofcmds = 0; 964 for (u32 i = 0; i < x->nsegs; ++i) { 965 if (i == 0 || i == MSEG_LINKEDIT) { 966 sizeofcmds += MACHO_SEGCMD64_SIZE; /* no sections */ 967 continue; 968 } 969 if (x->segs[i].nsects == 0) continue; 970 sizeofcmds += MACHO_SEGCMD64_SIZE + x->segs[i].nouts * MACHO_SECT64_SIZE; 971 } 972 (void)nseg_real; 973 /* LC_DYLD_CHAINED_FIXUPS / LC_DYLD_EXPORTS_TRIE */ 974 sizeofcmds += 16u + 16u; 975 /* LC_SYMTAB / LC_DYSYMTAB */ 976 sizeofcmds += MACHO_SYMTAB_CMD_SIZE + MACHO_DYSYMTAB_CMD_SIZE; 977 /* LC_LOAD_DYLINKER */ 978 { 979 u32 ld_size = 12u + (u32)(sizeof("/usr/lib/dyld") - 1u) + 1u; 980 sizeofcmds += (u32)ALIGN_UP((u64)ld_size, 8u); 981 } 982 /* LC_UUID + LC_BUILD_VERSION + LC_MAIN */ 983 sizeofcmds += 24u + 24u + 24u; 984 /* LC_LOAD_DYLIB per dylib */ 985 for (u32 i = 0; i < x->ndylibs; ++i) { 986 size_t nl = pool_slice(x->c->global, x->dylibs[i].install).len; 987 u32 sz = 24u + (u32)nl + 1u; 988 sizeofcmds += (u32)ALIGN_UP((u64)sz, 8u); 989 } 990 /* LC_FUNCTION_STARTS / LC_DATA_IN_CODE / LC_CODE_SIGNATURE */ 991 sizeofcmds += 16u + 16u + 16u; 992 993 x->headers_size = MACHO_HDR64_SIZE + sizeofcmds; 994 995 /* Now place sections in __TEXT, __DATA_CONST, __DATA. */ 996 u64 vaddr = MZ_PAGEZERO + x->headers_size; 997 u64 fileoff = x->headers_size; 998 /* Pad __TEXT sections to natural alignment. */ 999 for (u32 i = 0; i < x->nsegs; ++i) { 1000 if (i == 0 || i == MSEG_LINKEDIT) continue; /* DWARF placed here too */ 1001 MSeg* sg = &x->segs[i]; 1002 if (i > 1) { 1003 /* page-align the start of __DATA_CONST and __DATA */ 1004 vaddr = ALIGN_UP(vaddr, MZ_PAGE); 1005 fileoff = ALIGN_UP(fileoff, MZ_PAGE); 1006 } 1007 sg->vmaddr = (i == 1) ? MZ_PAGEZERO : vaddr; 1008 sg->fileoff = (i == 1) ? 0 : fileoff; 1009 /* __TEXT carries the headers_size + sections. */ 1010 u64 seg_start_v = sg->vmaddr; 1011 u64 seg_start_f = sg->fileoff; 1012 /* For __TEXT, sections begin after the header area. */ 1013 u64 cur_v = (i == 1) ? (seg_start_v + x->headers_size) : seg_start_v; 1014 u64 cur_f = (i == 1) ? (seg_start_f + x->headers_size) : seg_start_f; 1015 u64 first_zerofill_v = 0; 1016 int seen_zerofill = 0; 1017 /* Non-zerofill first */ 1018 for (u32 j = 0; j < sg->nsects; ++j) { 1019 MSec* m = &x->secs[sg->first_sec + j]; 1020 if (m->is_zerofill) continue; 1021 cur_v = ALIGN_UP(cur_v, (u64)m->align); 1022 cur_f = ALIGN_UP(cur_f, (u64)m->align); 1023 m->vaddr = cur_v; 1024 m->file_offset = cur_f; 1025 cur_v += m->size; 1026 cur_f += m->size; 1027 } 1028 first_zerofill_v = cur_v; 1029 /* zerofill last (no file bytes) */ 1030 for (u32 j = 0; j < sg->nsects; ++j) { 1031 MSec* m = &x->secs[sg->first_sec + j]; 1032 if (!m->is_zerofill) continue; 1033 cur_v = ALIGN_UP(cur_v, (u64)m->align); 1034 m->vaddr = cur_v; 1035 m->file_offset = 0; 1036 cur_v += m->size; 1037 seen_zerofill = 1; 1038 } 1039 sg->filesize = (i == 1) 1040 ? (cur_f - seg_start_f) 1041 : (first_zerofill_v ? (first_zerofill_v - seg_start_v) 1042 : (cur_v - seg_start_v)); 1043 sg->vmsize = ALIGN_UP(cur_v - seg_start_v, MZ_PAGE); 1044 if (sg->vmsize == 0 && sg->nsects > 0) sg->vmsize = MZ_PAGE; 1045 if (i == 1) { 1046 x->stubs_vaddr = 0; 1047 for (u32 j = 0; j < sg->nsects; ++j) { 1048 MSec* m = &x->secs[sg->first_sec + j]; 1049 if (slice_eq_cstr(slice_from_cstr(m->sectname), "__stubs")) 1050 x->stubs_vaddr = m->vaddr; 1051 } 1052 } 1053 if (i == 2) { 1054 for (u32 j = 0; j < sg->nsects; ++j) { 1055 MSec* m = &x->secs[sg->first_sec + j]; 1056 if (slice_eq_cstr(slice_from_cstr(m->sectname), "__got")) 1057 x->got_vaddr = m->vaddr; 1058 } 1059 } 1060 if (i == 3) { 1061 for (u32 j = 0; j < sg->nsects; ++j) { 1062 MSec* m = &x->secs[sg->first_sec + j]; 1063 if (slice_eq_cstr(slice_from_cstr(m->sectname), "__thread_ptrs")) 1064 x->tlv_ptrs_vaddr = m->vaddr; 1065 /* TLS storage image base: min vaddr across __thread_data and 1066 * __thread_bss sections. __thread_vars is excluded — it holds 1067 * the descriptors, not the data that maps into the per-thread 1068 * block. */ 1069 if ((slice_eq_cstr(slice_from_cstr(m->sectname), "__thread_data") || 1070 slice_eq_cstr(slice_from_cstr(m->sectname), "__thread_bss")) && 1071 (!x->has_tls_image || m->vaddr < x->tls_image_vaddr)) { 1072 x->tls_image_vaddr = m->vaddr; 1073 x->has_tls_image = 1; 1074 } 1075 } 1076 } 1077 vaddr = sg->vmaddr + sg->vmsize; 1078 /* Mach-O segments are mapped in page units. If a segment's memory 1079 * image extends past its initialized file bytes (for example 1080 * __DATA,__bss), the following segment's fileoff must not reuse those 1081 * pages or the kernel can map later file contents into the zero-fill 1082 * tail. */ 1083 fileoff = sg->fileoff + ((sg->vmsize > ALIGN_UP(sg->filesize, MZ_PAGE)) 1084 ? sg->vmsize 1085 : sg->filesize); 1086 (void)seen_zerofill; 1087 } 1088 /* LINKEDIT placeholder; size is filled after blob assembly. */ 1089 vaddr = ALIGN_UP(vaddr, MZ_PAGE); 1090 fileoff = ALIGN_UP(fileoff, MZ_PAGE); 1091 x->segs[MSEG_LINKEDIT].vmaddr = vaddr; 1092 x->segs[MSEG_LINKEDIT].fileoff = fileoff; 1093 x->linkedit_vaddr = vaddr; 1094 x->linkedit_fileoff = fileoff; 1095 1096 /* Encode __stubs bytes now that vaddrs are settled. Internal-GOT 1097 * entries have stub_idx=0 (direct CALL26, no stub) and must be 1098 * skipped so the (stub_idx - 1u) arithmetic doesn't wrap. */ 1099 for (u32 i = 0; i < x->nimports; ++i) { 1100 MachImp* mi = &x->imports[i]; 1101 if (!mi->is_func || !mi->stub_idx) continue; 1102 u64 stub_v = x->stubs_vaddr + (mi->stub_idx - 1u) * x->macho->stub_size; 1103 u64 got_v = x->got_vaddr + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1104 x->macho->emit_stub( 1105 x->stubs_bytes + (mi->stub_idx - 1u) * x->macho->stub_size, stub_v, 1106 got_v); 1107 } 1108 1109 /* Phase B: build OutSec[] now that all MSec vaddrs are final. Walk 1110 * MSecs sorted by (segidx, vaddr) and coalesce adjacent same-name 1111 * runs. Mirrors link_elf.c's OutShdr build at link_elf.c:879. */ 1112 { 1113 u32* order = 1114 (u32*)h->alloc(h, sizeof(u32) * (x->nsecs + 1u), _Alignof(u32)); 1115 if (!order && x->nsecs) 1116 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on outsec sort"); 1117 for (u32 i = 0; i < x->nsecs; ++i) order[i] = i; 1118 /* Insertion sort — section count is small. */ 1119 for (u32 i = 1; i < x->nsecs; ++i) { 1120 u32 cur = order[i]; 1121 MSec* a = &x->secs[cur]; 1122 u32 j = i; 1123 while (j > 0) { 1124 MSec* b = &x->secs[order[j - 1]]; 1125 if ((b->segidx < a->segidx) || 1126 (b->segidx == a->segidx && b->vaddr <= a->vaddr)) 1127 break; 1128 order[j] = order[j - 1]; 1129 --j; 1130 } 1131 order[j] = cur; 1132 } 1133 u32 cap = x->nsecs + 1u; 1134 x->outs = (OutSec*)h->alloc(h, sizeof(OutSec) * cap, _Alignof(OutSec)); 1135 if (!x->outs) 1136 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on OutSec"); 1137 memset(x->outs, 0, sizeof(OutSec) * cap); 1138 x->nouts = 0; 1139 for (u32 i = 0; i < x->nsecs; ++i) { 1140 MSec* m = &x->secs[order[i]]; 1141 OutSec* tail = x->nouts ? &x->outs[x->nouts - 1] : NULL; 1142 int merge = tail && tail->segidx == m->segidx && 1143 slice_eq_cstr(slice_from_cstr(tail->sectname), m->sectname) && 1144 slice_eq_cstr(slice_from_cstr(tail->segname), m->segname); 1145 if (merge) { 1146 if (tail->flags != m->flags || tail->is_zerofill != m->is_zerofill) 1147 compiler_panic( 1148 x->c, SRCLOC_NONE, 1149 "link_macho: coalesce mismatch on %.*s,%.*s (flags/zerofill)", 1150 SLICE_ARG(slice_from_cstr(m->segname)), 1151 SLICE_ARG(slice_from_cstr(m->sectname))); 1152 u64 end = m->vaddr + m->size; 1153 u64 prev_end = tail->vaddr + tail->size; 1154 if (end > prev_end) tail->size = end - tail->vaddr; 1155 if (m->align > tail->align) tail->align = m->align; 1156 } else { 1157 OutSec* o = &x->outs[x->nouts++]; 1158 o->segname = m->segname; 1159 o->sectname = m->sectname; 1160 o->vaddr = m->vaddr; 1161 o->file_offset = m->file_offset; 1162 o->size = m->size; 1163 o->align = m->align; 1164 o->flags = m->flags; 1165 o->reserved1 = m->reserved1; 1166 o->reserved2 = m->reserved2; 1167 o->segidx = m->segidx; 1168 o->is_zerofill = m->is_zerofill; 1169 } 1170 } 1171 h->free(h, order, sizeof(u32) * (x->nsecs + 1u)); 1172 /* Recompute per-segment OutSec span; Phase A's count was for 1173 * sizeofcmds sizing — recompute it here as the source of truth and 1174 * assert agreement. */ 1175 for (u32 i = 0; i < x->nsegs; ++i) { 1176 x->segs[i].first_out = 0; 1177 } 1178 u32 prev_nouts[MSEG_COUNT]; 1179 for (u32 i = 0; i < x->nsegs; ++i) prev_nouts[i] = x->segs[i].nouts; 1180 for (u32 i = 0; i < x->nsegs; ++i) x->segs[i].nouts = 0; 1181 for (u32 i = 0; i < x->nouts; ++i) { 1182 u8 sx = x->outs[i].segidx; 1183 if (x->segs[sx].nouts == 0) x->segs[sx].first_out = i; 1184 ++x->segs[sx].nouts; 1185 } 1186 for (u32 i = 0; i < x->nsegs; ++i) { 1187 if (prev_nouts[i] != x->segs[i].nouts) 1188 compiler_panic(x->c, SRCLOC_NONE, 1189 "link_macho: OutSec count drift seg %u (%u vs %u)", 1190 (u32)i, prev_nouts[i], x->segs[i].nouts); 1191 } 1192 } 1193 } 1194 1195 /* ---- pass: shift LinkImage into final vaddrs/file_offsets ---- 1196 * 1197 * The sections in img->sections are still in their original 1198 * link_layout coordinates. Map each LinkSection -> its MSec and copy 1199 * the final vaddr/file_offset so reloc-apply walks correctly. */ 1200 1201 /* link_sec_id -> backing MSec index, built once. */ 1202 static void build_section_index(MCtx* x) { 1203 u32 n = x->img->nsections; 1204 x->by_link_sec_cap = n + 1u; 1205 x->by_link_sec = 1206 (MSec**)x->h->alloc(x->h, sizeof(MSec*) * x->by_link_sec_cap, 1207 _Alignof(MSec*)); 1208 if (!x->by_link_sec) 1209 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on section index"); 1210 for (u32 i = 0; i < x->by_link_sec_cap; ++i) x->by_link_sec[i] = NULL; 1211 for (u32 i = 0; i < x->nsecs; ++i) { 1212 MSec* m = &x->secs[i]; 1213 if (m->link_sec_id && m->link_sec_id < x->by_link_sec_cap) 1214 x->by_link_sec[m->link_sec_id] = m; 1215 } 1216 } 1217 1218 /* Re-base every section to its final Mach-O vaddr/file_offset, then shift the 1219 * relocs and defined symbols anchored to each section by its per-section delta. 1220 * A per-section delta table + three single passes (O(nsecs + nrelocs + nsyms)) 1221 * replaces the former per-section rescan of the whole reloc + sym vectors 1222 * (O(nsecs * (nrelocs + nsyms)) — the dominant obj-count superlinearity). The 1223 * ELF emitter (src/obj/elf/link.c) is the reference; this generalizes it to 1224 * per-section deltas because Mach-O segments get different bases. */ 1225 static void shift_sections(MCtx* x) { 1226 LinkImage* img = x->img; 1227 u32 n = img->nsections; 1228 i64* dv = (i64*)x->h->alloc(x->h, sizeof(i64) * (n + 1u), _Alignof(i64)); 1229 i64* df = (i64*)x->h->alloc(x->h, sizeof(i64) * (n + 1u), _Alignof(i64)); 1230 if (!dv || !df) 1231 compiler_panic(x->c, SRCLOC_NONE, "link_macho: oom on shift deltas"); 1232 for (u32 i = 0; i <= n; ++i) { 1233 dv[i] = 0; 1234 df[i] = 0; 1235 } 1236 /* Per-section deltas; re-base each LinkSection. Sections keyed by id (the 1237 * same id relocs/syms carry), matching the former ls->id match. */ 1238 for (u32 i = 0; i < x->nsecs; ++i) { 1239 MSec* m = &x->secs[i]; 1240 LinkSection* ls; 1241 u32 id; 1242 if (!m->link_sec_id) continue; 1243 ls = &img->sections[m->link_sec_id - 1u]; 1244 id = ls->id; 1245 if (id == 0 || id > n) continue; 1246 dv[id] = (i64)m->vaddr - (i64)ls->vaddr; 1247 df[id] = (i64)m->file_offset - (i64)ls->file_offset; 1248 ls->vaddr = m->vaddr; 1249 ls->file_offset = m->file_offset; 1250 } 1251 /* Shift relocs by their section's delta (0 == no shift, was the early-out). */ 1252 for (u32 ri = 0; ri < LinkRelocs_count(&img->relocs); ++ri) { 1253 LinkRelocApply* r = LinkRelocs_at(&img->relocs, ri); 1254 u32 id = r->link_section_id; 1255 if (id == LINK_SEC_NONE || id > n) continue; 1256 r->write_vaddr = (u64)((i64)r->write_vaddr + dv[id]); 1257 r->write_file_offset = (u64)((i64)r->write_file_offset + df[id]); 1258 } 1259 /* Shift defined, non-absolute symbols. Match by section_id (multiple input 1260 * sections may share a pre-shift vaddr; the bucket each starts at offset 0). */ 1261 for (u32 si = 0; si < LinkSyms_count(&img->syms); ++si) { 1262 LinkSymbol* s = LinkSyms_at(&img->syms, si); 1263 u32 id; 1264 if (!s->defined || s->kind == SK_ABS) continue; 1265 id = s->section_id; 1266 if (id == LINK_SEC_NONE || id > n) continue; 1267 s->vaddr = (u64)((i64)s->vaddr + dv[id]); 1268 } 1269 x->h->free(x->h, dv, sizeof(i64) * (n + 1u)); 1270 x->h->free(x->h, df, sizeof(i64) * (n + 1u)); 1271 } 1272 1273 /* ---- pass: apply relocations + collect chained-fixup sites ---- 1274 * 1275 * Reloc dispatch: 1276 * target=imported func + CALL26/JUMP26 -> S = stub vaddr 1277 * target=import + GOT_LOAD_PAGE21/PAGEOFF12 -> S = got slot vaddr 1278 * target=import + ABS64 -> write 0; collect bind site 1279 * target=internal + ABS64 -> write target VA; collect rebase site 1280 * everything else -> standard apply 1281 * 1282 * Patch sites for chained fixups are 8-byte slots; for ABS32 we do not 1283 * support fixups (no chained-fixup format for 32-bit pointers in 1284 * standard arm64 — would need DYLD_CHAINED_PTR_32). Internal R_ABS32 1285 * still works (no slide adjustment is wrong technically, but for 1286 * compile-time-known offsets it suffices). 1287 */ 1288 1289 typedef struct FixSite { 1290 u8 segidx; /* 2 = __DATA_CONST, 3 = __DATA */ 1291 u8 is_bind; /* 0 = rebase, 1 = bind */ 1292 u8 pad[2]; 1293 u32 import_idx; /* 1-based import index for binds, 0 for rebases */ 1294 u64 vaddr; /* absolute VA of the slot */ 1295 u64 rebase_target; /* unslid target VA; only used for rebases */ 1296 } FixSite; 1297 1298 typedef struct FixList { 1299 Heap* heap; 1300 FixSite* a; 1301 u32 n; 1302 u32 cap; 1303 } FixList; 1304 1305 static void fix_init(FixList* fl, Heap* h) { 1306 fl->heap = h; 1307 fl->a = NULL; 1308 fl->n = 0; 1309 fl->cap = 0; 1310 } 1311 static void fix_fini(FixList* fl) { 1312 if (fl->a) fl->heap->free(fl->heap, fl->a, sizeof(*fl->a) * fl->cap); 1313 fl->a = NULL; 1314 fl->n = fl->cap = 0; 1315 } 1316 static void fix_push(FixList* fl, const FixSite* s) { 1317 if (VEC_GROW(fl->heap, fl->a, fl->cap, fl->n + 1u)) return; 1318 fl->a[fl->n++] = *s; 1319 } 1320 1321 /* find MSec covering an absolute vaddr */ 1322 static MSec* msec_for_vaddr(MCtx* x, u64 v) { 1323 for (u32 i = 0; i < x->nsecs; ++i) { 1324 MSec* m = &x->secs[i]; 1325 if (v >= m->vaddr && v < m->vaddr + m->size) return m; 1326 } 1327 return NULL; 1328 } 1329 1330 static u8* bytes_for_section(MCtx* x, MSec* m, LinkImage* img) { 1331 if (m->synth_data) { 1332 /* Synthetic — caller reads/writes via x->stubs_bytes / x->got_bytes. */ 1333 if (m->synth_data == x->stubs_bytes) return x->stubs_bytes; 1334 if (m->synth_data == x->got_bytes) return x->got_bytes; 1335 return NULL; 1336 } 1337 /* Backed by a LinkSection: find the LinkSegment buffer that section 1338 * sits in (link_layout.c stored input section bytes there). */ 1339 LinkSection* ls = &img->sections[m->link_sec_id - 1u]; 1340 u32 segid = ls->segment_id; 1341 if (segid == LINK_SEG_NONE) return NULL; 1342 return img->segment_bytes[segid - 1u]; 1343 } 1344 1345 /* Map the LinkSection that backs a write_vaddr to an MSec, then to the 1346 * underlying byte buffer. */ 1347 static u8* patch_ptr(MCtx* x, LinkImage* img, const LinkRelocApply* r, 1348 MSec** out_msec) { 1349 /* Look up via the LinkSection. After shift_sections the section 1350 * vaddr is the Mach-O vaddr; the corresponding MSec backs it. */ 1351 if (r->link_section_id == LINK_SEC_NONE) return NULL; 1352 LinkSection* ls = &img->sections[r->link_section_id - 1u]; 1353 /* MSec by link_sec_id via the prebuilt index (O(1)); fall back to a scan if 1354 * the index was not built (it always is, post plan_layout). */ 1355 MSec* m = NULL; 1356 if (x->by_link_sec && ls->id < x->by_link_sec_cap) { 1357 m = x->by_link_sec[ls->id]; 1358 } else { 1359 for (u32 i = 0; i < x->nsecs; ++i) { 1360 if (x->secs[i].link_sec_id == ls->id) { 1361 m = &x->secs[i]; 1362 break; 1363 } 1364 } 1365 } 1366 if (!m) return NULL; 1367 /* The LinkSegment's bytes are valid (not shifted), but the offset 1368 * within them is the original input_offset. Use input_offset for 1369 * the byte offset, since the LinkSegment buffer wasn't reshuffled. */ 1370 /* link_layout.c set ls->file_offset = seg.file_offset + input_offset 1371 * originally. ls->vaddr similarly. After our shift, they're new. 1372 * The byte offset within the segment buffer is still input_offset. */ 1373 u8* base = bytes_for_section(x, m, img); 1374 if (!base) return NULL; 1375 u32 within_section = (u32)(r->write_vaddr - m->vaddr); 1376 /* The segment buffer's first byte corresponds to ls->input_offset==0 1377 * for the FIRST section in the segment. But that's a complication. 1378 * For simplicity we recompute the segment-relative byte offset by 1379 * (file_offset - segment.file_offset) where segment.file_offset is 1380 * unchanged. Wait: the original layout produced `ls->file_offset = 1381 * seg.file_offset + input_offset`, and we may have changed 1382 * ls->file_offset. Let's just use input_offset stored on the 1383 * LinkSection. */ 1384 u32 in_off = (u32)(ls->input_offset + within_section); 1385 if (out_msec) *out_msec = m; 1386 return base + in_off; 1387 } 1388 1389 /* Symbol-relative resolved-address S, accounting for imports. */ 1390 static int sym_S(MCtx* x, LinkImage* img, LinkSymId id, u64* out_S, 1391 int* out_imp_idx) { 1392 *out_S = 0; 1393 *out_imp_idx = 0; 1394 if (id == LINK_SYM_NONE) return 0; 1395 LinkSymbol* s = sym_at(img, id); 1396 if (!s) return 0; 1397 /* Look up the import index — real imports plus internal-GOT entries 1398 * the collect_imports pass materialized for GOT-routed internal refs. */ 1399 u32 idx = 0; 1400 if (id < x->sym_to_imp_size) idx = x->sym_to_imp[id]; 1401 if (!idx && s->name != 0) { 1402 LinkSymId canon = symhash_get(&img->globals, s->name); 1403 if (canon != LINK_SYM_NONE && canon < x->sym_to_imp_size) 1404 idx = x->sym_to_imp[canon]; 1405 } 1406 if (s->imported) { 1407 *out_imp_idx = (int)idx; 1408 return 1; 1409 } 1410 /* Internal symbol that has a GOT slot — surface the import index so 1411 * the GOT_LOAD reloc paths in apply_relocs find it, but also expose 1412 * S=vaddr so non-GOT relocs (CALL26 etc.) still apply directly. */ 1413 *out_imp_idx = (int)idx; 1414 *out_S = s->vaddr; 1415 return 0; 1416 } 1417 1418 static void apply_relocs(MCtx* x, FixList* fl) { 1419 LinkImage* img = x->img; 1420 for (u32 i = 0; i < LinkRelocs_count(&img->relocs); ++i) { 1421 LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 1422 if (r->target == LINK_SYM_NONE) continue; 1423 /* File-only .debug_* section: patch the registry buffer in place (no 1424 * __got/stub/chained-fixup — debug bytes aren't loaded or slid). A 1425 * SK_SECTION target resolves to its DWARF-section-relative base; a 1426 * code/data symbol to its final (absolute) vaddr for low_pc. Mach-O 1427 * vaddrs are already absolute, so there's no extra image base. */ 1428 { 1429 const LinkSection* sec = &img->sections[r->link_section_id - 1u]; 1430 if (sec->file_only) { 1431 u8* dbg = link_fileonly_bytes(img, r->link_section_id); 1432 const LinkSymbol* tgt = sym_at(img, r->target); 1433 if (dbg && tgt) 1434 link_reloc_apply(x->c, r->kind, dbg + r->offset, tgt->vaddr, 1435 r->addend, 0); 1436 continue; 1437 } 1438 } 1439 MSec* msec = NULL; 1440 u8* P_bytes = patch_ptr(x, img, r, &msec); 1441 if (!P_bytes) continue; 1442 u64 P = r->write_vaddr; 1443 1444 u64 S; 1445 int imp_idx; 1446 int is_imp = sym_S(x, img, r->target, &S, &imp_idx); 1447 1448 /* TLVP relocs route through a __thread_ptrs slot regardless of 1449 * whether the descriptor target is in-image or imported. Resolved 1450 * before the import / internal split because an imported TLV 1451 * descriptor doesn't use the __got slot (its address lives in 1452 * __thread_ptrs with its own chained bind). */ 1453 if (reloc_kind_is_tlvp(x->c, r->kind)) { 1454 u32 tlv_idx = 1455 (r->target < x->sym_to_tlv_size) ? x->sym_to_tlv[r->target] : 0u; 1456 if (!tlv_idx) 1457 compiler_panic(x->c, SRCLOC_NONE, 1458 "link_macho: TLVP reloc has no __thread_ptrs slot"); 1459 u64 slot_v = x->tlv_ptrs_vaddr + (tlv_idx - 1u) * MZ_TLVP_SIZE; 1460 link_reloc_apply(x->c, r->kind, P_bytes, slot_v, r->addend, P); 1461 continue; 1462 } 1463 1464 if (is_imp) { 1465 MachImp* mi = (imp_idx > 0) ? &x->imports[imp_idx - 1] : NULL; 1466 if (reloc_kind_is_branch(x->c, r->kind)) { 1467 if (!mi || !mi->stub_idx) 1468 compiler_panic(x->c, SRCLOC_NONE, 1469 "link_macho: import has no stub for branch"); 1470 u64 stub_v = x->stubs_vaddr + (mi->stub_idx - 1u) * x->macho->stub_size; 1471 link_reloc_apply(x->c, r->kind, P_bytes, stub_v, r->addend, P); 1472 continue; 1473 } 1474 if (reloc_kind_is_got_load(x->c, r->kind)) { 1475 if (!mi) 1476 compiler_panic(x->c, SRCLOC_NONE, 1477 "link_macho: GOT reloc for unknown import"); 1478 u64 got_v = x->got_vaddr + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1479 link_reloc_apply(x->c, r->kind, P_bytes, got_v, r->addend, P); 1480 continue; 1481 } 1482 if (reloc_kind_is_direct_page(x->c, r->kind)) { 1483 /* Direct page/lo12 against an import: route through __got. */ 1484 if (!mi) 1485 compiler_panic(x->c, SRCLOC_NONE, 1486 "link_macho: PAGE/LO12 against unknown import"); 1487 u64 got_v = x->got_vaddr + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1488 link_reloc_apply(x->c, r->kind, P_bytes, got_v, r->addend, P); 1489 continue; 1490 } 1491 if (r->kind == R_ABS64) { 1492 /* Direct 8-byte absolute against an import: bind the slot. */ 1493 wr_u64_le(P_bytes, 0); 1494 FixSite fs = {(u8)msec->segidx, 1, {0}, (u32)imp_idx, P, 0}; 1495 fix_push(fl, &fs); 1496 continue; 1497 } 1498 compiler_panic(x->c, SRCLOC_NONE, 1499 "link_macho: unhandled reloc kind %u against imported " 1500 "symbol", 1501 (u32)r->kind); 1502 } 1503 1504 /* Internal relocs. */ 1505 if (r->kind == R_ABS64) { 1506 /* Special case: ABS64 reloc inside a TLV descriptor record 1507 * (__thread_vars section) targeting in-image TLS storage. This 1508 * is the descriptor's word-2 "offset" field — dyld interprets it 1509 * as the per-thread offset of the storage within the TLS image, 1510 * NOT as an absolute address. Apple's ld writes the literal 1511 * offset and emits no chained-fixup entry; replicate that so the 1512 * chain skips over this slot (chained_fixups already does the 1513 * right thing: no fixsite -> no chain link). */ 1514 if (msec && (msec->flags & SECTION_TYPE) == S_THREAD_LOCAL_VARIABLES && 1515 x->has_tls_image) { 1516 u64 offset = (S + (u64)r->addend) - x->tls_image_vaddr; 1517 wr_u64_le(P_bytes, offset); 1518 continue; 1519 } 1520 /* Rebase site. */ 1521 wr_u64_le(P_bytes, S + (u64)r->addend); 1522 FixSite fs = {(u8)msec->segidx, 0, {0}, 0, P, S + (u64)r->addend}; 1523 fix_push(fl, &fs); 1524 continue; 1525 } 1526 /* Internal symbol routed through __got (clang emits GOT_LOAD_PAGE21 1527 * for any extern global, even if the def is in-image). imp_idx 1528 * was populated by collect_imports' internal-GOT pass; redirect 1529 * the page/lo12 reloc to the GOT slot's vaddr. */ 1530 if (imp_idx > 0 && reloc_kind_is_got_load(x->c, r->kind)) { 1531 MachImp* mi = &x->imports[imp_idx - 1]; 1532 u64 got_v = x->got_vaddr + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1533 link_reloc_apply(x->c, r->kind, P_bytes, got_v, r->addend, P); 1534 continue; 1535 } 1536 /* Generic apply. */ 1537 link_reloc_apply(x->c, r->kind, P_bytes, S, r->addend, P); 1538 } 1539 1540 /* Per-slot chained fixup. Real imports → bind (dyld resolves at 1541 * load). Internal GOT entries → rebase pointing at the symbol's 1542 * image-relative vaddr; a target vaddr of 0 (weak undef → NULL) gets 1543 * no fixup, just a literal zero slot — chained fixups treat 0 as a 1544 * gap and won't disturb it. */ 1545 for (u32 i = 0; i < x->nimports; ++i) { 1546 MachImp* mi = &x->imports[i]; 1547 u64 slot_v = x->got_vaddr + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1548 if (mi->internal) { 1549 /* Re-read the symbol's final vaddr now that shift_sections has 1550 * rebased every defined symbol into the Mach-O image layout 1551 * (collect_imports snapshotted too early). */ 1552 LinkSymbol* s = sym_at(img, mi->sym); 1553 u64 tgt_v = s ? s->vaddr : 0; 1554 u8* slot = x->got_bytes + (mi->got_idx - 1u) * MZ_GOT_SIZE; 1555 wr_u64_le(slot, tgt_v); 1556 if (tgt_v == 0) continue; /* weak-undef → NULL */ 1557 FixSite fs = {2u, 0, {0}, 0, slot_v, tgt_v}; 1558 fix_push(fl, &fs); 1559 } else { 1560 /* clear slot bytes (already zero) — dyld writes via chain */ 1561 FixSite fs = {2u, 1, {0}, i + 1u, slot_v, 0}; 1562 fix_push(fl, &fs); 1563 } 1564 } 1565 1566 /* Per-slot TLV pointer fixups. Mirror of the __got loop above: each 1567 * __thread_ptrs slot points at the descriptor record. When the 1568 * descriptor is in-image (internal) we REBASE to its final vaddr; when 1569 * it lives in a dylib we BIND through the descriptor's MachImp. The 1570 * slot itself lives in __DATA (segidx=3), distinct from __got's 1571 * __DATA_CONST (segidx=2). */ 1572 for (u32 i = 0; i < x->ntlv; ++i) { 1573 MachTlv* ts = &x->tlv_slots[i]; 1574 u64 slot_v = x->tlv_ptrs_vaddr + (ts->tlv_idx - 1u) * MZ_TLVP_SIZE; 1575 u8* slot = x->tlv_ptrs_bytes + (ts->tlv_idx - 1u) * MZ_TLVP_SIZE; 1576 if (ts->imported) { 1577 if (!ts->import_idx) 1578 compiler_panic(x->c, SRCLOC_NONE, 1579 "link_macho: imported TLV without matching import slot"); 1580 wr_u64_le(slot, 0); 1581 FixSite fs = {3u, 1, {0}, ts->import_idx, slot_v, 0}; 1582 fix_push(fl, &fs); 1583 } else { 1584 LinkSymbol* s = sym_at(img, ts->sym); 1585 u64 tgt_v = s ? s->vaddr : 0; 1586 wr_u64_le(slot, tgt_v); 1587 if (tgt_v == 0) continue; /* weak-undef descriptor → NULL */ 1588 FixSite fs = {3u, 0, {0}, 0, slot_v, tgt_v}; 1589 fix_push(fl, &fs); 1590 } 1591 } 1592 } 1593 1594 /* ---- chained fixups blob assembler ---- 1595 * 1596 * For each segment that has fixups, build a dyld_chained_starts_in_segment 1597 * with one chain per page (MZ_PAGE). Within a page, sort sites by 1598 * offset, encode each as DYLD_CHAINED_PTR_64, and link via the `next` 1599 * field (4-byte units, 0 = end of chain). 1600 */ 1601 1602 static int site_cmp_by_vaddr(const void* a, const void* b) { 1603 const FixSite* x = a; 1604 const FixSite* y = b; 1605 if (x->vaddr < y->vaddr) return -1; 1606 if (x->vaddr > y->vaddr) return 1; 1607 return 0; 1608 } 1609 1610 /* Map each MCtx segment slot to its index in the emitted LC_SEGMENT_64 1611 * sequence and return the emitted-segment count. Empty middle segments 1612 * (__DATA_CONST / __DWARF with no sections) are omitted from the load 1613 * commands (see emit_load_command_segment calls), so the emitted order is 1614 * a compaction of the slot order: PAGEZERO, TEXT, [DATA_CONST], [DATA], 1615 * [DWARF], LINKEDIT. Must mirror that emit logic exactly. dyld indexes 1616 * dyld_chained_starts_in_image.seg_info_offset[] by this emitted order, so 1617 * the chained-fixups seg table uses it rather than the raw slot index — 1618 * indexing by slot leaves a non-emitted hole that x86_64 dyld's 1619 * applyFixupsGeneric resolves to the wrong segment (SIGBUS at load). 1620 * Slots that are not emitted get emit_idx[slot] = 0xff. */ 1621 static u32 macho_emit_seg_indices(const MCtx* x, u8 emit_idx[MSEG_COUNT]) { 1622 u32 n = 0; 1623 for (u32 i = 0; i < x->nsegs; ++i) { 1624 int emitted = (i == MSEG_PAGEZERO) || (i == MSEG_LINKEDIT) || 1625 (x->segs[i].nsects > 0); 1626 emit_idx[i] = emitted ? (u8)n : 0xffu; 1627 if (emitted) ++n; 1628 } 1629 return n; 1630 } 1631 1632 /* tiny insertion sort to avoid pulling qsort */ 1633 static void sort_sites(FixSite* a, u32 n) { 1634 for (u32 i = 1; i < n; ++i) { 1635 FixSite tmp = a[i]; 1636 u32 j = i; 1637 while (j > 0 && site_cmp_by_vaddr(&a[j - 1], &tmp) > 0) { 1638 a[j] = a[j - 1]; 1639 --j; 1640 } 1641 a[j] = tmp; 1642 } 1643 } 1644 1645 static void emit_pointer(u8* slot, int is_bind, u32 ord_or_target_lo, 1646 u32 high_or_target_hi, u32 next4) { 1647 /* DYLD_CHAINED_PTR_64: 1648 * bind : ordinal:24, addend:8, reserved:19, next:12, bind:1=1 1649 * rebase: target:36 (vmaddr), high8:8, reserved:7, next:12, bind:1=0 1650 */ 1651 u64 v = 0; 1652 if (is_bind) { 1653 u64 ordinal = (u64)ord_or_target_lo & 0xffffffull; /* 24 bits */ 1654 u64 addend = 0; 1655 u64 next = (u64)next4 & 0xfffull; 1656 v = ordinal | (addend << 24) | (0ull /* reserved */ << 32) | (next << 51) | 1657 ((u64)1 << 63); 1658 } else { 1659 /* rebase: target is full vmaddr; we get hi:lo split. */ 1660 u64 target = ((u64)high_or_target_hi << 32) | (u64)ord_or_target_lo; 1661 target &= ((u64)1 << 36) - 1u; /* 36 bits */ 1662 u64 high8 = 0; 1663 u64 next = (u64)next4 & 0xfffull; 1664 v = target | (high8 << 36) | (0ull /* reserved */ << 44) | (next << 51) | 1665 ((u64)0 << 63); 1666 } 1667 wr_u64_le(slot, v); 1668 } 1669 1670 static void build_chained_fixups(MCtx* x, FixList* fl) { 1671 Heap* h = x->h; 1672 ObjByteBuf* out = &x->chained_fixups; 1673 objbb_init(out, h); 1674 1675 /* Header (32 B): 1676 * uint32 fixups_version (=0) 1677 * uint32 starts_offset 1678 * uint32 imports_offset 1679 * uint32 symbols_offset 1680 * uint32 imports_count 1681 * uint32 imports_format (=1) 1682 * uint32 symbols_format (=0) 1683 */ 1684 u32 hdr_pos = objbb_u32(out, 0); /* fixups_version */ 1685 (void)hdr_pos; 1686 u32 starts_offset_pos = objbb_u32(out, 0); 1687 u32 imports_offset_pos = objbb_u32(out, 0); 1688 u32 symbols_offset_pos = objbb_u32(out, 0); 1689 objbb_u32(out, x->nimports_real); 1690 objbb_u32(out, DYLD_CHAINED_IMPORT); 1691 objbb_u32(out, 0); /* symbols uncompressed */ 1692 /* dyld expects 8-byte alignment of the starts table. */ 1693 objbb_align(out, 4); 1694 1695 /* dyld_chained_starts_in_image: 1696 * uint32 seg_count 1697 * uint32 seg_info_offset[seg_count] 1698 * 1699 * seg_count must equal mach-O segment count (5). 1700 * seg_info_offset[i] = 0 means no fixups in that segment. 1701 */ 1702 u32 starts_off = out->len; 1703 wr_u32_le(out->data + starts_offset_pos, starts_off); 1704 /* seg_count + seg_info_offset[] are indexed by emitted LC_SEGMENT_64 1705 * order, not MCtx slot — empty middle segments are dropped from the load 1706 * commands. */ 1707 u8 emit_seg[MSEG_COUNT]; 1708 u32 emit_nsegs = macho_emit_seg_indices(x, emit_seg); 1709 objbb_u32(out, emit_nsegs); 1710 /* Reserve seg_info_offset[]. */ 1711 u32 seg_info_offsets_pos = out->len; 1712 for (u32 i = 0; i < emit_nsegs; ++i) objbb_u32(out, 0); 1713 1714 /* Sort fixsites by vaddr globally. */ 1715 sort_sites(fl->a, fl->n); 1716 1717 /* Per segment, emit dyld_chained_starts_in_segment when fixups present. */ 1718 for (u32 si = 0; si < x->nsegs; ++si) { 1719 /* count sites in this segment */ 1720 u32 first = (u32)-1, count = 0; 1721 for (u32 k = 0; k < fl->n; ++k) { 1722 if (fl->a[k].segidx == si) { 1723 if (first == (u32)-1) first = k; 1724 ++count; 1725 } 1726 } 1727 if (!count) continue; 1728 if (emit_seg[si] == 0xffu) 1729 compiler_panic(x->c, SRCLOC_NONE, 1730 "link_macho: chained fixups in non-emitted segment %u", 1731 (unsigned)si); 1732 /* Page-align this struct to 4. */ 1733 objbb_align(out, 4); 1734 u32 sis_off = out->len; 1735 /* Patch seg_info_offset[emitted index] to (sis_off - starts_off). */ 1736 wr_u32_le(out->data + seg_info_offsets_pos + emit_seg[si] * 4u, 1737 sis_off - starts_off); 1738 1739 /* Compute page count for this segment. */ 1740 u64 seg_va = x->segs[si].vmaddr; 1741 u64 seg_size = x->segs[si].vmsize ? x->segs[si].vmsize : MZ_PAGE; 1742 u32 page_count = (u32)((seg_size + MZ_PAGE - 1u) / MZ_PAGE); 1743 1744 /* dyld_chained_starts_in_segment: 1745 * uint32 size 1746 * uint16 page_size 1747 * uint16 pointer_format 1748 * uint64 segment_offset (offset of segment's first byte from 1749 * mach_header) 1750 * uint32 max_valid_pointer (0 for 64-bit) 1751 * uint16 page_count 1752 * uint16 page_start[page_count] (0xFFFF = no fixups in page) 1753 */ 1754 u32 sis_size_pos = objbb_u32(out, 0); /* fill below */ 1755 objbb_u16(out, (u16)MZ_PAGE); 1756 objbb_u16(out, (u16)DYLD_CHAINED_PTR_64); 1757 objbb_u64(out, (u64)x->segs[si].fileoff); /* segment file offset */ 1758 objbb_u32(out, 0); 1759 objbb_u16(out, (u16)page_count); 1760 u32 page_starts_pos = out->len; 1761 for (u32 p = 0; p < page_count; ++p) objbb_u16(out, 0xFFFFu); 1762 /* size includes the page_start array */ 1763 u32 sis_size = out->len - sis_size_pos + 4u; 1764 /* Hmm, the `size` field is the size of *this* struct. We measure 1765 * from sis_off through end of page_starts. */ 1766 sis_size = out->len - sis_off; 1767 wr_u32_le(out->data + sis_size_pos, sis_size); 1768 1769 /* Now: walk sites in this segment, group by page, write 1770 * page_start[i] = offset_in_page of first site, and chain via 1771 * next-field in the actual segment's bytes. */ 1772 /* Sites are sorted globally; collect contiguous run for this seg. */ 1773 u32 cur = first; 1774 while (cur < first + count) { 1775 u32 page_idx = (u32)((fl->a[cur].vaddr - seg_va) / MZ_PAGE); 1776 u32 offset_in_page = (u32)((fl->a[cur].vaddr - seg_va) % MZ_PAGE); 1777 wr_u16_le(out->data + page_starts_pos + page_idx * 2u, 1778 (u16)offset_in_page); 1779 /* Walk this page's chain. */ 1780 u32 next_in_page = cur; 1781 while (next_in_page + 1 < first + count) { 1782 u64 nv = fl->a[next_in_page + 1].vaddr; 1783 if (nv >= seg_va + (u64)(page_idx + 1) * MZ_PAGE) break; 1784 ++next_in_page; 1785 } 1786 /* Encode chain pointers. */ 1787 for (u32 k = cur; k <= next_in_page; ++k) { 1788 FixSite* s = &fl->a[k]; 1789 u32 next4 = 0; 1790 if (k < next_in_page) { 1791 u64 dist = fl->a[k + 1].vaddr - s->vaddr; 1792 next4 = (u32)(dist / 4u); 1793 } 1794 /* Find segment bytes. Synthetic pointer sections have private 1795 * buffers; file-backed sections can live in any segment, including 1796 * pointer-bearing read-only constants in __TEXT. */ 1797 u8* slot = NULL; 1798 if (s->segidx == 2 && x->got_bytes && s->vaddr >= x->got_vaddr && 1799 s->vaddr < x->got_vaddr + x->got_size) { 1800 /* __DATA_CONST: __got slot. */ 1801 slot = x->got_bytes + (s->vaddr - x->got_vaddr); 1802 } else if (x->tlv_ptrs_bytes && s->vaddr >= x->tlv_ptrs_vaddr && 1803 s->vaddr < x->tlv_ptrs_vaddr + x->tlv_ptrs_size) { 1804 slot = x->tlv_ptrs_bytes + (s->vaddr - x->tlv_ptrs_vaddr); 1805 } else { 1806 MSec* m = msec_for_vaddr(x, s->vaddr); 1807 if (m && m->link_sec_id) { 1808 u8* base = bytes_for_section(x, m, x->img); 1809 if (base) { 1810 LinkSection* ls = &x->img->sections[m->link_sec_id - 1u]; 1811 u32 in_off = (u32)(ls->input_offset + (s->vaddr - m->vaddr)); 1812 slot = base + in_off; 1813 } 1814 } 1815 } 1816 if (!slot) 1817 compiler_panic(x->c, SRCLOC_NONE, 1818 "link_macho: chained-fixup slot for vaddr 0x%llx not " 1819 "in any segment buffer", 1820 (unsigned long long)s->vaddr); 1821 if (s->is_bind) { 1822 /* ordinal is import index (1-based) - 1; chained-import format 1823 * uses 0-based. */ 1824 if (s->import_idx == 0 || s->import_idx > x->nimports_real) { 1825 compiler_panic( 1826 x->c, SRCLOC_NONE, 1827 "link_macho: chained bind for vaddr 0x%llx uses import index " 1828 "%u outside real import table size %u", 1829 (unsigned long long)s->vaddr, (unsigned)s->import_idx, 1830 (unsigned)x->nimports_real); 1831 } 1832 u32 ord = s->import_idx - 1u; 1833 emit_pointer(slot, 1, ord, 0, next4); 1834 } else { 1835 /* rebase target = unslid vmaddr */ 1836 u32 lo = (u32)(s->rebase_target & 0xffffffffu); 1837 u32 hi = (u32)(s->rebase_target >> 32); 1838 emit_pointer(slot, 0, lo, hi, next4); 1839 } 1840 } 1841 cur = next_in_page + 1u; 1842 } 1843 } 1844 1845 /* Imports table: one dyld_chained_import (4B) per real import. 1846 * Layout: lib_ordinal:8, weak:1, name_offset:23. Internal-GOT 1847 * entries are not bound by dyld so they're omitted here. */ 1848 objbb_align(out, 4); 1849 u32 imports_off = out->len; 1850 wr_u32_le(out->data + imports_offset_pos, imports_off); 1851 /* We need to first build the symbol pool to know name offsets. */ 1852 u32 symbols_off = imports_off + x->nimports_real * 4u; 1853 /* Reserve imports area. */ 1854 for (u32 i = 0; i < x->nimports_real; ++i) objbb_u32(out, 0); 1855 /* Emit symbols (each NUL-terminated). Set name_offset on each import. */ 1856 wr_u32_le(out->data + symbols_offset_pos, out->len); 1857 /* Leading NUL for offset 0. */ 1858 objbb_u8(out, 0); 1859 for (u32 i = 0; i < x->nimports_real; ++i) { 1860 MachImp* mi = &x->imports[i]; 1861 Slice nm_s = pool_slice(x->c->global, mi->name); 1862 const char* nm = nm_s.s; 1863 size_t nl = nm_s.len; 1864 if (!nm || !nl || mi->dylib_ord == 0 || mi->dylib_ord > x->ndylibs) { 1865 compiler_panic(x->c, SRCLOC_NONE, 1866 "link_macho: invalid chained import %u " 1867 "(name=%u dylib_ord=%u ndylibs=%u)", 1868 (unsigned)i, (unsigned)mi->name, (unsigned)mi->dylib_ord, 1869 (unsigned)x->ndylibs); 1870 } 1871 u32 off = out->len - symbols_off; 1872 objbb_str(out, nm, (u32)nl); 1873 /* Patch the import slot. */ 1874 u32 packed = ((u32)mi->dylib_ord & 0xffu) | 1875 ((u32)(mi->weak ? 1u : 0u) << 8) | ((off & 0x7fffffu) << 9); 1876 wr_u32_le(out->data + imports_off + i * 4u, packed); 1877 } 1878 (void)symbols_off; 1879 } 1880 1881 /* ---- exports trie ---- * 1882 * 1883 * Minimal trie: one node carrying a single export "_main" with the 1884 * entry symbol's VA-relative offset. This is enough for dyld; binaries 1885 * with a real exports trie include more data but we don't need it. */ 1886 1887 static void uleb128(ObjByteBuf* out, u64 v) { 1888 do { 1889 u8 byte = v & 0x7fu; 1890 v >>= 7; 1891 if (v) byte |= 0x80u; 1892 objbb_u8(out, byte); 1893 } while (v); 1894 } 1895 1896 static u32 uleb128_size(u64 v) { 1897 u32 n = 0; 1898 do { 1899 ++n; 1900 v >>= 7; 1901 } while (v); 1902 return n; 1903 } 1904 1905 static void build_exports_trie(MCtx* x) { 1906 /* Format: 1907 * node = (terminal_size: uleb128) (export_data)? (children_count: u8) 1908 * (children: [(label NUL) (offset uleb128)]*) 1909 * 1910 * We emit a trie with a single leaf at "_main" with offset 1911 * entry_offset (from __TEXT base). 1912 * 1913 * Easiest: single root node with children_count=1, child label = "_main", 1914 * child offset points to a leaf node. 1915 */ 1916 ObjByteBuf* out = &x->exports_trie; 1917 objbb_init(out, x->h); 1918 1919 LinkImage* img = x->img; 1920 LinkSymbol* esym = sym_at(img, img->entry_sym); 1921 if (!esym || !esym->defined) { 1922 /* No entry — emit a single empty terminal trie. */ 1923 objbb_u8(out, 0); /* terminal_size 0 */ 1924 objbb_u8(out, 0); /* children 0 */ 1925 return; 1926 } 1927 Slice nm_s = pool_slice(x->c->global, esym->name); 1928 const char* nm = nm_s.s; 1929 size_t nl = nm_s.len; 1930 if (!nm || nl == 0) { 1931 objbb_u8(out, 0); 1932 objbb_u8(out, 0); 1933 return; 1934 } 1935 /* leaf node: terminal_size = sizeof(uleb(flags)+uleb(offset)) 1936 * flags = 0 (regular export); offset = vaddr - __TEXT.vmaddr */ 1937 u64 entry_off = esym->vaddr - x->text_vaddr; 1938 1939 /* Compute leaf-node bytes length: uleb(flags=0) + uleb(offset). */ 1940 u32 flags = 0; 1941 u32 leaf_payload_len = uleb128_size(flags) + uleb128_size(entry_off); 1942 /* Layout: root node first, then leaf. The root node's child entry 1943 * carries the absolute offset of the leaf within the trie. */ 1944 1945 /* root: terminal_size=0, children_count=1, "_main"\0, child_offset= 1946 * (leaf-position uleb). 1947 * 1948 * The child offset's own ULEB width contributes to the leaf position, so 1949 * solve for the fixed point before emitting. */ 1950 u32 leaf_pos = 2u + (u32)nl + 1u + 1u; 1951 for (;;) { 1952 u32 n = uleb128_size(leaf_pos); 1953 u32 next = 2u + (u32)nl + 1u + n; 1954 if (next == leaf_pos) break; 1955 leaf_pos = next; 1956 } 1957 1958 objbb_u8(out, 0); /* root terminal size */ 1959 objbb_u8(out, 1); /* children_count */ 1960 objbb_str(out, nm, (u32)nl); 1961 uleb128(out, leaf_pos); 1962 /* leaf node */ 1963 if (out->len != leaf_pos) 1964 compiler_panic(x->c, SRCLOC_NONE, 1965 "macho: exports trie leaf offset mismatch"); 1966 /* terminal_size byte then payload */ 1967 objbb_u8(out, (u8)leaf_payload_len); 1968 uleb128(out, flags); 1969 uleb128(out, entry_off); 1970 objbb_u8(out, 0); /* children_count */ 1971 /* Pad trie to 8 bytes. */ 1972 objbb_align(out, 8); 1973 } 1974 1975 /* ---- symtab + strtab + indirect symtab ---- */ 1976 1977 typedef struct NlistRec { 1978 u32 strx; 1979 u8 type; 1980 u8 sect; /* 1-based section index (Mach-O) */ 1981 u16 desc; 1982 u64 value; 1983 } NlistRec; 1984 1985 static void build_symtab(MCtx* x) { 1986 Heap* h = x->h; 1987 LinkImage* img = x->img; 1988 objbb_init(&x->symtab, h); 1989 objbb_init(&x->strtab, h); 1990 objbb_init(&x->indirect, h); 1991 1992 /* strtab leading NUL */ 1993 objbb_u8(&x->strtab, 0); 1994 1995 /* Approach: 1996 * - Add one local nlist per defined LinkSymbol (locals + non-imported 1997 * externs) — but to keep things simple we only emit external defined 1998 * syms (mainly _main), plus all imports as N_UNDF|N_EXT. 1999 * 2000 * Mach-O dyld requires the symtab order: locals first, ext-defs next, 2001 * undef last (matched by LC_DYSYMTAB ranges). 2002 */ 2003 2004 /* Pass A: defined externals. */ 2005 u32 n_local = 0; 2006 u32 n_extdef = 0; 2007 u32 n_undef = 0; 2008 2009 /* For now we emit only externals + imports. No locals. */ 2010 /* extdef pass */ 2011 for (u32 i = 0; i < LinkSyms_count(&img->syms); ++i) { 2012 LinkSymbol* s = LinkSyms_at(&img->syms, i); 2013 if (!s->defined) continue; 2014 if (s->bind != SB_GLOBAL && s->bind != SB_WEAK) continue; 2015 if (s->name == 0) continue; 2016 if (!link_symbol_is_canonical_global(img, s)) continue; 2017 if (s->kind == SK_ABS) continue; /* skip abs externs */ 2018 /* Locate which OutSec contains this vaddr to figure out n_sect. 2019 * n_sect is the 1-based index into the flat section_64 table the 2020 * file actually contains (post-coalesce), matching what we emit 2021 * in emit_load_command_segment. */ 2022 u8 n_sect = 0; 2023 /* Prefer the section whose half-open [vaddr, vaddr+size) range contains 2024 * the symbol. This must win over the end-boundary fallback below: when 2025 * two sections abut (A ends exactly where B begins), a symbol at the 2026 * boundary is the *start* of B, not the end of A. */ 2027 for (u32 k = 0; k < x->nouts; ++k) { 2028 OutSec* o = &x->outs[k]; 2029 if (s->vaddr >= o->vaddr && s->vaddr < o->vaddr + o->size) { 2030 n_sect = (u8)(k + 1u); 2031 break; 2032 } 2033 } 2034 /* Fallback: a symbol sitting exactly one-past-the-end of a section with 2035 * no following section covering it (e.g. an end-of-section marker) is 2036 * attributed to the section that ends there. */ 2037 if (n_sect == 0) { 2038 for (u32 k = 0; k < x->nouts; ++k) { 2039 OutSec* o = &x->outs[k]; 2040 if (s->vaddr == o->vaddr + o->size) { 2041 n_sect = (u8)(k + 1u); 2042 break; 2043 } 2044 } 2045 } 2046 if (n_sect == 0) continue; 2047 Slice nm_s = pool_slice(x->c->global, s->name); 2048 const char* nm = nm_s.s; 2049 size_t nl = nm_s.len; 2050 u32 strx = x->strtab.len; 2051 if (nm && nl) objbb_str(&x->strtab, nm, (u32)nl); 2052 2053 u8 t[16]; 2054 u8 nt = N_SECT | N_EXT; 2055 if (s->bind == SB_WEAK) { 2056 /* N_WEAK_DEF in n_desc (not a flag in n_type) */ 2057 } 2058 wr_u32_le(t + 0, strx); 2059 t[4] = nt; 2060 t[5] = n_sect; 2061 wr_u16_le(t + 6, s->bind == SB_WEAK ? N_WEAK_DEF : 0); 2062 wr_u64_le(t + 8, s->vaddr); 2063 objbb_append(&x->symtab, t, 16); 2064 ++n_extdef; 2065 } 2066 2067 /* undef imports — real imports only. Internal-GOT entries don't get 2068 * N_UNDF nlist records since they're defined in the image. */ 2069 u32 imp_first_symtab_idx = n_extdef; 2070 for (u32 i = 0; i < x->nimports_real; ++i) { 2071 MachImp* mi = &x->imports[i]; 2072 Slice nm_s = pool_slice(x->c->global, mi->name); 2073 const char* nm = nm_s.s; 2074 size_t nl = nm_s.len; 2075 u32 strx = x->strtab.len; 2076 if (nm && nl) objbb_str(&x->strtab, nm, (u32)nl); 2077 2078 u8 t[16]; 2079 wr_u32_le(t + 0, strx); 2080 t[4] = N_UNDF | N_EXT; 2081 t[5] = 0; 2082 /* n_desc carries dylib ordinal in high byte (REFERENCED_DYNAMICALLY etc.) 2083 */ 2084 u16 desc = (u16)(((u16)mi->dylib_ord & 0xff) << 8); 2085 if (mi->weak) desc |= N_WEAK_REF; 2086 wr_u16_le(t + 6, desc); 2087 wr_u64_le(t + 8, 0); 2088 objbb_append(&x->symtab, t, 16); 2089 ++n_undef; 2090 } 2091 2092 /* indirect symtab: one entry per __stubs slot, then one per __got 2093 * slot. Internal-GOT slots use INDIRECT_SYMBOL_LOCAL (0x80000000) 2094 * since they have no nlist entry. */ 2095 u32 indirect_start = 0; 2096 /* Patch reserved1 of each synth OutSec. __stubs and __got are each 2097 * singleton OutSecs (synth sections never coalesce with user input), 2098 * so a sectname match identifies them unambiguously. */ 2099 for (u32 i = 0; i < x->nouts; ++i) { 2100 OutSec* o = &x->outs[i]; 2101 if (slice_eq_cstr(slice_from_cstr(o->sectname), "__stubs") && o->size) { 2102 o->reserved1 = indirect_start; 2103 for (u32 k = 0; k < x->nimports; ++k) { 2104 MachImp* mi = &x->imports[k]; 2105 if (!mi->stub_idx) continue; 2106 u32 sym_idx = imp_first_symtab_idx + k; 2107 objbb_u32(&x->indirect, sym_idx); 2108 ++indirect_start; 2109 } 2110 } 2111 } 2112 for (u32 i = 0; i < x->nouts; ++i) { 2113 OutSec* o = &x->outs[i]; 2114 if (slice_eq_cstr(slice_from_cstr(o->sectname), "__got") && o->size) { 2115 o->reserved1 = indirect_start; 2116 for (u32 k = 0; k < x->nimports; ++k) { 2117 MachImp* mi = &x->imports[k]; 2118 u32 sym_idx = mi->internal ? 0x80000000u /* INDIRECT_SYMBOL_LOCAL */ 2119 : (imp_first_symtab_idx + k); 2120 objbb_u32(&x->indirect, sym_idx); 2121 ++indirect_start; 2122 } 2123 } 2124 } 2125 2126 x->nsyms = n_local + n_extdef + n_undef; 2127 (void)n_local; 2128 (void)imp_first_symtab_idx; 2129 } 2130 2131 /* ---- LINKEDIT layout assembly ---- 2132 * 2133 * Place blobs in the order Apple prefers: 2134 * chained_fixups, exports_trie, fn_starts, data_in_code, 2135 * symtab, indirect, strtab, codesig 2136 */ 2137 2138 static void layout_linkedit(MCtx* x) { 2139 /* LC_FUNCTION_STARTS is a ULEB128 stream terminated by a zero byte. Keep a 2140 * real empty table here so tools that rewrite LINKEDIT preserve the 2141 * canonical blob order between exports and the symbol table. */ 2142 objbb_init(&x->fn_starts, x->h); 2143 objbb_u8(&x->fn_starts, 0); 2144 objbb_init(&x->data_in_code, x->h); 2145 objbb_init(&x->codesig, x->h); 2146 2147 u64 cur = x->linkedit_fileoff; 2148 /* chained fixups */ 2149 cur = ALIGN_UP(cur, 8u); 2150 x->chained_fixups_off = (u32)cur; 2151 cur += x->chained_fixups.len; 2152 /* exports trie. Keep LINKEDIT data blobs contiguous; Apple strip rejects 2153 * padding between chained fixups and the exports trie. */ 2154 x->exports_trie_off = (u32)cur; 2155 cur += x->exports_trie.len; 2156 /* function starts */ 2157 x->fn_starts_off = (u32)cur; 2158 cur += x->fn_starts.len; 2159 /* data in code */ 2160 cur = ALIGN_UP(cur, 8u); 2161 x->data_in_code_off = (u32)cur; 2162 /* symtab */ 2163 cur = ALIGN_UP(cur, 8u); 2164 x->symtab_off = (u32)cur; 2165 cur += x->symtab.len; 2166 /* indirect symtab */ 2167 cur = ALIGN_UP(cur, 4u); 2168 x->indirect_off = (u32)cur; 2169 cur += x->indirect.len; 2170 /* strtab */ 2171 cur = ALIGN_UP(cur, 8u); 2172 x->strtab_off = (u32)cur; 2173 cur += x->strtab.len; 2174 /* code signature: end-aligned to 16 */ 2175 cur = ALIGN_UP(cur, 16u); 2176 x->codesig_off = (u32)cur; 2177 2178 /* Linkedit segment file_size includes everything up to (but not yet 2179 * including) codesig. Codesig is computed below. */ 2180 u64 le_size = cur - x->linkedit_fileoff; 2181 /* Set linkedit segment size; will be increased after codesig. */ 2182 x->segs[MSEG_LINKEDIT].filesize = le_size; 2183 x->segs[MSEG_LINKEDIT].vmsize = ALIGN_UP(le_size, MZ_PAGE); 2184 if (!x->segs[MSEG_LINKEDIT].vmsize) x->segs[MSEG_LINKEDIT].vmsize = MZ_PAGE; 2185 } 2186 2187 /* ---- ad-hoc code signature (CodeDirectory + SuperBlob) ---- 2188 * 2189 * Produces a minimal embedded SuperBlob with a single CodeDirectory. 2190 * The CD is sha256-hashed over CS_PAGE_SIZE_LOG2 = 4096-byte pages of 2191 * the file (excluding the codesig itself). The kernel verifies the 2192 * CD's hash chain on exec. 2193 * 2194 * Output format (in big-endian for SuperBlob/CodeDirectory headers): 2195 * [SuperBlob] 2196 * u32 magic (0xfade0cc0) 2197 * u32 length 2198 * u32 count (=1) 2199 * [Slot] 2200 * u32 type (=0 CSSLOT_CODEDIRECTORY) 2201 * u32 offset (=20) -- relative to start of SuperBlob 2202 * [CodeDirectory] 2203 * u32 magic (0xfade0c02) 2204 * u32 length (bytes including all hashes) 2205 * u32 version (>=0x20400 for execSeg fields) 2206 * u32 flags (=0 ad-hoc — actually flags must include 0x2 2207 * (kSecCodeSignatureAdhoc)) u32 hashOffset (offset of first slot hash) u32 2208 * identOffset (offset of identifier string) u32 nSpecialSlots (=0) u32 2209 * nCodeSlots u32 codeLimit (file bytes covered) u8 hashSize (=32) u8 2210 * hashType (=2 sha256) u8 platform (=0) u8 pageSize (=12 for 4096) u32 2211 * spare2 (=0) u32 scatterOffset (=0) u32 teamOffset (=0) u32 spare3 (=0) 2212 * u64 codeLimit64 (=0) 2213 * u64 execSegBase (=__TEXT.fileoff) 2214 * u64 execSegLimit (=__TEXT.filesize) 2215 * u64 execSegFlags (=1 main binary) 2216 * [identifier bytes "a.out\0"] 2217 * [codeslot hashes nCodeSlots * 32 B] 2218 * 2219 * Hashes computed AFTER everything else is final — including the codesig 2220 * blob's own offset in the file (the hash range stops just before 2221 * codeLimit). */ 2222 2223 static void wr_u64_be(u8* p, u64 v) { 2224 for (u32 i = 0; i < 8; ++i) p[7 - i] = (u8)(v >> (i * 8)); 2225 } 2226 2227 /* Build the codesig blob with placeholder hashes; size is precise so 2228 * file layout is final after this. */ 2229 static void build_codesig_skeleton(MCtx* x, u32 code_limit, const char* ident) { 2230 u32 page_log2 = x->cs_page_log2 ? x->cs_page_log2 : CS_PAGE_SIZE_LOG2; 2231 u32 code_page = 1u << page_log2; 2232 u32 nslots = (code_limit + code_page - 1u) / code_page; 2233 2234 /* CodeDirectory size: 2235 * header 88 bytes through execSegFlags 2236 * identifier (ident_len + 1) 2237 * hashes (nslots * 32) 2238 */ 2239 u32 ident_len = (u32)slice_from_cstr(ident).len + 1u; 2240 u32 cd_hdr = 88u; 2241 u32 cd_size = cd_hdr + ident_len + nslots * CS_SHA256_LEN; 2242 /* SuperBlob: 12 hdr + 8 slot + cd. */ 2243 u32 sb_size = 12u + 8u + cd_size; 2244 2245 ObjByteBuf* out = &x->codesig; 2246 objbb_init(out, x->h); 2247 objbb_reserve(out, sb_size); 2248 memset(out->data, 0, sb_size); 2249 out->len = sb_size; 2250 2251 u8* sb = out->data; 2252 /* SuperBlob header */ 2253 wr_u32_be(sb + 0, CS_MAGIC_EMBEDDED_SIGNATURE); 2254 wr_u32_be(sb + 4, sb_size); 2255 wr_u32_be(sb + 8, 1); /* count */ 2256 /* slot 0: type=CSSLOT_CODEDIRECTORY, offset=20 */ 2257 wr_u32_be(sb + 12, CSSLOT_CODEDIRECTORY); 2258 wr_u32_be(sb + 16, 20u); 2259 2260 /* CodeDirectory */ 2261 u8* cd = sb + 20; 2262 wr_u32_be(cd + 0, CS_MAGIC_CODEDIRECTORY); 2263 wr_u32_be(cd + 4, cd_size); 2264 wr_u32_be(cd + 8, 0x20400u); /* version with execSeg */ 2265 wr_u32_be(cd + 12, 0x2u); /* flags = adhoc */ 2266 wr_u32_be(cd + 16, cd_hdr + ident_len); /* hashOffset */ 2267 wr_u32_be(cd + 20, cd_hdr); /* identOffset */ 2268 wr_u32_be(cd + 24, 0); /* nSpecialSlots */ 2269 wr_u32_be(cd + 28, nslots); 2270 wr_u32_be(cd + 32, code_limit); 2271 cd[36] = (u8)CS_SHA256_LEN; 2272 cd[37] = (u8)CS_HASHTYPE_SHA256; 2273 cd[38] = 0; /* platform */ 2274 cd[39] = (u8)page_log2; 2275 wr_u32_be(cd + 40, 0); /* spare2 */ 2276 wr_u32_be(cd + 44, 0); /* scatterOffset */ 2277 wr_u32_be(cd + 48, 0); /* teamOffset */ 2278 wr_u32_be(cd + 52, 0); /* spare3 */ 2279 wr_u64_be(cd + 56, 0); /* codeLimit64 */ 2280 wr_u64_be(cd + 64, x->segs[1].fileoff); /* execSegBase */ 2281 wr_u64_be(cd + 72, x->segs[1].filesize); /* execSegLimit */ 2282 wr_u64_be(cd + 80, CS_EXECSEG_MAIN_BINARY); 2283 2284 /* identifier */ 2285 memcpy(cd + cd_hdr, ident, ident_len); 2286 2287 x->codesig_size = sb_size; 2288 } 2289 2290 static void compute_codesig(MCtx* x, const u8* full_file, u32 file_len_excl_cs, 2291 const char* ident) { 2292 u32 page_log2 = x->cs_page_log2 ? x->cs_page_log2 : CS_PAGE_SIZE_LOG2; 2293 u32 code_page = 1u << page_log2; 2294 u32 nslots = (file_len_excl_cs + code_page - 1u) / code_page; 2295 u32 ident_len = (u32)slice_from_cstr(ident).len + 1u; 2296 u8* cd = x->codesig.data + 12 + 8; 2297 u8* hashes = cd + 88u + ident_len; 2298 2299 for (u32 i = 0; i < nslots; ++i) { 2300 u32 off = i * code_page; 2301 u32 take = (off + code_page <= file_len_excl_cs) ? code_page 2302 : (file_len_excl_cs - off); 2303 Sha256 s; 2304 sha256_init(&s); 2305 sha256_update(&s, full_file + off, take); 2306 /* Pages shorter than code_page get the standard SHA over the 2307 * partial bytes — Apple's tools do exactly this (no zero padding 2308 * on the tail). */ 2309 sha256_final(&s, hashes + i * CS_SHA256_LEN); 2310 } 2311 } 2312 2313 /* ---- final emission ---- */ 2314 2315 static void emit_load_command_segment(ObjByteBuf* lc, MCtx* x, u32 segidx) { 2316 MSeg* sg = &x->segs[segidx]; 2317 u32 seg_cmd_size = MACHO_SEGCMD64_SIZE + sg->nouts * MACHO_SECT64_SIZE; 2318 u32 base = lc->len; 2319 objbb_u32(lc, LC_SEGMENT_64); 2320 objbb_u32(lc, seg_cmd_size); 2321 /* segname: 16 bytes zero-padded */ 2322 u8 nm[16]; 2323 memset(nm, 0, 16); 2324 size_t nlen = slice_from_cstr(sg->name).len; 2325 if (nlen > 16) nlen = 16; 2326 memcpy(nm, sg->name, nlen); 2327 objbb_append(lc, nm, 16); 2328 objbb_u64(lc, sg->vmaddr); 2329 objbb_u64(lc, sg->vmsize); 2330 objbb_u64(lc, sg->fileoff); 2331 objbb_u64(lc, sg->filesize); 2332 objbb_u32(lc, sg->maxprot); 2333 objbb_u32(lc, sg->initprot); 2334 objbb_u32(lc, sg->nouts); 2335 objbb_u32(lc, 0); /* flags */ 2336 2337 for (u32 j = 0; j < sg->nouts; ++j) { 2338 OutSec* o = &x->outs[sg->first_out + j]; 2339 u8 sname[16], gname[16]; 2340 memset(sname, 0, 16); 2341 memset(gname, 0, 16); 2342 size_t sl = o->sectname ? slice_from_cstr(o->sectname).len : 0; 2343 if (sl > 16) sl = 16; 2344 if (sl) memcpy(sname, o->sectname, sl); 2345 size_t gl = slice_from_cstr(sg->name).len; /* segname must match */ 2346 if (gl > 16) gl = 16; 2347 memcpy(gname, sg->name, gl); 2348 objbb_append(lc, sname, 16); 2349 objbb_append(lc, gname, 16); 2350 objbb_u64(lc, o->vaddr); 2351 objbb_u64(lc, o->size); 2352 objbb_u32(lc, (u32)o->file_offset); 2353 /* align is power of 2; encode as log2. */ 2354 u32 a = o->align ? o->align : 1u; 2355 u32 al = 0; 2356 while ((1u << al) < a) ++al; 2357 objbb_u32(lc, al); 2358 objbb_u32(lc, 0); /* reloff */ 2359 objbb_u32(lc, 0); /* nreloc */ 2360 objbb_u32(lc, o->flags); 2361 objbb_u32(lc, o->reserved1); 2362 objbb_u32(lc, o->reserved2); 2363 objbb_u32(lc, 0); /* reserved3 */ 2364 } 2365 (void)base; 2366 } 2367 2368 void link_emit_macho(LinkImage* img, Writer* w); 2369 2370 void link_emit_macho(LinkImage* img, Writer* w) { 2371 MCtx x; 2372 memset(&x, 0, sizeof(x)); 2373 x.img = img; 2374 x.c = img->c; 2375 x.h = img->heap; 2376 x.w = w; 2377 x.linker = img->linker; 2378 x.link_arch = link_arch_desc_for(img->c); 2379 /* Apple-Silicon arm64 mains must be signed with 16 KiB hash pages (the native 2380 * VM page); x86_64 uses 4 KiB. See CS_PAGE_SIZE_LOG2. */ 2381 x.cs_page_log2 = (img->c->target.arch == KIT_ARCH_ARM_64) ? 14u : 12u; 2382 { 2383 const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_MACHO); 2384 x.macho = 2385 fmt && fmt->macho_arch ? fmt->macho_arch(img->c->target.arch) : NULL; 2386 } 2387 2388 if (!x.link_arch || !x.macho || !x.macho->cputype || !x.macho->emit_stub || 2389 !x.macho->stub_size) 2390 compiler_panic(x.c, SRCLOC_NONE, 2391 "link_emit_macho: no Mach-O descriptor for target"); 2392 if (img->entry_sym == LINK_SYM_NONE) 2393 compiler_panic(x.c, SRCLOC_NONE, "link_emit_macho: no resolved entry"); 2394 2395 collect_imports(&x); 2396 collect_tlv(&x); 2397 plan_layout(&x); 2398 build_section_index(&x); 2399 shift_sections(&x); 2400 2401 /* entry offset within __TEXT segment. */ 2402 LinkSymbol* esym = sym_at(img, img->entry_sym); 2403 if (!esym || !esym->defined) 2404 compiler_panic(x.c, SRCLOC_NONE, "link_emit_macho: entry symbol undefined"); 2405 if (esym->vaddr < x.text_vaddr) 2406 compiler_panic(x.c, SRCLOC_NONE, 2407 "link_emit_macho: entry symbol below __TEXT base"); 2408 x.entry_offset = (u32)(esym->vaddr - x.text_vaddr); 2409 2410 /* image-id UUID. */ 2411 u8 image_id[LINK_IMAGE_ID_BYTES]; 2412 link_image_id_compute(img, image_id); 2413 memcpy(x.uuid, image_id, 16); 2414 2415 /* Reloc apply collects fixsites. */ 2416 FixList fl; 2417 fix_init(&fl, x.h); 2418 apply_relocs(&x, &fl); 2419 2420 /* Build LINKEDIT contents. */ 2421 build_chained_fixups(&x, &fl); 2422 build_exports_trie(&x); 2423 build_symtab(&x); 2424 layout_linkedit(&x); 2425 2426 /* Compute code-sig skeleton sized to file bytes excluding sig. */ 2427 u32 code_limit = x.codesig_off; 2428 build_codesig_skeleton(&x, code_limit, "a.out"); 2429 /* Now extend linkedit segment to include codesig. */ 2430 u64 le_size = (u64)x.codesig_off + (u64)x.codesig_size - x.linkedit_fileoff; 2431 x.segs[MSEG_LINKEDIT].filesize = le_size; 2432 x.segs[MSEG_LINKEDIT].vmsize = ALIGN_UP(le_size, MZ_PAGE); 2433 2434 /* Build load commands buffer. */ 2435 ObjByteBuf lc; 2436 objbb_init(&lc, x.h); 2437 2438 /* LC_SEGMENT_64 for each segment with sections (and PAGEZERO/LINKEDIT). */ 2439 emit_load_command_segment(&lc, &x, 0); /* PAGEZERO */ 2440 emit_load_command_segment(&lc, &x, 1); /* TEXT */ 2441 if (x.segs[2].nsects > 0) 2442 emit_load_command_segment(&lc, &x, 2); /* DATA_CONST */ 2443 if (x.segs[3].nsects > 0) emit_load_command_segment(&lc, &x, 3); /* DATA */ 2444 if (x.segs[MSEG_DWARF].nsects > 0) 2445 emit_load_command_segment(&lc, &x, MSEG_DWARF); /* DWARF (debug info) */ 2446 emit_load_command_segment(&lc, &x, MSEG_LINKEDIT); /* LINKEDIT */ 2447 2448 /* LC_DYLD_CHAINED_FIXUPS (linkedit_data_command: 16B) */ 2449 objbb_u32(&lc, LC_DYLD_CHAINED_FIXUPS); 2450 objbb_u32(&lc, 16); 2451 objbb_u32(&lc, x.chained_fixups_off); 2452 objbb_u32(&lc, x.chained_fixups.len); 2453 2454 /* LC_DYLD_EXPORTS_TRIE */ 2455 objbb_u32(&lc, LC_DYLD_EXPORTS_TRIE); 2456 objbb_u32(&lc, 16); 2457 objbb_u32(&lc, x.exports_trie_off); 2458 objbb_u32(&lc, x.exports_trie.len); 2459 2460 /* LC_SYMTAB */ 2461 objbb_u32(&lc, LC_SYMTAB); 2462 objbb_u32(&lc, MACHO_SYMTAB_CMD_SIZE); 2463 objbb_u32(&lc, x.symtab_off); 2464 objbb_u32(&lc, x.nsyms); 2465 objbb_u32(&lc, x.strtab_off); 2466 objbb_u32(&lc, x.strtab.len); 2467 2468 /* LC_DYSYMTAB */ 2469 /* nlocal=0, nextdef=#defined-globals, nundef=#imports. We tracked 2470 * those during build_symtab; recompute by inspecting strtab... easier 2471 * to recount: defined globals are total - imports. */ 2472 u32 nlocal = 0; 2473 u32 nundef = x.nimports_real; 2474 u32 nextdef = (x.nsyms > nundef) ? x.nsyms - nundef - nlocal : 0; 2475 objbb_u32(&lc, LC_DYSYMTAB); 2476 objbb_u32(&lc, MACHO_DYSYMTAB_CMD_SIZE); 2477 objbb_u32(&lc, 0); /* ilocalsym */ 2478 objbb_u32(&lc, nlocal); 2479 objbb_u32(&lc, nlocal); 2480 objbb_u32(&lc, nextdef); 2481 objbb_u32(&lc, nlocal + nextdef); 2482 objbb_u32(&lc, nundef); 2483 objbb_u32(&lc, 0); 2484 objbb_u32(&lc, 0); /* tocoff, ntoc */ 2485 objbb_u32(&lc, 0); 2486 objbb_u32(&lc, 0); /* modtaboff, nmodtab */ 2487 objbb_u32(&lc, 0); 2488 objbb_u32(&lc, 0); /* extrefsymoff, nextrefsyms */ 2489 objbb_u32(&lc, x.indirect_off); 2490 objbb_u32(&lc, x.indirect.len / 4u); 2491 objbb_u32(&lc, 0); 2492 objbb_u32(&lc, 0); /* extreloff, nextrel */ 2493 objbb_u32(&lc, 0); 2494 objbb_u32(&lc, 0); /* locreloff, nlocrel */ 2495 2496 /* LC_LOAD_DYLINKER */ 2497 { 2498 const char* dyld = "/usr/lib/dyld"; 2499 u32 dyld_len = (u32)slice_from_cstr(dyld).len; 2500 u32 cmd_size = (u32)ALIGN_UP((u64)(12u + dyld_len + 1u), 8u); 2501 objbb_u32(&lc, LC_LOAD_DYLINKER); 2502 objbb_u32(&lc, cmd_size); 2503 objbb_u32(&lc, 12u); /* name offset within cmd */ 2504 u32 wrote = objbb_str(&lc, dyld, dyld_len); 2505 (void)wrote; 2506 /* Pad to cmd_size. */ 2507 while (lc.len < (u32)((u64)objbb_align(&lc, 1) + 0)) { 2508 /* no-op */ 2509 break; 2510 } 2511 /* Re-align to cmd_size. */ 2512 u32 want = (u32)(lc.len); 2513 /* Walk back: lc grew by 12 + (strlen+1). Pad to cmd_size. */ 2514 u32 cmd_start_back = lc.len - (12u + dyld_len + 1u); 2515 u32 pad_needed = cmd_size - (lc.len - cmd_start_back); 2516 while (pad_needed-- > 0) objbb_u8(&lc, 0); 2517 (void)want; 2518 } 2519 2520 /* LC_UUID */ 2521 objbb_u32(&lc, LC_UUID); 2522 objbb_u32(&lc, 24); 2523 objbb_append(&lc, x.uuid, 16); 2524 2525 /* LC_BUILD_VERSION */ 2526 objbb_u32(&lc, LC_BUILD_VERSION); 2527 objbb_u32(&lc, 24); 2528 objbb_u32(&lc, macho_platform_for_target(img->c->target)); 2529 objbb_u32(&lc, (12u << 16) | 0); /* minos 12.0.0 */ 2530 objbb_u32(&lc, (12u << 16) | 0); /* sdk 12.0.0 */ 2531 objbb_u32(&lc, 0); /* ntools */ 2532 2533 /* LC_MAIN — entryoff is offset within __TEXT segment from its file 2534 * start (0). */ 2535 objbb_u32(&lc, LC_MAIN); 2536 objbb_u32(&lc, 24); 2537 objbb_u64(&lc, (u64)x.entry_offset); /* entryoff = vaddr - __TEXT.vmaddr */ 2538 objbb_u64(&lc, 0); /* stacksize */ 2539 2540 /* LC_LOAD_DYLIB per dylib. */ 2541 for (u32 i = 0; i < x.ndylibs; ++i) { 2542 Slice nm_s = pool_slice(x.c->global, x.dylibs[i].install); 2543 const char* nm = nm_s.s; 2544 size_t nl = nm_s.len; 2545 u32 cmd_size = (u32)ALIGN_UP((u64)(24u + (u32)nl + 1u), 8u); 2546 u32 cmd_start = lc.len; 2547 objbb_u32(&lc, LC_LOAD_DYLIB); 2548 objbb_u32(&lc, cmd_size); 2549 objbb_u32(&lc, 24u); /* name offset */ 2550 objbb_u32(&lc, 0); /* timestamp */ 2551 objbb_u32(&lc, (1u << 16)); /* current_version 1.0 */ 2552 objbb_u32(&lc, (1u << 16)); /* compat_version 1.0 */ 2553 objbb_str(&lc, nm ? nm : "", (u32)nl); 2554 while (lc.len - cmd_start < cmd_size) objbb_u8(&lc, 0); 2555 } 2556 2557 /* LC_FUNCTION_STARTS / LC_DATA_IN_CODE */ 2558 objbb_u32(&lc, LC_FUNCTION_STARTS_C); 2559 objbb_u32(&lc, 16); 2560 objbb_u32(&lc, x.fn_starts_off); 2561 objbb_u32(&lc, x.fn_starts.len); 2562 2563 objbb_u32(&lc, LC_DATA_IN_CODE_C); 2564 objbb_u32(&lc, 16); 2565 objbb_u32(&lc, x.data_in_code_off); 2566 objbb_u32(&lc, 0); 2567 2568 /* LC_CODE_SIGNATURE */ 2569 objbb_u32(&lc, LC_CODE_SIGNATURE_C); 2570 objbb_u32(&lc, 16); 2571 objbb_u32(&lc, x.codesig_off); 2572 objbb_u32(&lc, x.codesig_size); 2573 2574 /* Sanity: lc.len + MACHO_HDR64_SIZE must equal headers_size we 2575 * predicted in plan_layout. If not, we mis-sized — panic. */ 2576 if ((u64)lc.len + MACHO_HDR64_SIZE != x.headers_size) { 2577 compiler_panic(x.c, SRCLOC_NONE, 2578 "link_macho: load-cmd size mismatch: predicted %llu got %u", 2579 (unsigned long long)(x.headers_size - MACHO_HDR64_SIZE), 2580 lc.len); 2581 } 2582 2583 /* ---- now stream the file ---- */ 2584 /* The Writer in kit allows seek; we'll write a flat buffer first 2585 * (so we can hash it for codesig) and flush at the end. */ 2586 ObjByteBuf file; 2587 objbb_init(&file, x.h); 2588 2589 /* mach_header_64 */ 2590 u32 ncmds = 0; 2591 /* Recount: PAGEZERO + TEXT + maybe DATA_CONST + maybe DATA + LINKEDIT 2592 * + chained + exports_trie + symtab + dysymtab + dyld + uuid + 2593 * build_version + main + nDylibs + fn_starts + data_in_code + 2594 * codesig. */ 2595 ncmds += 2; /* PAGEZERO + TEXT */ 2596 if (x.segs[2].nsects > 0) ncmds++; 2597 if (x.segs[3].nsects > 0) ncmds++; 2598 if (x.segs[MSEG_DWARF].nsects > 0) ncmds++; /* __DWARF (debug info) */ 2599 ncmds++; /* LINKEDIT */ 2600 ncmds += 11 + x.ndylibs; 2601 /* (chained, exports_trie, symtab, dysymtab, dyld, uuid, build_version, 2602 * main, fn_starts, data_in_code, codesig) = 11 */ 2603 2604 objbb_u32(&file, MH_MAGIC_64); 2605 objbb_u32(&file, x.macho->cputype); 2606 objbb_u32(&file, x.macho->cpusubtype); 2607 objbb_u32(&file, MH_EXECUTE); 2608 objbb_u32(&file, ncmds); 2609 objbb_u32(&file, lc.len); 2610 { 2611 u32 mh_flags = MH_DYLDLINK | MH_TWOLEVEL | MH_NOUNDEFS | MH_PIE; 2612 /* dyld scans __thread_vars and allocates a pthread_key for each 2613 * descriptor only when this flag is set; without it the descriptor's 2614 * thunk pointer is silently patched to _tlv_bootstrap_error. Apple's 2615 * ld sets it whenever the image contains S_THREAD_LOCAL_* sections. */ 2616 if (x.ntlv) mh_flags |= MH_HAS_TLV_DESCRIPTORS; 2617 objbb_u32(&file, mh_flags); 2618 } 2619 objbb_u32(&file, 0); /* reserved */ 2620 objbb_append(&file, lc.data, lc.len); 2621 2622 /* Pad to first section's file offset. */ 2623 /* __TEXT first section begins at headers_size; we wrote header+lc = 2624 * headers_size, so no pad needed. Then each MSec's file_offset 2625 * tells us where to write its bytes. */ 2626 2627 /* Now emit segment payload bytes per MSec. */ 2628 for (u32 i = 0; i < x.nsecs; ++i) { 2629 MSec* m = &x.secs[i]; 2630 if (m->is_zerofill || m->size == 0) continue; 2631 /* Pad up to m->file_offset. */ 2632 while (file.len < m->file_offset) objbb_u8(&file, 0); 2633 if (m->synth_data) { 2634 objbb_append(&file, m->synth_data, m->synth_size); 2635 } else { 2636 LinkSection* ls = &img->sections[m->link_sec_id - 1u]; 2637 u32 segid = ls->segment_id; 2638 u8* base = 2639 (segid != LINK_SEG_NONE) ? img->segment_bytes[segid - 1u] : NULL; 2640 if (base && ls->size) { 2641 objbb_append(&file, base + ls->input_offset, (u32)ls->size); 2642 } else if (ls->size) { 2643 for (u64 k = 0; k < ls->size; ++k) objbb_u8(&file, 0); 2644 } 2645 } 2646 } 2647 2648 /* Pad to LINKEDIT start. */ 2649 while (file.len < x.linkedit_fileoff) objbb_u8(&file, 0); 2650 2651 /* LINKEDIT contents in declared order. */ 2652 while (file.len < x.chained_fixups_off) objbb_u8(&file, 0); 2653 objbb_append(&file, x.chained_fixups.data, x.chained_fixups.len); 2654 while (file.len < x.exports_trie_off) objbb_u8(&file, 0); 2655 objbb_append(&file, x.exports_trie.data, x.exports_trie.len); 2656 while (file.len < x.fn_starts_off) objbb_u8(&file, 0); 2657 objbb_append(&file, x.fn_starts.data, x.fn_starts.len); 2658 while (file.len < x.data_in_code_off) objbb_u8(&file, 0); 2659 /* empty */ 2660 while (file.len < x.symtab_off) objbb_u8(&file, 0); 2661 objbb_append(&file, x.symtab.data, x.symtab.len); 2662 while (file.len < x.indirect_off) objbb_u8(&file, 0); 2663 objbb_append(&file, x.indirect.data, x.indirect.len); 2664 while (file.len < x.strtab_off) objbb_u8(&file, 0); 2665 objbb_append(&file, x.strtab.data, x.strtab.len); 2666 while (file.len < x.codesig_off) objbb_u8(&file, 0); 2667 2668 /* Compute codesig hashes over file bytes [0, codesig_off). */ 2669 /* The codesig blob currently has zero hashes; hash now. */ 2670 compute_codesig(&x, file.data, x.codesig_off, "a.out"); 2671 /* Append codesig. */ 2672 objbb_append(&file, x.codesig.data, x.codesig.len); 2673 2674 /* Stream out. */ 2675 kit_writer_seek(w, 0); 2676 kit_writer_write(w, file.data, file.len); 2677 2678 /* Cleanup. */ 2679 fix_fini(&fl); 2680 objbb_fini(&lc); 2681 objbb_fini(&file); 2682 objbb_fini(&x.chained_fixups); 2683 objbb_fini(&x.exports_trie); 2684 objbb_fini(&x.symtab); 2685 objbb_fini(&x.strtab); 2686 objbb_fini(&x.indirect); 2687 objbb_fini(&x.fn_starts); 2688 objbb_fini(&x.data_in_code); 2689 objbb_fini(&x.codesig); 2690 if (x.imports) x.h->free(x.h, x.imports, 0); /* VEC_GROW: cap unknown */ 2691 if (x.dylibs) x.h->free(x.h, x.dylibs, 0); 2692 if (x.sym_to_imp) 2693 x.h->free(x.h, x.sym_to_imp, sizeof(u32) * x.sym_to_imp_size); 2694 if (x.secs) x.h->free(x.h, x.secs, 0); 2695 if (x.by_link_sec) 2696 x.h->free(x.h, x.by_link_sec, sizeof(MSec*) * x.by_link_sec_cap); 2697 if (x.stubs_bytes) x.h->free(x.h, x.stubs_bytes, x.stubs_size); 2698 if (x.got_bytes) x.h->free(x.h, x.got_bytes, x.got_size); 2699 if (x.tlv_ptrs_bytes) x.h->free(x.h, x.tlv_ptrs_bytes, x.tlv_ptrs_size); 2700 if (x.tlv_slots) x.h->free(x.h, x.tlv_slots, 0); 2701 if (x.sym_to_tlv) 2702 x.h->free(x.h, x.sym_to_tlv, sizeof(u32) * x.sym_to_tlv_size); 2703 }