link.c (73398B)
1 /* link_emit_coff: write a PE32+ MH_EXECUTABLE-style image to the 2 * caller-provided Writer. 3 * 4 * Phase 3.1 deliverable per doc/OBJ.md: skeleton + base-reloc 5 * handling for the four standard PE sections. Import-table synthesis 6 * (.idata / IAT) lands in Phase 3.2; per-arch IAT stub bytes in 3.3; 7 * TLS directory in 3.5; debug directory in 3.6 — those code paths 8 * panic loudly here so the strict-by-default posture surfaces them. 9 * 10 * File layout (in write order): 11 * 12 * [DOS stub IMAGE_DOS_HEADER] -- 64 bytes; e_lfanew=0x40 13 * [PE signature "PE\0\0"] -- 4 bytes 14 * [IMAGE_FILE_HEADER] -- 20 bytes 15 * [IMAGE_OPTIONAL_HEADER64] -- 240 bytes (PE32+) 16 * [IMAGE_SECTION_HEADER * nsec] -- 40 bytes each 17 * [pad to FileAlignment] 18 * [.text bytes, padded to FileAlignment] 19 * [.rdata bytes, padded to FileAlignment] 20 * [.data bytes, padded to FileAlignment] 21 * [.reloc bytes, padded to FileAlignment] 22 * 23 * .bss is uninitialized — it has a section header (with VirtualSize) 24 * but no file bytes and PointerToRawData=0. 25 * 26 * RVAs follow SectionAlignment (0x1000); FileAlignment is 0x200; the 27 * first section starts at RVA 0x1000 (right after the headers map). 28 * ImageBase is the Win64 convention 0x140000000. 29 * 30 * Reloc strategy. The link layout pass has already placed every kept 31 * input section into img->sections / img->segments under the ELF/Mach-O 32 * coordinate system (image-relative vaddrs, often packed by permission 33 * bucket). COFF wants a different packing — the four standard 34 * sections at SectionAlignment-aligned RVAs — so this writer re-derives 35 * per-input-section vaddrs from scratch and shifts each LinkSection / 36 * symbol / LinkRelocApply by its section's per-section delta before 37 * applying relocations. link_emit_macho takes the same tack for its 38 * __DATA_CONST splits; the ELF writer leaves vaddrs alone because the 39 * link layout already matches ELF's PT_LOAD shape. */ 40 41 #include "link/link.h" 42 43 #include <stdlib.h> 44 #include <string.h> 45 46 #include "core/core.h" 47 #include "core/heap.h" 48 #include "core/pool.h" 49 #include "core/slice.h" 50 #include "core/util.h" 51 #include "core/vec.h" 52 #include "link/link_internal.h" 53 #include "obj/coff/coff.h" 54 #include "obj/format.h" 55 56 /* ---- .idata layout constants ---- 57 * 58 * Per doc/OBJ.md: the .idata content is a concatenation of an 59 * IMAGE_IMPORT_DESCRIPTOR table (NULL-terminated), one ILT per DLL 60 * (each NULL-terminated u64 array), one IAT per DLL (same shape), 61 * a hint/name table, and a DLL-name string pool. Each block is 62 * pointer-sized aligned within the section. AArch64 import thunks use 63 * PAGEOFFSET_12L for 64-bit ILT/IAT slots, so those sub-blocks must be 64 * 8-byte aligned. */ 65 #define PE_IDATA_BLOCK_ALIGN 8u 66 /* Hint field on IMAGE_IMPORT_BY_NAME records. kit never has a real 67 * hint (the OS loader doesn't need one to do the bsearch on the DLL's 68 * export name table), so 0 is the canonical "no hint" value. */ 69 #define PE_IMPORT_HINT_NONE 0u 70 71 /* ---- PE/Win64 layout constants ---- 72 * 73 * Centralised here so the wire-format numbers in this TU stay named 74 * (and the magic-numbers rule in CLAUDE.md is honoured). Values match 75 * the PE/COFF spec + Win64 conventions; mingw-w64's ld defaults agree. */ 76 #define PE_IMAGE_BASE LINK_PE_IMAGE_BASE 77 #define PE_SECTION_ALIGNMENT 0x1000u 78 #define PE_FILE_ALIGNMENT 0x200u 79 #define PE_FIRST_SECTION_RVA 0x1000u 80 #define PE_DOS_E_LFANEW 0x40u 81 #define PE_NUM_DATA_DIRS COFF_NUM_DATA_DIRECTORIES 82 #define PE_OPT_HDR_SIZE COFF_OPT_HDR64_SIZE 83 #define PE_LINKER_MAJOR 0u 84 #define PE_LINKER_MINOR 1u 85 #define PE_OS_MAJOR 6u /* Windows Vista+ — mingw default */ 86 #define PE_OS_MINOR 0u 87 #define PE_SUBSYS_MAJOR 6u 88 #define PE_SUBSYS_MINOR 0u 89 #define PE_STACK_RESERVE 0x100000ULL 90 #define PE_STACK_COMMIT 0x1000ULL 91 #define PE_HEAP_RESERVE 0x100000ULL 92 #define PE_HEAP_COMMIT 0x1000ULL 93 /* DllCharacteristics bits that apply regardless of relocatability. */ 94 #define PE_DLL_CHARS_BASE \ 95 (IMAGE_DLLCHARACTERISTICS_NX_COMPAT | \ 96 IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) 97 /* ASLR bits — only valid when the image carries base relocations (PIE). 98 * Advertising DYNAMIC_BASE / HIGH_ENTROPY_VA alongside RELOCS_STRIPPED is 99 * contradictory: the loader has nothing to fix up, so it can't relocate. */ 100 #define PE_DLL_CHARS_ASLR \ 101 (IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | \ 102 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) 103 104 /* PE32+ DOS-stub-to-PE-signature offsets (manual, since we marshal 105 * field-by-field rather than memcpy'ing the packed struct). */ 106 #define PE_DOS_HDR_SIZE COFF_DOS_HEADER_SIZE 107 #define PE_SIG_SIZE 4u 108 #define PE_FILE_HDR_SIZE COFF_FILE_HEADER_SIZE 109 #define PE_SECTION_HDR_SIZE COFF_SECTION_HEADER_SIZE 110 111 /* Standard PE output buckets, plus .idata (import directory) and 112 * .reloc — both synthesised here rather than copied from input 113 * sections. Order matters: it's the on-image RVA order. */ 114 typedef enum CoffBucket { 115 COFF_BUCKET_TEXT = 0, 116 COFF_BUCKET_RDATA = 1, 117 COFF_BUCKET_IDATA = 2, 118 COFF_BUCKET_DATA = 3, 119 COFF_BUCKET_TLS = 4, 120 COFF_BUCKET_BSS = 5, 121 COFF_BUCKET_PDATA = 6, 122 COFF_BUCKET_RELOC = 7, 123 COFF_NBUCKETS = 8, 124 } CoffBucket; 125 126 /* IMAGE_TLS_DIRECTORY64 wire size: u64*4 + u32*2 = 40 bytes. */ 127 #define COFF_TLS_DIRECTORY64_SIZE 40u 128 /* Byte offsets of the four u64 VA fields within IMAGE_TLS_DIRECTORY64 129 * — they need base relocations so ASLR can fix them up. */ 130 #define COFF_TLSDIR_OFF_START_ADDR 0u 131 #define COFF_TLSDIR_OFF_END_ADDR 8u 132 #define COFF_TLSDIR_OFF_INDEX_ADDR 16u 133 #define COFF_TLSDIR_OFF_CALLBACKS 24u 134 135 typedef struct CoffSection { 136 const char* name; /* short ASCII; <= 8 bytes including NUL pad */ 137 u32 characteristics; 138 u8* bytes; /* NULL for .bss / .reloc-before-build */ 139 u32 size; /* VirtualSize (real bytes; for .bss, mem size) */ 140 u32 size_raw; /* SizeOfRawData (file size, FileAlignment-padded) */ 141 u32 rva; /* VirtualAddress in image */ 142 u32 file_offset; /* PointerToRawData; 0 for .bss */ 143 u8 in_image; /* 1 if this bucket is emitted as a section */ 144 u8 has_file_bytes; /* 0 for .bss */ 145 u8 pad[2]; 146 } CoffSection; 147 148 /* ---- byte writer helpers ---- */ 149 150 static void coff_write_zeroes(Writer* w, u64 n) { 151 static const u8 zeroes[256] = {0}; 152 while (n) { 153 u64 step = n > sizeof(zeroes) ? sizeof(zeroes) : n; 154 kit_writer_write(w, zeroes, (size_t)step); 155 n -= step; 156 } 157 } 158 159 /* Return the COFF bucket for a kept LinkSection. SF_TLS sections route 160 * into the dedicated .tls bucket so SECREL relocations from TLS access 161 * code resolve against the merged TLS image, not against .data. 162 * Everything else partitions on SF_EXEC / SF_WRITE plus the SSEM_NOBITS 163 * bit for .bss. */ 164 static CoffBucket coff_bucket_for(Compiler* c, const LinkSection* ls) { 165 if (ls->name) { 166 Slice nm = pool_slice(c->global, ls->name); 167 if (nm.s && nm.len >= 6u && memcmp(nm.s, ".pdata", 6u) == 0) 168 return COFF_BUCKET_PDATA; 169 } 170 if (ls->flags & SF_EXEC) return COFF_BUCKET_TEXT; 171 if (ls->flags & SF_TLS) return COFF_BUCKET_TLS; 172 if (ls->sem == SSEM_NOBITS) return COFF_BUCKET_BSS; 173 if (ls->flags & SF_WRITE) return COFF_BUCKET_DATA; 174 return COFF_BUCKET_RDATA; 175 } 176 177 /* True for relocation kinds that need an entry in .reloc so the OS 178 * loader can patch the site after ASLR picks a runtime ImageBase. 179 * PC-relative fixups don't need base-relocs — the displacement is 180 * load-invariant. */ 181 static int coff_reloc_needs_base_reloc(RelocKind k) { 182 return k == R_ABS64 || k == R_ABS32; 183 } 184 185 /* Look up the LinkSection whose [vaddr, vaddr+size] range covers the 186 * given image-relative address `v`, or return NULL. Used to attribute 187 * symbol vaddrs to a containing section so we can apply per-section 188 * vaddr deltas after re-laying out for PE. */ 189 static const LinkSection* coff_section_at(const LinkImage* img, u64 v) { 190 u32 i; 191 for (i = 0; i < img->nsections; ++i) { 192 const LinkSection* ls = &img->sections[i]; 193 if (v >= ls->vaddr && v <= ls->vaddr + ls->size) return ls; 194 } 195 return NULL; 196 } 197 198 /* Per-input-section delta map. Indexed by `LinkSection.id - 1`. 199 * Populated by coff_build_buckets. Consumed by every subsequent pass 200 * that needs to translate input-coordinate offsets (the world that 201 * img->sections / img->relocs live in) into PE-coordinate ones (where 202 * the writer plants bytes). delta is stored explicitly so callers 203 * avoid recomputing (new_rva + bucket.rva - old_vaddr) for every 204 * LinkRelocApply whose link_section_id points at the section. */ 205 typedef struct CoffSecMap { 206 u32 new_rva; /* image-relative RVA after PE relayout */ 207 u32 new_file_off; /* file offset of the patched byte */ 208 i64 delta; /* new_rva - old_vaddr */ 209 u8 bucket; 210 u8 pad[3]; 211 } CoffSecMap; 212 213 /* TLS directory placement state. Populated when at least one SF_TLS 214 * section survives dead-strip; consumed by the optional-header writer, 215 * the .reloc builder (base-relocs for the four absolute VA fields), 216 * and the .rdata emit pass that writes the final 40-byte record. */ 217 typedef struct CoffTlsLayout { 218 int present; /* 1 iff at least one TLS section was kept */ 219 u32 dir_rdata_off; /* byte offset of the IMAGE_TLS_DIRECTORY64 within .rdata 220 */ 221 u32 tls_size; /* size of the merged .tls bucket */ 222 LinkSymId tls_index_sym; /* resolved _tls_index LinkSymbol */ 223 LinkSymId callbacks_sym; /* __xl_a when mingw's TLS callbacks are linked */ 224 u64 callbacks_addend; /* mingw points past the leading NULL sentinel */ 225 } CoffTlsLayout; 226 227 static LinkSymId coff_find_sym(LinkImage* img, const char* name) { 228 Sym sym = pool_intern_cstr(img->c->global, name); 229 u32 n = LinkSyms_count(&img->syms); 230 u32 i; 231 for (i = 0; i < n; ++i) { 232 const LinkSymbol* s = LinkSyms_at(&img->syms, i); 233 if (s->name == sym) return (LinkSymId)(i + 1); 234 } 235 return LINK_SYM_NONE; 236 } 237 238 /* Locate _tls_index by name in the resolved symbol table. mingw's 239 * libmingwex defines it (as part of tlsmcrt); without a CRT the link 240 * fails here with a clear message rather than producing a TLS 241 * directory pointing at a stale address. */ 242 static LinkSymId coff_find_tls_index_sym(LinkImage* img) { 243 return coff_find_sym(img, "_tls_index"); 244 } 245 246 static const LinkSection* coff_symbol_section(const LinkImage* img, 247 const LinkSymbol* s) { 248 if (s->name) { 249 Slice nm_s = pool_slice(img->c->global, s->name); 250 const char* nm = nm_s.s; 251 size_t n = nm_s.len; 252 const char* sec_name = NULL; 253 if (nm && n == 6 && memcmp(nm, "__xd_a", 6) == 0) 254 sec_name = ".CRT$XDA"; 255 else if (nm && n == 6 && memcmp(nm, "__xd_z", 6) == 0) 256 sec_name = ".CRT$XDZ"; 257 else if (nm && n == 6 && memcmp(nm, "__xl_a", 6) == 0) 258 sec_name = ".CRT$XLA"; 259 else if (nm && n == 6 && memcmp(nm, "__xl_c", 6) == 0) 260 sec_name = ".CRT$XLC"; 261 else if (nm && n == 6 && memcmp(nm, "__xl_d", 6) == 0) 262 sec_name = ".CRT$XLD"; 263 else if (nm && n == 6 && memcmp(nm, "__xl_z", 6) == 0) 264 sec_name = ".CRT$XLZ"; 265 if (sec_name) { 266 u32 i; 267 for (i = 0; i < img->nsections; ++i) { 268 const LinkSection* ls = &img->sections[i]; 269 if (ls->name && 270 slice_eq_cstr(pool_slice(img->c->global, ls->name), sec_name)) 271 return ls; 272 } 273 } 274 } 275 if (s->section_id != LINK_SEC_NONE && s->section_id <= img->nsections) 276 return &img->sections[s->section_id - 1]; 277 return coff_section_at(img, s->vaddr); 278 } 279 280 static u64 coff_symbol_final_va(const LinkImage* img, 281 const CoffSection out[COFF_NBUCKETS], 282 const CoffSecMap* map, LinkSymId id, 283 const char* what) { 284 const LinkSymbol* s = LinkSyms_at(&img->syms, id - 1); 285 if (!s->defined || s->kind == SK_ABS) { 286 compiler_panic(img->c, SRCLOC_NONE, 287 "link_emit_coff: `%.*s` is not a defined section-bound " 288 "symbol", 289 SLICE_ARG(slice_from_cstr(what))); 290 } 291 const LinkSection* sec = coff_symbol_section(img, s); 292 if (!sec) { 293 compiler_panic(img->c, SRCLOC_NONE, 294 "link_emit_coff: `%.*s` has no containing section", 295 SLICE_ARG(slice_from_cstr(what))); 296 } 297 u8 b = map[sec->id - 1].bucket; 298 return PE_IMAGE_BASE + (u64)out[b].rva + (u64)map[sec->id - 1].new_rva + 299 (s->vaddr - sec->vaddr); 300 } 301 302 /* Reserve 40 bytes at the tail of the .rdata bucket for the 303 * IMAGE_TLS_DIRECTORY64 record. Records the offset for later emit and 304 * grows the bucket if needed. The bytes start zeroed; coff_emit_tls_dir 305 * fills them in once final RVAs are known. */ 306 static void coff_plan_tls_layout(LinkImage* img, CoffSection out[COFF_NBUCKETS], 307 u32* rdata_cap, CoffTlsLayout* tls) { 308 memset(tls, 0, sizeof(*tls)); 309 if (out[COFF_BUCKET_TLS].size == 0) return; 310 tls->present = 1; 311 tls->tls_size = out[COFF_BUCKET_TLS].size; 312 tls->tls_index_sym = coff_find_tls_index_sym(img); 313 if (tls->tls_index_sym == LINK_SYM_NONE) { 314 compiler_panic(img->c, SRCLOC_NONE, 315 "link_emit_coff: .tls section requires `_tls_index` " 316 "(provided by mingw libmingwex / tlsmcrt.o) — none of " 317 "the linked inputs define it"); 318 } 319 /* IMAGE_TLS_DIRECTORY64 needs 8-byte alignment for its u64 fields; 320 * round the .rdata size up before reserving the 40-byte record. */ 321 tls->callbacks_sym = coff_find_sym(img, "__xl_a"); 322 if (tls->callbacks_sym != LINK_SYM_NONE) { 323 tls->callbacks_addend = 8; 324 } else { 325 tls->callbacks_sym = coff_find_sym(img, "__xl_c"); 326 tls->callbacks_addend = 0; 327 } 328 u32 rdata_size = (u32)ALIGN_UP((u64)out[COFF_BUCKET_RDATA].size, 8ull); 329 u32 need = rdata_size + COFF_TLS_DIRECTORY64_SIZE; 330 if (need > *rdata_cap) { 331 (void)VEC_GROW(img->heap, out[COFF_BUCKET_RDATA].bytes, *rdata_cap, need); 332 } 333 /* Zero any padding bytes introduced by the alignment bump and the 334 * directory slot itself. */ 335 if (rdata_size > out[COFF_BUCKET_RDATA].size) { 336 memset(out[COFF_BUCKET_RDATA].bytes + out[COFF_BUCKET_RDATA].size, 0, 337 rdata_size - out[COFF_BUCKET_RDATA].size); 338 } 339 memset(out[COFF_BUCKET_RDATA].bytes + rdata_size, 0, 340 COFF_TLS_DIRECTORY64_SIZE); 341 tls->dir_rdata_off = rdata_size; 342 out[COFF_BUCKET_RDATA].size = need; 343 } 344 345 /* Write the IMAGE_TLS_DIRECTORY64 bytes once all bucket RVAs are 346 * final. Each u64 VA field gets ImageBase + RVA; the base-reloc pass 347 * will emit IMAGE_REL_BASED_DIR64 entries so ASLR keeps them valid. */ 348 static void coff_emit_tls_dir(const LinkImage* img, 349 const CoffSection out[COFF_NBUCKETS], 350 const CoffSecMap* map, const CoffTlsLayout* tls) { 351 if (!tls->present) return; 352 u64 tls_start = PE_IMAGE_BASE + (u64)out[COFF_BUCKET_TLS].rva; 353 u64 tls_end = tls_start + (u64)tls->tls_size; 354 u64 idx_vaddr = 355 coff_symbol_final_va(img, out, map, tls->tls_index_sym, "_tls_index"); 356 const char* callbacks_name = tls->callbacks_addend ? "__xl_a" : "__xl_c"; 357 u64 callbacks_vaddr = 358 tls->callbacks_sym 359 ? coff_symbol_final_va(img, out, map, tls->callbacks_sym, 360 callbacks_name) + 361 tls->callbacks_addend 362 : 0; 363 364 u8* p = out[COFF_BUCKET_RDATA].bytes + tls->dir_rdata_off; 365 wr_u64_le(p + COFF_TLSDIR_OFF_START_ADDR, tls_start); 366 wr_u64_le(p + COFF_TLSDIR_OFF_END_ADDR, tls_end); 367 wr_u64_le(p + COFF_TLSDIR_OFF_INDEX_ADDR, idx_vaddr); 368 wr_u64_le(p + COFF_TLSDIR_OFF_CALLBACKS, callbacks_vaddr); 369 wr_u32_le(p + 32, 0); /* SizeOfZeroFill */ 370 wr_u32_le(p + 36, 0); /* Characteristics */ 371 } 372 373 static void coff_define_tls_used(LinkImage* img, 374 const CoffSection out[COFF_NBUCKETS], 375 const CoffTlsLayout* tls) { 376 if (!tls->present) return; 377 if (!img->linker) return; 378 link_emit_boundary_sym(img->linker, img, "_tls_used", 379 PE_IMAGE_BASE + (u64)out[COFF_BUCKET_RDATA].rva + 380 (u64)tls->dir_rdata_off); 381 } 382 383 /* ---- import-table synthesis (Phase 3.2) --------------------------- 384 * 385 * Per doc/OBJ.md: every LinkSymbol with `imported = 1` gets 386 * routed through an IAT slot synthesized in `.idata`. Function 387 * imports additionally receive a small per-arch stub in `.text` 388 * (`ff 25 disp32` on x64 / `adrp;ldr;br` on aa64) so a direct CALL26 389 * or PC32 against the symbol lands on a stub that indirects through 390 * the IAT. Data imports skip the stub — the symbol's final vaddr is 391 * just the IAT slot vaddr, and code-gen emits a `mov rax, [slot]` 392 * sequence the same way it would for any other GOT-style load. 393 * 394 * kit's COFF code-gen uses direct symbol references; there is no 395 * separate `__imp_<name>` LinkSymbol consulted at link time. The 396 * IAT-slot rewrite happens entirely by overriding the imported 397 * symbol's vaddr in apply_all_relocs. */ 398 399 typedef struct CoffImport { 400 LinkSymId sym; /* canonical LinkSymId from img->syms */ 401 Sym import_name; /* DLL export name override (short-import NameType); 0=use 402 sym */ 403 u32 dll_idx; /* index into CoffImportTable.dlls */ 404 u32 stub_off; /* offset in .text bucket (functions only) */ 405 u32 iat_off; /* offset in .idata IAT block */ 406 u32 ilt_off; /* offset in .idata ILT block */ 407 u32 hint_off; /* offset in .idata hint/name table */ 408 u8 is_func; 409 u8 pad[3]; 410 } CoffImport; 411 412 typedef struct CoffImportDll { 413 Sym soname; 414 u32 first; /* index of first import in CoffImportTable.imports */ 415 u32 count; 416 u32 ilt_off; /* offset of this DLL's ILT block in .idata */ 417 u32 iat_off; /* offset of this DLL's IAT block in .idata */ 418 u32 name_off; /* offset of DLL name string in .idata */ 419 } CoffImportDll; 420 421 typedef struct CoffImportTable { 422 CoffImport* imports; 423 u32 nimports; 424 u32 imports_cap; /* heap-allocation size for cleanup */ 425 u32 nfunc_imports; /* subset of nimports that needs a .text stub */ 426 CoffImportDll* dlls; 427 u32 ndlls; 428 u32 dlls_cap; /* heap-allocation size for cleanup */ 429 /* Offsets within .idata of the five sub-blocks. Filled in by 430 * coff_plan_idata_layout once nimports / ndlls is known. */ 431 u32 desc_off; /* always 0 — descriptors come first */ 432 u32 desc_size; 433 u32 ilt_base; 434 u32 ilt_total; 435 u32 iat_base; 436 u32 iat_total; 437 u32 hint_base; 438 u32 hint_total; 439 u32 name_base; 440 u32 name_total; 441 u32 idata_size; 442 /* Stub region in .text bucket. Stubs are appended after every 443 * input .text section has been bucketed. stub_text_off is the 444 * bucket-local offset of the first stub; per-import stub offsets 445 * are stored in CoffImport.stub_off. */ 446 u32 stub_text_off; 447 u32 stub_total; 448 } CoffImportTable; 449 450 /* Sort comparator: imports grouped by DLL slot, stable on input 451 * order within a DLL (sort is stable enough via secondary key). */ 452 static int coff_import_cmp(const void* a, const void* b) { 453 const CoffImport* ia = (const CoffImport*)a; 454 const CoffImport* ib = (const CoffImport*)b; 455 if (ia->dll_idx < ib->dll_idx) return -1; 456 if (ia->dll_idx > ib->dll_idx) return 1; 457 /* Secondary: LinkSymId so the order is reproducible. */ 458 if (ia->sym < ib->sym) return -1; 459 if (ia->sym > ib->sym) return 1; 460 return 0; 461 } 462 463 static const char* coff_import_lookup_name(Compiler* c, const LinkSymbol* s, 464 size_t* nlen_out) { 465 Slice nm_s = s->name ? pool_slice(c->global, s->name) : SLICE_NULL; 466 const char* nm = nm_s.s; 467 size_t nlen = nm_s.len; 468 static const char kImpPrefix[] = "__imp_"; 469 const size_t kImpPrefixLen = sizeof(kImpPrefix) - 1u; 470 if (nm && nlen > kImpPrefixLen && 471 memcmp(nm, kImpPrefix, kImpPrefixLen) == 0) { 472 nm += kImpPrefixLen; 473 nlen -= kImpPrefixLen; 474 } 475 if (nlen_out) *nlen_out = nlen; 476 return nm; 477 } 478 479 /* The name placed in the PE hint/name table for an import. Honors the 480 * short-import NameType override (CoffImport.import_name, e.g. EXPORTAS's real 481 * DLL export name) when present, else derives it from the symbol name. */ 482 static const char* coff_import_emit_name(Compiler* c, const CoffImport* imp, 483 const LinkSymbol* s, 484 size_t* nlen_out) { 485 if (imp->import_name) { 486 Slice nm_s = pool_slice(c->global, imp->import_name); 487 if (nlen_out) *nlen_out = nm_s.len; 488 return nm_s.s; 489 } 490 return coff_import_lookup_name(c, s, nlen_out); 491 } 492 493 /* True iff the import classifies as function-like. Mirrors the ELF 494 * `sym_is_func_import` heuristic: if the canonical kind is known 495 * we trust it, otherwise we default to function (which matches the 496 * COFF code-gen contract — direct calls are by far the common case 497 * and a data import wrongly stubbed would still fail loudly via the 498 * IAT-routed call). */ 499 static int coff_import_is_func(Compiler* c, const LinkSymbol* s) { 500 if (s->name) { 501 Slice nm_s = pool_slice(c->global, s->name); 502 const char* nm = nm_s.s; 503 size_t nlen = nm_s.len; 504 if (nm && nlen > 6u && memcmp(nm, "__imp_", 6u) == 0) return 0; 505 } 506 if (s->kind == SK_FUNC || s->kind == SK_IFUNC) return 1; 507 if (s->kind == SK_OBJ) return 0; 508 /* SK_UNDEF / SK_NOTYPE: assume function (the common case). */ 509 return 1; 510 } 511 512 /* Walk LinkSyms, collect imports, group by DLL soname. Returns 1 if 513 * any imports were collected, 0 otherwise (caller skips the entire 514 * .idata path). */ 515 static int coff_collect_imports(LinkImage* img, CoffImportTable* it) { 516 Heap* heap = img->heap; 517 Compiler* c = img->c; 518 Linker* l = img->linker; 519 u32 nsyms = LinkSyms_count(&img->syms); 520 u32 imp_cap = 0; 521 u32 dll_cap = 0; 522 u32 i; 523 524 memset(it, 0, sizeof(*it)); 525 if (!l) return 0; 526 for (i = 0; i < nsyms; ++i) { 527 LinkSymbol* s = LinkSyms_at(&img->syms, i); 528 LinkInput* in; 529 u32 dll_idx = (u32)-1; 530 u32 d; 531 if (!s->imported) continue; 532 if (s->name == 0) continue; 533 if (s->dso_input_id == LINK_INPUT_NONE) { 534 compiler_panic(c, SRCLOC_NONE, 535 "link_emit_coff: imported symbol has no providing DSO"); 536 } 537 /* img->globals only carries defined globals/weaks; imported undefs 538 * never land there. Dedup by name: skip if any earlier slot 539 * already collected this name. */ 540 { 541 int dup = 0; 542 for (u32 k = 0; k < it->nimports; ++k) { 543 LinkSymbol* prev = LinkSyms_at(&img->syms, it->imports[k].sym - 1); 544 if (prev->name == s->name) { 545 dup = 1; 546 break; 547 } 548 } 549 if (dup) continue; 550 } 551 if (s->dso_input_id - 1u >= LinkInputs_count(&l->inputs)) { 552 compiler_panic(c, SRCLOC_NONE, 553 "link_emit_coff: import dso_input_id out of range"); 554 } 555 in = LinkInputs_at(&l->inputs, s->dso_input_id - 1u); 556 if (in->soname == 0) { 557 compiler_panic(c, SRCLOC_NONE, 558 "link_emit_coff: providing DSO has no soname; cannot " 559 "emit IMAGE_IMPORT_DESCRIPTOR.Name"); 560 } 561 /* Find-or-add the DLL slot. */ 562 for (d = 0; d < it->ndlls; ++d) { 563 if (it->dlls[d].soname == in->soname) { 564 dll_idx = d; 565 break; 566 } 567 } 568 if (dll_idx == (u32)-1) { 569 if (VEC_GROW(heap, it->dlls, dll_cap, it->ndlls + 1u)) 570 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom on import dlls"); 571 dll_idx = it->ndlls++; 572 memset(&it->dlls[dll_idx], 0, sizeof(it->dlls[dll_idx])); 573 it->dlls[dll_idx].soname = in->soname; 574 } 575 if (VEC_GROW(heap, it->imports, imp_cap, it->nimports + 1u)) 576 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom on imports"); 577 memset(&it->imports[it->nimports], 0, sizeof(it->imports[it->nimports])); 578 it->imports[it->nimports].sym = s->id; 579 it->imports[it->nimports].import_name = in->coff_import_name; 580 it->imports[it->nimports].dll_idx = dll_idx; 581 it->imports[it->nimports].is_func = (u8)coff_import_is_func(c, s); 582 if (it->imports[it->nimports].is_func) ++it->nfunc_imports; 583 ++it->nimports; 584 it->dlls[dll_idx].count++; 585 } 586 if (it->nimports == 0) return 0; 587 /* Re-bucket the imports array so each DLL's run is contiguous. */ 588 qsort(it->imports, it->nimports, sizeof(*it->imports), coff_import_cmp); 589 /* Fix up CoffImportDll.first now that imports[] is sorted. */ 590 { 591 u32 cur = 0; 592 for (u32 d = 0; d < it->ndlls; ++d) { 593 it->dlls[d].first = cur; 594 cur += it->dlls[d].count; 595 } 596 } 597 it->imports_cap = imp_cap; 598 it->dlls_cap = dll_cap; 599 return 1; 600 } 601 602 static void coff_imports_free(LinkImage* img, CoffImportTable* it) { 603 Heap* heap = img->heap; 604 if (it->imports) { 605 heap->free(heap, it->imports, 606 (size_t)it->imports_cap * sizeof(*it->imports)); 607 } 608 if (it->dlls) { 609 heap->free(heap, it->dlls, (size_t)it->dlls_cap * sizeof(*it->dlls)); 610 } 611 } 612 613 /* Compute every per-block / per-import offset inside .idata and the 614 * total .idata size in bytes. Also assigns per-import hint/name and 615 * dll-name offsets so the descriptor table can reference them by RVA 616 * later (RVAs need the bucket's final RVA, added in coff_emit_idata). */ 617 static void coff_plan_idata_layout(LinkImage* img, CoffImportTable* it) { 618 Compiler* c = img->c; 619 u32 off; 620 621 /* Block 1: import descriptors (one per DLL + zero terminator). */ 622 it->desc_off = 0; 623 it->desc_size = (it->ndlls + 1u) * COFF_IMPORT_DESCRIPTOR_SIZE; 624 off = (u32)ALIGN_UP((u64)it->desc_size, (u64)PE_IDATA_BLOCK_ALIGN); 625 626 /* Block 2: ILTs. Per DLL: count entries + 1 (terminator), 8 B each. */ 627 it->ilt_base = off; 628 for (u32 d = 0; d < it->ndlls; ++d) { 629 it->dlls[d].ilt_off = off; 630 /* Per-import: assign ilt_off within this DLL's block. */ 631 for (u32 k = 0; k < it->dlls[d].count; ++k) { 632 it->imports[it->dlls[d].first + k].ilt_off = 633 off + k * (u32)COFF_THUNK_DATA64_SIZE; 634 } 635 off += (it->dlls[d].count + 1u) * (u32)COFF_THUNK_DATA64_SIZE; 636 } 637 it->ilt_total = off - it->ilt_base; 638 off = (u32)ALIGN_UP((u64)off, (u64)PE_IDATA_BLOCK_ALIGN); 639 640 /* Block 3: IATs (same shape as ILTs). */ 641 it->iat_base = off; 642 for (u32 d = 0; d < it->ndlls; ++d) { 643 it->dlls[d].iat_off = off; 644 for (u32 k = 0; k < it->dlls[d].count; ++k) { 645 it->imports[it->dlls[d].first + k].iat_off = 646 off + k * (u32)COFF_THUNK_DATA64_SIZE; 647 } 648 off += (it->dlls[d].count + 1u) * (u32)COFF_THUNK_DATA64_SIZE; 649 } 650 it->iat_total = off - it->iat_base; 651 off = (u32)ALIGN_UP((u64)off, (u64)PE_IDATA_BLOCK_ALIGN); 652 653 /* Block 4: hint/name records. Each: u16 hint + NUL-term name + 654 * 1-byte pad if the resulting size is odd (PE/COFF spec). */ 655 it->hint_base = off; 656 for (u32 i = 0; i < it->nimports; ++i) { 657 LinkSymbol* s = LinkSyms_at(&img->syms, it->imports[i].sym - 1); 658 size_t nlen = 0; 659 const char* nm = coff_import_emit_name(c, &it->imports[i], s, &nlen); 660 if (!nm || nlen == 0) 661 compiler_panic(c, SRCLOC_NONE, 662 "link_emit_coff: imported symbol has empty name"); 663 it->imports[i].hint_off = off; 664 /* hint (2 B) + name (nlen + 1) + optional pad to even. */ 665 u32 rec = 2u + (u32)nlen + 1u; 666 if (rec & 1u) ++rec; 667 off += rec; 668 } 669 it->hint_total = off - it->hint_base; 670 off = (u32)ALIGN_UP((u64)off, (u64)PE_IDATA_BLOCK_ALIGN); 671 672 /* Block 5: DLL name strings (NUL-terminated). */ 673 it->name_base = off; 674 for (u32 d = 0; d < it->ndlls; ++d) { 675 Slice nm_s = pool_slice(c->global, it->dlls[d].soname); 676 const char* nm = nm_s.s; 677 size_t nlen = nm_s.len; 678 if (!nm || nlen == 0) 679 compiler_panic(c, SRCLOC_NONE, 680 "link_emit_coff: providing DSO has empty soname"); 681 it->dlls[d].name_off = off; 682 off += (u32)nlen + 1u; 683 } 684 it->name_total = off - it->name_base; 685 it->idata_size = off; 686 } 687 688 /* Append the function-import stubs to the .text bucket. Each stub is 689 * the format arch descriptor's stub size. Records each stub's bucket- 690 * local offset on the matching CoffImport so the per-symbol stub vaddr 691 * can be computed once the .text bucket's RVA is final. */ 692 static void coff_append_stubs(LinkImage* img, CoffImportTable* it, 693 CoffSection* text_bucket, u32* text_bucket_cap) { 694 Heap* heap = img->heap; 695 Compiler* c = img->c; 696 const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_COFF); 697 const ObjCoffArchOps* arch = 698 fmt && fmt->coff_arch ? fmt->coff_arch(c->target.arch) : NULL; 699 u32 stub_size; 700 u32 stub_align; 701 u64 cur; 702 if (!arch || arch->stub_size == 0 || !arch->emit_iat_stub) { 703 compiler_panic(c, SRCLOC_NONE, 704 "link_emit_coff: arch has no COFF IAT stub emitter"); 705 } 706 stub_size = arch->stub_size; 707 /* Stubs are pure code; aligning to instruction alignment is enough. 708 * x64 wants byte-granular, aa64 wants 4 B; align to stub size as a 709 * convenient upper bound. */ 710 stub_align = stub_size; 711 cur = (u64)text_bucket->size; 712 cur = ALIGN_UP(cur, (u64)stub_align); 713 it->stub_text_off = (u32)cur; 714 for (u32 i = 0; i < it->nimports; ++i) { 715 if (!it->imports[i].is_func) continue; 716 it->imports[i].stub_off = (u32)cur; 717 cur += stub_size; 718 } 719 it->stub_total = (u32)cur - it->stub_text_off; 720 if (it->stub_total == 0) return; 721 /* Grow the .text bucket buffer to hold the new region. */ 722 u32 need = (u32)cur; 723 if (need > *text_bucket_cap) { 724 (void)VEC_GROW(heap, text_bucket->bytes, *text_bucket_cap, need); 725 } 726 /* Zero the alignment pad; stub bytes are written later by 727 * coff_emit_stubs once vaddrs are known. */ 728 if ((u32)cur > text_bucket->size) { 729 memset(text_bucket->bytes + text_bucket->size, 0, 730 (size_t)((u32)cur - text_bucket->size)); 731 } 732 text_bucket->size = (u32)cur; 733 } 734 735 /* Emit each function import's IAT stub into the .text bucket. Must 736 * run after coff_assign_layout has fixed both .text's RVA and 737 * .idata's RVA, since the stub bakes in the post-shift IAT slot 738 * displacement. */ 739 static void coff_emit_stubs(LinkImage* img, const CoffImportTable* it, 740 const CoffSection out[COFF_NBUCKETS]) { 741 Compiler* c = img->c; 742 const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_COFF); 743 const ObjCoffArchOps* arch = 744 fmt && fmt->coff_arch ? fmt->coff_arch(c->target.arch) : NULL; 745 u64 img_base = PE_IMAGE_BASE; 746 u32 text_rva = out[COFF_BUCKET_TEXT].rva; 747 u32 idata_rva = out[COFF_BUCKET_IDATA].rva; 748 if (!arch || !arch->emit_iat_stub) { 749 compiler_panic(c, SRCLOC_NONE, 750 "link_emit_coff: arch has no COFF IAT stub emitter"); 751 } 752 for (u32 i = 0; i < it->nimports; ++i) { 753 u64 stub_va, slot_va; 754 if (!it->imports[i].is_func) continue; 755 stub_va = img_base + (u64)text_rva + (u64)it->imports[i].stub_off; 756 slot_va = img_base + (u64)idata_rva + (u64)it->imports[i].iat_off; 757 arch->emit_iat_stub(out[COFF_BUCKET_TEXT].bytes + it->imports[i].stub_off, 758 stub_va, slot_va); 759 } 760 } 761 762 /* Emit .idata content into the bucket buffer. Allocates the buffer 763 * here (size is already known from coff_plan_idata_layout). */ 764 static void coff_emit_idata(LinkImage* img, const CoffImportTable* it, 765 CoffSection out[COFF_NBUCKETS], 766 u32* idata_bucket_cap) { 767 Heap* heap = img->heap; 768 Compiler* c = img->c; 769 CoffSection* idata = &out[COFF_BUCKET_IDATA]; 770 u32 idata_rva = idata->rva; 771 u8* buf; 772 /* Allocate the bucket buffer (idata_size is already block-aligned). */ 773 buf = (u8*)heap->alloc(heap, it->idata_size, _Alignof(u64)); 774 if (!buf) 775 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom on .idata buffer"); 776 memset(buf, 0, it->idata_size); 777 idata->bytes = buf; 778 idata->size = it->idata_size; 779 *idata_bucket_cap = it->idata_size; 780 781 /* Block 1: IMAGE_IMPORT_DESCRIPTOR table. */ 782 for (u32 d = 0; d < it->ndlls; ++d) { 783 u8* p = buf + d * (u32)COFF_IMPORT_DESCRIPTOR_SIZE; 784 u32 ilt_rva = idata_rva + it->dlls[d].ilt_off; 785 u32 iat_rva = idata_rva + it->dlls[d].iat_off; 786 u32 name_rva = idata_rva + it->dlls[d].name_off; 787 wr_u32_le(p + 0, ilt_rva); /* OriginalFirstThunk */ 788 wr_u32_le(p + 4, 0u); /* TimeDateStamp */ 789 wr_u32_le(p + 8, 0u); /* ForwarderChain */ 790 wr_u32_le(p + 12, name_rva); /* Name */ 791 wr_u32_le(p + 16, iat_rva); /* FirstThunk */ 792 } 793 /* Trailing zero descriptor already zero-filled by memset. */ 794 795 /* Blocks 2+3: ILT + IAT. Both initially point at the same hint/name 796 * record for each import; the OS loader rewrites IAT entries at 797 * load time. */ 798 for (u32 i = 0; i < it->nimports; ++i) { 799 u64 hint_rva = (u64)idata_rva + (u64)it->imports[i].hint_off; 800 wr_u64_le(buf + it->imports[i].ilt_off, hint_rva); 801 wr_u64_le(buf + it->imports[i].iat_off, hint_rva); 802 } 803 /* Per-DLL ILT/IAT terminators are u64 0, already zero-filled. */ 804 805 /* Block 4: hint/name records. */ 806 for (u32 i = 0; i < it->nimports; ++i) { 807 LinkSymbol* s = LinkSyms_at(&img->syms, it->imports[i].sym - 1); 808 size_t nlen = 0; 809 const char* nm = coff_import_emit_name(c, &it->imports[i], s, &nlen); 810 u8* p = buf + it->imports[i].hint_off; 811 wr_u16_le(p, PE_IMPORT_HINT_NONE); 812 memcpy(p + 2, nm, nlen); 813 /* NUL terminator + optional pad already zero. */ 814 } 815 816 /* Block 5: DLL name strings. */ 817 for (u32 d = 0; d < it->ndlls; ++d) { 818 Slice nm_s = pool_slice(c->global, it->dlls[d].soname); 819 const char* nm = nm_s.s; 820 size_t nlen = nm_s.len; 821 memcpy(buf + it->dlls[d].name_off, nm, nlen); 822 /* NUL already zero. */ 823 } 824 } 825 826 /* Per-LinkSymId vaddr override table for imports. Indexed by 827 * LinkSymId-1; 0 means "not an import". Built once after the .idata 828 * bucket RVA is final. Consumed by coff_apply_all_relocs in lieu of 829 * the symbol's own vaddr field (which is 0 for imports). */ 830 typedef struct CoffImportVaddr { 831 u64* by_sym; /* size = nsyms; 0 entries mean "not imported" */ 832 u32 nsyms; 833 } CoffImportVaddr; 834 835 static void coff_import_vaddr_build(LinkImage* img, const CoffImportTable* it, 836 const CoffSection out[COFF_NBUCKETS], 837 CoffImportVaddr* iv) { 838 Heap* heap = img->heap; 839 u64 img_base = PE_IMAGE_BASE; 840 u32 text_rva = out[COFF_BUCKET_TEXT].rva; 841 u32 idata_rva = out[COFF_BUCKET_IDATA].rva; 842 iv->nsyms = LinkSyms_count(&img->syms); 843 iv->by_sym = (u64*)heap->alloc(heap, sizeof(u64) * (size_t)(iv->nsyms + 1u), 844 _Alignof(u64)); 845 if (!iv->by_sym) 846 compiler_panic(img->c, SRCLOC_NONE, 847 "link_emit_coff: oom on import vaddr table"); 848 memset(iv->by_sym, 0, sizeof(u64) * (size_t)(iv->nsyms + 1u)); 849 for (u32 i = 0; i < it->nimports; ++i) { 850 LinkSymId sid = it->imports[i].sym; 851 u64 va; 852 if (it->imports[i].is_func) { 853 va = img_base + (u64)text_rva + (u64)it->imports[i].stub_off; 854 } else { 855 va = img_base + (u64)idata_rva + (u64)it->imports[i].iat_off; 856 } 857 iv->by_sym[sid - 1u] = va; 858 /* Fan out across every shadow LinkSymId with the same name so a 859 * per-input undef reference resolves to the same import slot. */ 860 { 861 LinkSymbol* canonical = LinkSyms_at(&img->syms, sid - 1u); 862 for (u32 j = 0; j < iv->nsyms; ++j) { 863 LinkSymbol* s = LinkSyms_at(&img->syms, j); 864 if (s->name == canonical->name && s->imported) { 865 iv->by_sym[s->id - 1u] = va; 866 } 867 } 868 } 869 } 870 } 871 872 static void coff_import_vaddr_free(LinkImage* img, CoffImportVaddr* iv) { 873 Heap* heap = img->heap; 874 if (iv->by_sym) { 875 heap->free(heap, iv->by_sym, sizeof(u64) * (size_t)(iv->nsyms + 1u)); 876 } 877 } 878 879 /* Resolve Compiler.target.arch -> IMAGE_FILE_MACHINE_* via the per-arch 880 * coff ops table. Panic if the arch has no COFF descriptor or the 881 * machine value is one kit doesn't ship (Phase 1 supports AMD64 and 882 * ARM64 only). */ 883 static u16 coff_machine_or_panic(Compiler* c) { 884 const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_COFF); 885 const ObjCoffArchOps* arch = 886 fmt && fmt->coff_arch ? fmt->coff_arch(c->target.arch) : NULL; 887 u16 m; 888 if (!arch) 889 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: no COFF arch descriptor"); 890 m = arch->machine; 891 if (m != IMAGE_FILE_MACHINE_AMD64 && m != IMAGE_FILE_MACHINE_ARM64) 892 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: unsupported machine 0x%x", 893 (unsigned)m); 894 return m; 895 } 896 897 static int coff_section_name_starts(Compiler* c, const LinkSection* ls, 898 const char* prefix) { 899 size_t pn = slice_from_cstr(prefix).len; 900 Slice s_s = ls->name ? pool_slice(c->global, ls->name) : SLICE_NULL; 901 const char* s = s_s.s; 902 size_t n = s_s.len; 903 return s && n >= pn && memcmp(s, prefix, pn) == 0; 904 } 905 906 static int coff_section_name_cmp(Compiler* c, const LinkSection* a, 907 const LinkSection* b) { 908 Slice as_s = a->name ? pool_slice(c->global, a->name) : SLICE_NULL; 909 Slice bs_s = b->name ? pool_slice(c->global, b->name) : SLICE_NULL; 910 const char* as = as_s.s ? as_s.s : ""; 911 const char* bs = bs_s.s ? bs_s.s : ""; 912 size_t an = as_s.len, bn = bs_s.len; 913 size_t n = an < bn ? an : bn; 914 int cmp = n ? memcmp(as, bs, n) : 0; 915 if (cmp) return cmp; 916 if (an < bn) return -1; 917 if (an > bn) return 1; 918 if (a->id < b->id) return -1; 919 if (a->id > b->id) return 1; 920 return 0; 921 } 922 923 static void coff_place_section(LinkImage* img, CoffSection out[COFF_NBUCKETS], 924 CoffSecMap* map, u64 bucket_cur[COFF_NBUCKETS], 925 u32 bucket_cap[COFF_NBUCKETS], 926 const LinkSection* ls) { 927 Heap* heap = img->heap; 928 CoffBucket b2 = coff_bucket_for(img->c, ls); 929 u32 align = ls->align ? ls->align : 1u; 930 u64 cur = bucket_cur[b2]; 931 cur = ALIGN_UP(cur, (u64)align); 932 map[ls->id - 1].bucket = (u8)b2; 933 /* Record the bucket-local offset; the absolute RVA / file offset 934 * are filled in after bucket placement (RVAs need 935 * SectionAlignment, file offsets need FileAlignment). */ 936 map[ls->id - 1].new_rva = (u32)cur; 937 if (b2 != COFF_BUCKET_BSS) { 938 /* Copy bytes from the source segment buffer into the bucket. */ 939 if (ls->size) { 940 u32 need = (u32)(cur + ls->size); 941 if (need > bucket_cap[b2]) { 942 (void)VEC_GROW(heap, out[b2].bytes, bucket_cap[b2], need); 943 } 944 memset(out[b2].bytes + bucket_cur[b2], 0, (size_t)(cur - bucket_cur[b2])); 945 if (ls->sem != SSEM_NOBITS) { 946 const LinkSegment* seg = &img->segments[ls->segment_id - 1]; 947 const u8* src = img->segment_bytes[seg->id - 1] + 948 (size_t)(ls->file_offset - seg->file_offset); 949 memcpy(out[b2].bytes + cur, src, (size_t)ls->size); 950 } else { 951 memset(out[b2].bytes + cur, 0, (size_t)ls->size); 952 } 953 } 954 } 955 cur += ls->size; 956 bucket_cur[b2] = cur; 957 out[b2].size = (u32)cur; 958 } 959 960 static void coff_insert_sorted_section(Compiler* c, const LinkSection** a, 961 u32* n, const LinkSection* ls) { 962 u32 i = *n; 963 while (i > 0 && coff_section_name_cmp(c, ls, a[i - 1u]) < 0) { 964 a[i] = a[i - 1u]; 965 --i; 966 } 967 a[i] = ls; 968 *n += 1u; 969 } 970 971 /* ---- pass 1: bucket input sections, assemble bytes, assign deltas ---- 972 * CoffSecMap is defined above (alongside CoffTlsLayout) because the 973 * TLS planning helpers need to consume one. */ 974 975 /* Build the four payload buckets (.text/.rdata/.data/.bss). 976 * 977 * `map[secid-1]` is populated for every kept LinkSection with the 978 * section's new RVA, new file offset, the bucket it landed in, and the 979 * delta to add to in-section vaddrs. Bucket buffers are 980 * heap-allocated; the caller frees them after emit. */ 981 static void coff_build_buckets(LinkImage* img, CoffSection out[COFF_NBUCKETS], 982 CoffSecMap* map) { 983 Heap* heap = img->heap; 984 Compiler* c = img->c; 985 const LinkSection** tls_sorted = NULL; 986 const LinkSection** crt_sorted = NULL; 987 u32 ntls_sorted = 0; 988 u32 ncrt_sorted = 0; 989 u32 i, b; 990 991 for (b = 0; b < COFF_NBUCKETS; ++b) { 992 memset(&out[b], 0, sizeof(out[b])); 993 } 994 out[COFF_BUCKET_TEXT].name = ".text"; 995 out[COFF_BUCKET_TEXT].characteristics = 996 IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ; 997 out[COFF_BUCKET_TEXT].has_file_bytes = 1; 998 out[COFF_BUCKET_RDATA].name = ".rdata"; 999 out[COFF_BUCKET_RDATA].characteristics = 1000 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; 1001 out[COFF_BUCKET_RDATA].has_file_bytes = 1; 1002 out[COFF_BUCKET_IDATA].name = ".idata"; 1003 out[COFF_BUCKET_IDATA].characteristics = 1004 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; 1005 out[COFF_BUCKET_IDATA].has_file_bytes = 1; 1006 out[COFF_BUCKET_DATA].name = ".data"; 1007 out[COFF_BUCKET_DATA].characteristics = 1008 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 1009 out[COFF_BUCKET_DATA].has_file_bytes = 1; 1010 /* The Windows loader uses .tls as a *template*: the bytes on disk 1011 * seed each thread's per-TLS copy at thread creation, and threads 1012 * write to their copies, not the template. The PE section is still 1013 * marked writable because that's what mingw and link.exe emit; the 1014 * loader special-cases it via the TLS directory. */ 1015 out[COFF_BUCKET_TLS].name = ".tls"; 1016 out[COFF_BUCKET_TLS].characteristics = 1017 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 1018 out[COFF_BUCKET_TLS].has_file_bytes = 1; 1019 out[COFF_BUCKET_BSS].name = ".bss"; 1020 out[COFF_BUCKET_BSS].characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | 1021 IMAGE_SCN_MEM_READ | 1022 IMAGE_SCN_MEM_WRITE; 1023 out[COFF_BUCKET_BSS].has_file_bytes = 0; 1024 out[COFF_BUCKET_PDATA].name = ".pdata"; 1025 out[COFF_BUCKET_PDATA].characteristics = 1026 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; 1027 out[COFF_BUCKET_PDATA].has_file_bytes = 1; 1028 out[COFF_BUCKET_RELOC].name = ".reloc"; 1029 out[COFF_BUCKET_RELOC].characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | 1030 IMAGE_SCN_MEM_READ | 1031 IMAGE_SCN_MEM_DISCARDABLE; 1032 out[COFF_BUCKET_RELOC].has_file_bytes = 1; 1033 1034 /* Track per-bucket cursors. Bucket sizes are bounded by the sum of 1035 * input section sizes plus per-section alignment padding; we grow 1036 * lazily via VEC_GROW. */ 1037 u64 bucket_cur[COFF_NBUCKETS]; 1038 u32 bucket_cap[COFF_NBUCKETS]; 1039 for (b = 0; b < COFF_NBUCKETS; ++b) { 1040 bucket_cur[b] = 0; 1041 bucket_cap[b] = 0; 1042 } 1043 1044 tls_sorted = img->nsections ? (const LinkSection**)heap->alloc( 1045 heap, sizeof(*tls_sorted) * img->nsections, 1046 _Alignof(const LinkSection*)) 1047 : NULL; 1048 crt_sorted = img->nsections ? (const LinkSection**)heap->alloc( 1049 heap, sizeof(*crt_sorted) * img->nsections, 1050 _Alignof(const LinkSection*)) 1051 : NULL; 1052 if (img->nsections && (!tls_sorted || !crt_sorted)) 1053 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom sorting sections"); 1054 1055 for (i = 0; i < img->nsections; ++i) { 1056 const LinkSection* ls = &img->sections[i]; 1057 if (!(ls->flags & SF_ALLOC)) continue; 1058 if (ls->flags & SF_TLS) { 1059 coff_insert_sorted_section(c, tls_sorted, &ntls_sorted, ls); 1060 continue; 1061 } 1062 if (coff_section_name_starts(c, ls, ".CRT$")) { 1063 coff_insert_sorted_section(c, crt_sorted, &ncrt_sorted, ls); 1064 continue; 1065 } 1066 coff_place_section(img, out, map, bucket_cur, bucket_cap, ls); 1067 } 1068 1069 for (i = 0; i < ntls_sorted; ++i) { 1070 coff_place_section(img, out, map, bucket_cur, bucket_cap, tls_sorted[i]); 1071 } 1072 for (i = 0; i < ncrt_sorted; ++i) { 1073 coff_place_section(img, out, map, bucket_cur, bucket_cap, crt_sorted[i]); 1074 } 1075 1076 /* Track caps so we can free with the right size later (heap->free 1077 * needs the original allocation size). Stash into size_raw 1078 * temporarily — overwritten below with the proper PE value. */ 1079 for (b = 0; b < COFF_NBUCKETS; ++b) out[b].size_raw = bucket_cap[b]; 1080 if (tls_sorted) 1081 heap->free(heap, tls_sorted, sizeof(*tls_sorted) * img->nsections); 1082 if (crt_sorted) 1083 heap->free(heap, crt_sorted, sizeof(*crt_sorted) * img->nsections); 1084 } 1085 1086 /* Assign RVAs and file offsets to the buckets that participate in the 1087 * image. Returns the file offset at which trailing pad-to-EOF should 1088 * land (== file size). */ 1089 static u64 coff_assign_layout(CoffSection out[COFF_NBUCKETS], 1090 u32 headers_file_size, u32 first_section_rva) { 1091 u32 rva = first_section_rva; 1092 u64 file = ALIGN_UP((u64)headers_file_size, (u64)PE_FILE_ALIGNMENT); 1093 u32 b; 1094 for (b = 0; b < COFF_NBUCKETS; ++b) { 1095 if (out[b].size == 0) { 1096 out[b].in_image = 0; 1097 out[b].rva = 0; 1098 out[b].file_offset = 0; 1099 out[b].size_raw = 0; 1100 continue; 1101 } 1102 out[b].in_image = 1; 1103 out[b].rva = (u32)ALIGN_UP((u64)rva, (u64)PE_SECTION_ALIGNMENT); 1104 if (out[b].has_file_bytes) { 1105 out[b].file_offset = (u32)file; 1106 out[b].size_raw = (u32)ALIGN_UP((u64)out[b].size, (u64)PE_FILE_ALIGNMENT); 1107 file += out[b].size_raw; 1108 } else { 1109 out[b].file_offset = 0; 1110 out[b].size_raw = 0; 1111 } 1112 rva = out[b].rva + out[b].size; 1113 } 1114 return file; 1115 } 1116 1117 /* Build the .reloc bytes by grouping absolute relocs by 4-KiB page. 1118 * The map[] array maps LinkSectionId-1 to the per-section post-PE-relayout 1119 * RVA, so we can compute each reloc's site_rva = section_rva + (orig 1120 * write_vaddr - orig section_vaddr). 1121 * 1122 * Layout per page: 1123 * u32 page_rva 1124 * u32 size_of_block (8 + n_entries*2, padded to a multiple of 4) 1125 * u16 entries[]: (type << 12) | (offset & 0xfff) 1126 * optional trailing u16 = 0 (IMAGE_REL_BASED_ABSOLUTE) for u32 alignment */ 1127 typedef struct CoffRelocEntry { 1128 u32 site_rva; 1129 u16 type; 1130 u16 pad; 1131 } CoffRelocEntry; 1132 1133 static int coff_reloc_entry_cmp(const void* a, const void* b) { 1134 const CoffRelocEntry* ea = (const CoffRelocEntry*)a; 1135 const CoffRelocEntry* eb = (const CoffRelocEntry*)b; 1136 if (ea->site_rva < eb->site_rva) return -1; 1137 if (ea->site_rva > eb->site_rva) return 1; 1138 return 0; 1139 } 1140 1141 static void coff_build_reloc_section(LinkImage* img, 1142 const CoffSection out[COFF_NBUCKETS], 1143 const CoffSecMap* map, CoffSection* reloc, 1144 const CoffRelocEntry* extras, 1145 u32 n_extras) { 1146 Heap* heap = img->heap; 1147 Compiler* c = img->c; 1148 u32 nrel = LinkRelocs_count(&img->relocs); 1149 CoffRelocEntry* entries = NULL; 1150 u32 nentries = 0; 1151 u32 cap = 0; 1152 u32 i; 1153 1154 for (i = 0; i < nrel; ++i) { 1155 const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 1156 const LinkSection* ls; 1157 u64 site_old_vaddr; 1158 u32 site_rva; 1159 u16 type; 1160 if (!coff_reloc_needs_base_reloc(r->kind)) continue; 1161 if (r->link_section_id == LINK_SEC_NONE) continue; 1162 ls = &img->sections[r->link_section_id - 1]; 1163 /* r->write_vaddr is in the pre-relayout coordinate system (same as 1164 * ls->vaddr), so the offset into the section is stable. Add the 1165 * containing bucket's final RVA to land at the image RVA. */ 1166 site_old_vaddr = r->write_vaddr; 1167 u8 sb = map[ls->id - 1].bucket; 1168 site_rva = out[sb].rva + map[ls->id - 1].new_rva + 1169 (u32)(site_old_vaddr - ls->vaddr); 1170 if (r->kind == R_ABS64) { 1171 type = (u16)IMAGE_REL_BASED_DIR64; 1172 } else { 1173 type = (u16)IMAGE_REL_BASED_HIGHLOW; 1174 } 1175 if (nentries == cap) { 1176 (void)VEC_GROW(heap, entries, cap, nentries + 1u); 1177 } 1178 entries[nentries].site_rva = site_rva; 1179 entries[nentries].type = type; 1180 entries[nentries].pad = 0; 1181 ++nentries; 1182 } 1183 /* Append caller-supplied extras (TLS directory absolute-VA fields, 1184 * etc.). These are already site-RVAs in the final image. */ 1185 for (i = 0; i < n_extras; ++i) { 1186 if (nentries == cap) { 1187 (void)VEC_GROW(heap, entries, cap, nentries + 1u); 1188 } 1189 entries[nentries] = extras[i]; 1190 ++nentries; 1191 } 1192 if (nentries == 0) { 1193 reloc->bytes = NULL; 1194 reloc->size = 0; 1195 if (entries) heap->free(heap, entries, cap * sizeof(*entries)); 1196 (void)c; 1197 return; 1198 } 1199 /* Sort entries by RVA so we can group runs sharing a 4-KiB page. */ 1200 qsort(entries, nentries, sizeof(*entries), coff_reloc_entry_cmp); 1201 1202 /* Two-pass: first compute the total size (so we can allocate the 1203 * blob exactly), then emit. */ 1204 u32 blob_size = 0; 1205 u32 run_start = 0; 1206 while (run_start < nentries) { 1207 u32 page = entries[run_start].site_rva & ~0xfffu; 1208 u32 run_end = run_start; 1209 while (run_end < nentries && 1210 (entries[run_end].site_rva & ~0xfffu) == page) { 1211 ++run_end; 1212 } 1213 u32 n = run_end - run_start; 1214 u32 block = COFF_BASE_RELOCATION_SIZE + n * 2u; 1215 block = (u32)ALIGN_UP((u64)block, 4ull); 1216 blob_size += block; 1217 run_start = run_end; 1218 } 1219 reloc->bytes = (u8*)heap->alloc(heap, blob_size, 4); 1220 if (!reloc->bytes && blob_size) 1221 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom on .reloc blob"); 1222 memset(reloc->bytes, 0, blob_size); 1223 reloc->size = blob_size; 1224 /* Stash allocation size for free path. */ 1225 reloc->size_raw = blob_size; 1226 1227 u32 cursor = 0; 1228 run_start = 0; 1229 while (run_start < nentries) { 1230 u32 page = entries[run_start].site_rva & ~0xfffu; 1231 u32 run_end = run_start; 1232 while (run_end < nentries && 1233 (entries[run_end].site_rva & ~0xfffu) == page) { 1234 ++run_end; 1235 } 1236 u32 n = run_end - run_start; 1237 u32 raw_size = COFF_BASE_RELOCATION_SIZE + n * 2u; 1238 u32 block = (u32)ALIGN_UP((u64)raw_size, 4ull); 1239 u8* p = reloc->bytes + cursor; 1240 wr_u32_le(p, page); 1241 wr_u32_le(p + 4, block); 1242 u32 k; 1243 for (k = 0; k < n; ++k) { 1244 u16 entry = (u16)(((u16)entries[run_start + k].type << 12) | 1245 (entries[run_start + k].site_rva & 0xfffu)); 1246 wr_u16_le(p + 8 + k * 2u, entry); 1247 } 1248 /* Optional trailing pad: a single IMAGE_REL_BASED_ABSOLUTE (0). */ 1249 if (block > raw_size) { 1250 wr_u16_le(p + 8 + n * 2u, 0); 1251 } 1252 cursor += block; 1253 run_start = run_end; 1254 } 1255 heap->free(heap, entries, cap * sizeof(*entries)); 1256 } 1257 1258 /* Patch each LinkRelocApply against the PE-relayout coordinates and 1259 * apply. `bucket_bytes[bucket]` gives the writable buffer for that 1260 * bucket; the per-section delta in map[] turns the old in-section 1261 * offsets into bucket-local offsets. 1262 * 1263 * Imported targets (LinkSymbol.imported == 1) have no vaddr of their 1264 * own — instead the .idata pass populated `iv->by_sym[id-1]` with the 1265 * function stub's vaddr (for callable imports) or the IAT slot's 1266 * vaddr (for data imports). This is the spot where that table is 1267 * consulted in lieu of the symbol's own zero vaddr. */ 1268 static void coff_apply_all_relocs(LinkImage* img, 1269 const CoffSection out[COFF_NBUCKETS], 1270 const CoffSecMap* map, 1271 const CoffImportVaddr* iv) { 1272 Compiler* c = img->c; 1273 u32 i; 1274 u64 img_base = PE_IMAGE_BASE; 1275 u32 nrel = LinkRelocs_count(&img->relocs); 1276 for (i = 0; i < nrel; ++i) { 1277 LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 1278 const LinkSymbol* tgt = LinkSyms_at(&img->syms, r->target - 1); 1279 const LinkSection* sec; 1280 const LinkSection* tgt_sec; 1281 u64 S, P; 1282 u8* P_bytes; 1283 u8 bucket; 1284 u32 site_off_in_sec; 1285 u32 site_bucket_off; 1286 if (r->link_section_id == LINK_SEC_NONE) continue; 1287 sec = &img->sections[r->link_section_id - 1]; 1288 bucket = map[sec->id - 1].bucket; 1289 if (!out[bucket].has_file_bytes || !out[bucket].bytes) { 1290 /* Shouldn't happen — .bss has no relocations. */ 1291 continue; 1292 } 1293 site_off_in_sec = (u32)(r->write_vaddr - sec->vaddr); 1294 site_bucket_off = map[sec->id - 1].new_rva + site_off_in_sec; 1295 P_bytes = out[bucket].bytes + site_bucket_off; 1296 /* P = ImageBase + bucket_rva + map[].new_rva + site_off_in_sec 1297 * — i.e. the final runtime address of the patch site. */ 1298 P = img_base + (u64)out[bucket].rva + (u64)map[sec->id - 1].new_rva + 1299 site_off_in_sec; 1300 1301 /* Resolve S: target symbol's new image-relative address. Look up 1302 * the LinkSection that contains the symbol's original vaddr, then 1303 * apply that section's delta. */ 1304 if (tgt->imported) { 1305 /* IAT-routed: stub vaddr (functions) / slot vaddr (data). */ 1306 if (!iv || iv->by_sym[r->target - 1u] == 0) 1307 compiler_panic(c, SRCLOC_NONE, 1308 "link_emit_coff: imported target lacks IAT slot"); 1309 S = iv->by_sym[r->target - 1u]; 1310 } else if (tgt->kind == SK_ABS) { 1311 S = tgt->vaddr; 1312 } else if (tgt->defined) { 1313 tgt_sec = coff_symbol_section(img, tgt); 1314 if (!tgt_sec) { 1315 compiler_panic(c, SRCLOC_NONE, 1316 "link_emit_coff: symbol vaddr 0x%llx has no " 1317 "containing section", 1318 (unsigned long long)tgt->vaddr); 1319 } 1320 u8 tb = map[tgt_sec->id - 1].bucket; 1321 u64 sym_off = tgt->vaddr - tgt_sec->vaddr; 1322 S = img_base + (u64)out[tb].rva + (u64)map[tgt_sec->id - 1].new_rva + 1323 sym_off; 1324 } else { 1325 /* Undef and not imported — shouldn't survive resolve_undefs. */ 1326 compiler_panic(c, SRCLOC_NONE, 1327 "link_emit_coff: unresolved non-imported symbol"); 1328 } 1329 /* COFF-only section-relative kinds: the SECREL value is the 1330 * symbol's offset from the start of its containing output section 1331 * (PE bucket), and SECTION is the 1-based PE section index. 1332 * link_reloc_apply only sees S and P, so we patch these inline 1333 * before delegating common kinds. */ 1334 if (r->kind == R_COFF_SECREL || r->kind == R_COFF_SECTION || 1335 r->kind == R_COFF_AARCH64_SECREL_LOW12A || 1336 r->kind == R_COFF_AARCH64_SECREL_HIGH12A || 1337 r->kind == R_COFF_AARCH64_SECREL_LOW12L) { 1338 if (!tgt->defined || tgt->kind == SK_ABS) { 1339 compiler_panic(c, SRCLOC_NONE, 1340 "link_emit_coff: COFF SECREL/SECTION requires a " 1341 "defined section-bound target symbol"); 1342 } 1343 u8 tb = map[tgt_sec->id - 1].bucket; 1344 u64 sym_off_in_bucket = 1345 (u64)map[tgt_sec->id - 1].new_rva + (tgt->vaddr - tgt_sec->vaddr); 1346 if (r->kind == R_COFF_SECREL) { 1347 u64 v = sym_off_in_bucket + (u64)r->addend; 1348 wr_u32_le(P_bytes, (u32)(v & 0xffffffffu)); 1349 } else if (r->kind == R_COFF_SECTION) { 1350 /* PE section indices are 1-based; buckets are 0-based, so add 1. */ 1351 wr_u16_le(P_bytes, (u16)((tb + 1u) & 0xffffu)); 1352 } else if (r->kind == R_COFF_AARCH64_SECREL_LOW12L) { 1353 /* AArch64 SECREL_LOW12L: patch a load/store unsigned-immediate 1354 * imm12. The encoded immediate is scaled by the access width, which 1355 * is recoverable from the instruction's size/opc bits. */ 1356 u64 v = sym_off_in_bucket + (u64)r->addend; 1357 u64 lo12 = v & 0xfffu; 1358 u32 instr = rd_u32_le(P_bytes); 1359 u32 sz = (instr >> 30) & 0x3u; 1360 u32 shift = (((instr >> 26) & 0x1u) && ((instr >> 23) & 0x1u)) ? 4u : sz; 1361 u32 align_mask = (1u << shift) - 1u; 1362 if (lo12 & align_mask) 1363 compiler_panic(c, SRCLOC_NONE, 1364 "link_emit_coff: ARM64 SECREL_LOW12L misaligned " 1365 "offset 0x%llx for scale %u", 1366 (unsigned long long)lo12, (unsigned)shift); 1367 instr = (instr & ~(0xfffu << 10)) | 1368 ((u32)((lo12 >> shift) & 0xfffu) << 10); 1369 wr_u32_le(P_bytes, instr); 1370 } else { 1371 /* AArch64 SECREL_{LOW,HIGH}12A: patch the imm12 field of an 1372 * existing ADD-imm12 instruction. LOW12A = bits [11:0] of the 1373 * SECREL; HIGH12A = bits [23:12]. The instruction's sh bit was 1374 * already set by the codegen (0 for LOW, 1 for HIGH). */ 1375 u64 v = sym_off_in_bucket + (u64)r->addend; 1376 u32 imm12 = (r->kind == R_COFF_AARCH64_SECREL_HIGH12A) 1377 ? (u32)((v >> 12) & 0xfffu) 1378 : (u32)(v & 0xfffu); 1379 u32 instr = rd_u32_le(P_bytes); 1380 instr = (instr & ~(0xfffu << 10)) | (imm12 << 10); 1381 wr_u32_le(P_bytes, instr); 1382 } 1383 continue; 1384 } 1385 if (r->kind == R_COFF_ADDR32NB) { 1386 u64 inline_addend = rd_u32_le(P_bytes); 1387 u64 v = (S - img_base) + inline_addend + (u64)r->addend; 1388 wr_u32_le(P_bytes, (u32)(v & 0xffffffffu)); 1389 continue; 1390 } 1391 if (tgt->bind == SB_WEAK && tgt->kind == SK_ABS && tgt->vaddr == 0) { 1392 /* AArch64 cannot generally ADRP from a PE image base down to absolute 1393 * NULL. Materialize the weak-undef address as zero directly; the paired 1394 * ADD low-12 relocation is already a no-op. */ 1395 if (r->kind == R_AARCH64_ADR_PREL_PG_HI21 || 1396 r->kind == R_AARCH64_ADR_PREL_PG_HI21_NC) { 1397 u32 instr = rd_u32_le(P_bytes); 1398 u32 rd = instr & 0x1fu; 1399 wr_u32_le(P_bytes, 0xd2800000u | rd); /* movz Xrd, #0 */ 1400 continue; 1401 } 1402 if (r->kind == R_AARCH64_ADD_ABS_LO12_NC) continue; 1403 } 1404 link_reloc_apply(c, r->kind, P_bytes, S, r->addend, P); 1405 } 1406 } 1407 1408 /* ---- header marshalling ---- 1409 * 1410 * Each helper streams its on-disk shape to the writer field-by-field; 1411 * we avoid sizeof(struct) on the packed PE wire types since they carry 1412 * implicit-padding hazards on hosts that disagree with #pragma pack(1) 1413 * defaults. */ 1414 1415 static void coff_write_dos_stub(Writer* w) { 1416 u8 buf[PE_DOS_HDR_SIZE]; 1417 memset(buf, 0, sizeof(buf)); 1418 /* e_magic ("MZ") + e_lfanew (offset of PE signature). All other 1419 * legacy fields zero. */ 1420 buf[0] = (u8)(IMAGE_DOS_SIGNATURE & 0xffu); 1421 buf[1] = (u8)((IMAGE_DOS_SIGNATURE >> 8) & 0xffu); 1422 wr_u32_le(buf + 0x3c, PE_DOS_E_LFANEW); 1423 kit_writer_write(w, buf, sizeof(buf)); 1424 } 1425 1426 static void coff_write_file_header(Writer* w, u16 machine, u16 nsec, 1427 u16 characteristics) { 1428 coff_wr_u16(w, machine); 1429 coff_wr_u16(w, nsec); 1430 coff_wr_u32(w, 0u); /* TimeDateStamp */ 1431 coff_wr_u32(w, 0u); /* PointerToSymbolTable */ 1432 coff_wr_u32(w, 0u); /* NumberOfSymbols */ 1433 coff_wr_u16(w, (u16)PE_OPT_HDR_SIZE); /* SizeOfOptionalHeader */ 1434 coff_wr_u16(w, characteristics); 1435 } 1436 1437 static void coff_write_optional_header(Writer* w, u32 entry_rva, 1438 const CoffSection out[COFF_NBUCKETS], 1439 u32 headers_size_padded, u32 image_size, 1440 int dynamic_base, u16 subsystem, 1441 const CoffImportTable* it, 1442 const CoffTlsLayout* tls) { 1443 /* Standard fields. */ 1444 coff_wr_u16(w, IMAGE_NT_OPTIONAL_HDR64_MAGIC); 1445 coff_wr_u8(w, PE_LINKER_MAJOR); 1446 coff_wr_u8(w, PE_LINKER_MINOR); 1447 /* SizeOfCode / SizeOfInitializedData / SizeOfUninitializedData. */ 1448 u32 size_code = 1449 out[COFF_BUCKET_TEXT].in_image ? out[COFF_BUCKET_TEXT].size_raw : 0; 1450 u32 size_init = 1451 (out[COFF_BUCKET_RDATA].in_image ? out[COFF_BUCKET_RDATA].size_raw : 0) + 1452 (out[COFF_BUCKET_IDATA].in_image ? out[COFF_BUCKET_IDATA].size_raw : 0) + 1453 (out[COFF_BUCKET_DATA].in_image ? out[COFF_BUCKET_DATA].size_raw : 0) + 1454 (out[COFF_BUCKET_TLS].in_image ? out[COFF_BUCKET_TLS].size_raw : 0) + 1455 (out[COFF_BUCKET_PDATA].in_image ? out[COFF_BUCKET_PDATA].size_raw : 0) + 1456 (out[COFF_BUCKET_RELOC].in_image ? out[COFF_BUCKET_RELOC].size_raw : 0); 1457 u32 size_uninit = 1458 out[COFF_BUCKET_BSS].in_image ? out[COFF_BUCKET_BSS].size : 0; 1459 coff_wr_u32(w, size_code); 1460 coff_wr_u32(w, size_init); 1461 coff_wr_u32(w, size_uninit); 1462 coff_wr_u32(w, entry_rva); 1463 coff_wr_u32(w, 1464 out[COFF_BUCKET_TEXT].in_image ? out[COFF_BUCKET_TEXT].rva : 0); 1465 /* Windows-specific fields. */ 1466 coff_wr_u64(w, PE_IMAGE_BASE); 1467 coff_wr_u32(w, PE_SECTION_ALIGNMENT); 1468 coff_wr_u32(w, PE_FILE_ALIGNMENT); 1469 coff_wr_u16(w, PE_OS_MAJOR); 1470 coff_wr_u16(w, PE_OS_MINOR); 1471 coff_wr_u16(w, 0u); /* MajorImageVersion */ 1472 coff_wr_u16(w, 0u); /* MinorImageVersion */ 1473 coff_wr_u16(w, PE_SUBSYS_MAJOR); 1474 coff_wr_u16(w, PE_SUBSYS_MINOR); 1475 coff_wr_u32(w, 0u); /* Win32VersionValue */ 1476 coff_wr_u32(w, image_size); 1477 coff_wr_u32(w, headers_size_padded); 1478 coff_wr_u32(w, 0u); /* CheckSum */ 1479 coff_wr_u16(w, subsystem ? subsystem : IMAGE_SUBSYSTEM_WINDOWS_CUI); 1480 coff_wr_u16( 1481 w, (u16)(PE_DLL_CHARS_BASE | (dynamic_base ? PE_DLL_CHARS_ASLR : 0))); 1482 coff_wr_u64(w, PE_STACK_RESERVE); 1483 coff_wr_u64(w, PE_STACK_COMMIT); 1484 coff_wr_u64(w, PE_HEAP_RESERVE); 1485 coff_wr_u64(w, PE_HEAP_COMMIT); 1486 coff_wr_u32(w, 0u); /* LoaderFlags */ 1487 coff_wr_u32(w, (u32)PE_NUM_DATA_DIRS); 1488 /* DataDirectory[16]. Populated entries: 1489 * [1] IMPORT — descriptor table RVA + total descriptor bytes 1490 * [3] EXCEPTION — .pdata runtime-function table 1491 * [5] BASERELOC — when PIE and .reloc is in the image 1492 * [12] IAT — first IAT block RVA + sum of per-DLL IAT sizes 1493 * Everything else stays zero. */ 1494 u32 i; 1495 int has_idata = it && it->nimports > 0 && out[COFF_BUCKET_IDATA].in_image; 1496 for (i = 0; i < PE_NUM_DATA_DIRS; ++i) { 1497 if (i == IMAGE_DIRECTORY_ENTRY_IMPORT && has_idata) { 1498 coff_wr_u32(w, out[COFF_BUCKET_IDATA].rva + it->desc_off); 1499 coff_wr_u32(w, it->desc_size); 1500 } else if (i == IMAGE_DIRECTORY_ENTRY_EXCEPTION && 1501 out[COFF_BUCKET_PDATA].in_image) { 1502 coff_wr_u32(w, out[COFF_BUCKET_PDATA].rva); 1503 coff_wr_u32(w, out[COFF_BUCKET_PDATA].size); 1504 } else if (i == IMAGE_DIRECTORY_ENTRY_IAT && has_idata) { 1505 coff_wr_u32(w, out[COFF_BUCKET_IDATA].rva + it->iat_base); 1506 coff_wr_u32(w, it->iat_total); 1507 } else if (i == IMAGE_DIRECTORY_ENTRY_BASERELOC && dynamic_base && 1508 out[COFF_BUCKET_RELOC].in_image) { 1509 coff_wr_u32(w, out[COFF_BUCKET_RELOC].rva); 1510 coff_wr_u32(w, out[COFF_BUCKET_RELOC].size); 1511 } else if (i == IMAGE_DIRECTORY_ENTRY_TLS && tls && tls->present) { 1512 coff_wr_u32(w, out[COFF_BUCKET_RDATA].rva + tls->dir_rdata_off); 1513 coff_wr_u32(w, COFF_TLS_DIRECTORY64_SIZE); 1514 } else { 1515 coff_wr_u32(w, 0u); 1516 coff_wr_u32(w, 0u); 1517 } 1518 } 1519 } 1520 1521 static void coff_write_section_header(Writer* w, const char* name, u32 vsize, 1522 u32 rva, u32 size_raw, u32 file_offset, 1523 u32 characteristics) { 1524 u8 nm[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 1525 size_t n = slice_from_cstr(name).len; 1526 if (n > 8) n = 8; 1527 memcpy(nm, name, n); 1528 kit_writer_write(w, nm, 8); 1529 coff_wr_u32(w, vsize); 1530 coff_wr_u32(w, rva); 1531 coff_wr_u32(w, size_raw); 1532 coff_wr_u32(w, file_offset); 1533 coff_wr_u32(w, 0u); /* PointerToRelocations */ 1534 coff_wr_u32(w, 0u); /* PointerToLinenumbers */ 1535 coff_wr_u16(w, 0u); /* NumberOfRelocations */ 1536 coff_wr_u16(w, 0u); /* NumberOfLinenumbers */ 1537 coff_wr_u32(w, characteristics); 1538 } 1539 1540 /* ---- main entry ---- */ 1541 1542 void link_emit_coff(LinkImage* img, Writer* w) { 1543 Heap* heap = img->heap; 1544 Compiler* c = img->c; 1545 u16 machine = coff_machine_or_panic(c); 1546 if (img->entry_sym == LINK_SYM_NONE) 1547 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: no resolved entry symbol"); 1548 1549 /* ---- pass 1: build buckets + per-section delta map ---- */ 1550 CoffSection out[COFF_NBUCKETS]; 1551 CoffSecMap* map = (CoffSecMap*)heap->alloc( 1552 heap, sizeof(CoffSecMap) * (img->nsections + 1u), _Alignof(CoffSecMap)); 1553 if (!map && img->nsections) 1554 compiler_panic(c, SRCLOC_NONE, "link_emit_coff: oom on section map"); 1555 memset(map, 0, sizeof(CoffSecMap) * (img->nsections + 1u)); 1556 1557 /* coff_build_buckets stashes per-bucket allocation caps in size_raw; 1558 * we read them back into a local before size_raw is recomputed by 1559 * coff_assign_layout so the cleanup path can free with the right 1560 * size. */ 1561 coff_build_buckets(img, out, map); 1562 /* coff_build_buckets stashes per-bucket allocation caps in size_raw 1563 * (the only bucket field we own for the duration of layout); read 1564 * them out before coff_assign_layout overwrites the field. .reloc 1565 * and .idata aren't touched by coff_build_buckets — their caps are 1566 * filled in below once coff_build_reloc_section / coff_emit_idata 1567 * run. */ 1568 u32 bucket_caps[COFF_NBUCKETS]; 1569 u32 b; 1570 for (b = 0; b < COFF_NBUCKETS; ++b) bucket_caps[b] = out[b].size_raw; 1571 1572 /* ---- pass 1b: collect imports and reserve .idata + .text stubs ---- 1573 * 1574 * Builds the per-DLL / per-import layout and appends one IAT-routing 1575 * stub per imported function to the .text bucket. The .idata bucket 1576 * size is set here (so it counts in nsec); the stub vaddrs and 1577 * IAT-slot vaddrs are finalised after coff_assign_layout. */ 1578 CoffImportTable imports; 1579 int have_imports = coff_collect_imports(img, &imports); 1580 if (have_imports) { 1581 coff_plan_idata_layout(img, &imports); 1582 coff_append_stubs(img, &imports, &out[COFF_BUCKET_TEXT], 1583 &bucket_caps[COFF_BUCKET_TEXT]); 1584 /* Reserve the .idata bucket size so coff_assign_layout / nsec 1585 * accounting sees it. Actual bytes are written by coff_emit_idata 1586 * once the bucket RVA is known. */ 1587 out[COFF_BUCKET_IDATA].size = imports.idata_size; 1588 } 1589 1590 /* ---- pass 1c: plan the TLS directory record ---- 1591 * 1592 * If any SF_TLS sections survived, reserve 40 bytes at the tail of 1593 * .rdata for the IMAGE_TLS_DIRECTORY64. Bytes are zeroed now and 1594 * filled in by coff_emit_tls_dir once the bucket RVAs are final. */ 1595 CoffTlsLayout tls; 1596 coff_plan_tls_layout(img, out, &bucket_caps[COFF_BUCKET_RDATA], &tls); 1597 1598 /* ---- pass 2: decide whether .reloc will be in the image ---- 1599 * 1600 * The headers' file size (and therefore every section's file 1601 * offset) depends on the section-table entry count, so we need to 1602 * commit to "is .reloc emitted?" before laying out file offsets. 1603 * .reloc lights up iff at least one absolute VA reloc points into a kept 1604 * section, OR a TLS directory is emitted (its VA fields need base-relocs). 1605 * ARM64 Windows rejects fixed images (/dynamicbase:no), and x64 Windows 1606 * accepts ASLR images by default, so PE images advertise DYNAMIC_BASE when 1607 * this table is present instead of tying the table to the generic ELF/Mach-O 1608 * img->pie flag. */ 1609 int emit_reloc = 0; 1610 { 1611 u32 i; 1612 u32 nrel = LinkRelocs_count(&img->relocs); 1613 for (i = 0; i < nrel; ++i) { 1614 const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i); 1615 if (!coff_reloc_needs_base_reloc(r->kind)) continue; 1616 if (r->link_section_id == LINK_SEC_NONE) continue; 1617 emit_reloc = 1; 1618 break; 1619 } 1620 if (!emit_reloc && tls.present) emit_reloc = 1; 1621 } 1622 1623 u32 nsec = 0; 1624 for (b = 0; b < COFF_NBUCKETS; ++b) { 1625 if (b == COFF_BUCKET_RELOC) { 1626 if (emit_reloc) ++nsec; /* tentative; size set below */ 1627 continue; 1628 } 1629 if (out[b].size) ++nsec; 1630 } 1631 u32 headers_size_unpadded = PE_DOS_HDR_SIZE + PE_SIG_SIZE + PE_FILE_HDR_SIZE + 1632 PE_OPT_HDR_SIZE + nsec * PE_SECTION_HDR_SIZE; 1633 u32 headers_size_padded = 1634 (u32)ALIGN_UP((u64)headers_size_unpadded, (u64)PE_FILE_ALIGNMENT); 1635 1636 /* First layout pass: fixes RVAs / file offsets for buckets that 1637 * already have a finalised size (.text, .rdata, .idata, .data, .bss). 1638 * .reloc's RVA is provisional — it depends on .reloc's own size, 1639 * which is still 0 at this point. */ 1640 (void)coff_assign_layout(out, headers_size_padded, PE_FIRST_SECTION_RVA); 1641 1642 /* ---- pass 2b: emit .idata bytes + per-arch IAT stubs ---- 1643 * 1644 * The .idata bucket's RVA is final after the first assign_layout; 1645 * stubs need it (the indirect-jump displacement targets an IAT slot) 1646 * and .idata's own descriptor / ILT / IAT records all carry RVAs. 1647 * coff_import_vaddr_build builds the per-LinkSymId override table 1648 * that apply_all_relocs consults in place of the (zero) symbol 1649 * vaddr for imported targets. */ 1650 CoffImportVaddr import_vaddr; 1651 memset(&import_vaddr, 0, sizeof(import_vaddr)); 1652 if (have_imports) { 1653 coff_emit_idata(img, &imports, out, &bucket_caps[COFF_BUCKET_IDATA]); 1654 coff_emit_stubs(img, &imports, out); 1655 coff_import_vaddr_build(img, &imports, out, &import_vaddr); 1656 } 1657 1658 /* Write the TLS directory bytes now that bucket RVAs are final. */ 1659 coff_emit_tls_dir(img, out, map, &tls); 1660 1661 /* ---- pass 3: build .reloc using the now-final bucket RVAs ---- 1662 * 1663 * coff_build_reloc_section reads out[bucket].rva indirectly via 1664 * map[].new_rva + (write_vaddr - sec->vaddr) → site offset within 1665 * the bucket; the absolute site_rva is bucket.rva + that offset. 1666 * Patch site RVAs are page-quantised in the emitted blob, so this 1667 * is the spot where the bucket RVAs need to be already final. 1668 * 1669 * TLS directory's four absolute-VA fields ride into the entries via 1670 * the `extras` array — they aren't ordinary symbol relocations, so 1671 * they don't show up in img->relocs. */ 1672 if (emit_reloc) { 1673 CoffRelocEntry tls_extras[4]; 1674 u32 n_tls_extras = 0; 1675 if (tls.present) { 1676 u32 dir_rva = out[COFF_BUCKET_RDATA].rva + tls.dir_rdata_off; 1677 static const u32 field_offs[4] = { 1678 COFF_TLSDIR_OFF_START_ADDR, 1679 COFF_TLSDIR_OFF_END_ADDR, 1680 COFF_TLSDIR_OFF_INDEX_ADDR, 1681 COFF_TLSDIR_OFF_CALLBACKS, 1682 }; 1683 u32 k; 1684 for (k = 0; k < 4; ++k) { 1685 if (field_offs[k] == COFF_TLSDIR_OFF_CALLBACKS && !tls.callbacks_sym) 1686 continue; 1687 tls_extras[n_tls_extras].site_rva = dir_rva + field_offs[k]; 1688 tls_extras[n_tls_extras].type = (u16)IMAGE_REL_BASED_DIR64; 1689 tls_extras[n_tls_extras].pad = 0; 1690 ++n_tls_extras; 1691 } 1692 } 1693 coff_build_reloc_section(img, out, map, &out[COFF_BUCKET_RELOC], tls_extras, 1694 n_tls_extras); 1695 bucket_caps[COFF_BUCKET_RELOC] = out[COFF_BUCKET_RELOC].size_raw; 1696 /* size_raw was stashed by build; assign_layout below recomputes it 1697 * as the FileAlignment-padded length. */ 1698 (void)coff_assign_layout(out, headers_size_padded, PE_FIRST_SECTION_RVA); 1699 } 1700 1701 /* `_tls_used` is the public mingw/PE name for the TLS directory 1702 * record. Keep it in lockstep with the optional-header TLS data 1703 * directory, rather than leaving references bound to mingw's tlssup.o 1704 * placeholder record. */ 1705 coff_define_tls_used(img, out, &tls); 1706 1707 /* ---- pass 4: resolve entry symbol's PE RVA ---- 1708 * 1709 * Done before apply so the optional-header field has its final 1710 * value. */ 1711 const LinkSymbol* entry_sym = LinkSyms_at(&img->syms, img->entry_sym - 1); 1712 if (!entry_sym->defined || entry_sym->kind == SK_ABS) 1713 compiler_panic(c, SRCLOC_NONE, 1714 "link_emit_coff: entry symbol is not a defined " 1715 "image-relative function"); 1716 const LinkSection* entry_sec = coff_section_at(img, entry_sym->vaddr); 1717 if (!entry_sec) 1718 compiler_panic(c, SRCLOC_NONE, 1719 "link_emit_coff: entry symbol has no containing " 1720 "section"); 1721 u8 entry_bucket = map[entry_sec->id - 1].bucket; 1722 u32 entry_rva = out[entry_bucket].rva + map[entry_sec->id - 1].new_rva + 1723 (u32)(entry_sym->vaddr - entry_sec->vaddr); 1724 1725 /* ---- pass 5: apply all relocations into bucket bytes ---- */ 1726 coff_apply_all_relocs(img, out, map, have_imports ? &import_vaddr : NULL); 1727 1728 /* ---- pass 6: compute SizeOfImage (in-memory size) ---- */ 1729 u32 image_size = 0; 1730 for (b = 0; b < COFF_NBUCKETS; ++b) { 1731 if (!out[b].in_image) continue; 1732 u32 end = out[b].rva + out[b].size; 1733 if (end > image_size) image_size = end; 1734 } 1735 image_size = (u32)ALIGN_UP((u64)image_size, (u64)PE_SECTION_ALIGNMENT); 1736 1737 /* ---- pass 7: write everything ---- */ 1738 u16 file_chars = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE; 1739 int dynamic_base = out[COFF_BUCKET_RELOC].in_image; 1740 if (!dynamic_base) { 1741 file_chars |= IMAGE_FILE_RELOCS_STRIPPED; 1742 } 1743 1744 coff_write_dos_stub(w); 1745 /* PE signature. */ 1746 coff_wr_u32(w, IMAGE_NT_SIGNATURE); 1747 coff_write_file_header(w, machine, (u16)nsec, file_chars); 1748 u16 subsystem = img->linker ? img->linker->pe_subsystem : 0; 1749 coff_write_optional_header(w, entry_rva, out, headers_size_padded, image_size, 1750 dynamic_base, subsystem, 1751 have_imports ? &imports : NULL, &tls); 1752 1753 /* Section table. */ 1754 for (b = 0; b < COFF_NBUCKETS; ++b) { 1755 if (!out[b].in_image) continue; 1756 coff_write_section_header(w, out[b].name, out[b].size, out[b].rva, 1757 out[b].size_raw, out[b].file_offset, 1758 out[b].characteristics); 1759 } 1760 1761 /* Pad to first section's file offset. */ 1762 u64 cur = (u64)headers_size_unpadded; 1763 u64 first_file_off = headers_size_padded; 1764 if (cur < first_file_off) { 1765 coff_write_zeroes(w, first_file_off - cur); 1766 cur = first_file_off; 1767 } 1768 1769 /* Section bodies. */ 1770 for (b = 0; b < COFF_NBUCKETS; ++b) { 1771 if (!out[b].in_image) continue; 1772 if (!out[b].has_file_bytes) continue; 1773 if (cur < out[b].file_offset) { 1774 coff_write_zeroes(w, out[b].file_offset - cur); 1775 cur = out[b].file_offset; 1776 } 1777 kit_writer_write(w, out[b].bytes, out[b].size); 1778 cur += out[b].size; 1779 if (out[b].size_raw > out[b].size) { 1780 coff_write_zeroes(w, out[b].size_raw - out[b].size); 1781 cur += out[b].size_raw - out[b].size; 1782 } 1783 } 1784 1785 /* ---- cleanup ---- */ 1786 for (b = 0; b < COFF_NBUCKETS; ++b) { 1787 if (out[b].bytes) heap->free(heap, out[b].bytes, bucket_caps[b]); 1788 } 1789 heap->free(heap, map, sizeof(CoffSecMap) * (img->nsections + 1u)); 1790 if (have_imports) { 1791 coff_import_vaddr_free(img, &import_vaddr); 1792 coff_imports_free(img, &imports); 1793 } 1794 }