registry.c (36121B)
1 #include <kit/cg.h> 2 #include <kit/config.h> 3 #include <string.h> 4 5 #include "core/slice.h" 6 #include "obj/coff/coff.h" 7 #include "obj/elf/elf.h" 8 #include "obj/format.h" 9 #include "obj/macho/macho.h" 10 #include "obj/obj.h" 11 #include "obj/rewrite.h" 12 #include "obj/wasm/wasm.h" 13 14 #if KIT_LINK_ENABLED 15 void link_emit_elf(LinkImage*, Writer*); 16 void link_emit_macho(LinkImage*, Writer*); 17 void link_emit_coff(LinkImage*, Writer*); 18 void layout_dyn(Linker*, LinkImage*); 19 void link_dyn_state_free(LinkImage*); 20 #define OBJ_LINK_EMIT_ELF link_emit_elf 21 #define OBJ_LINK_EMIT_MACHO link_emit_macho 22 #define OBJ_LINK_EMIT_COFF link_emit_coff 23 #define OBJ_LAYOUT_DYN layout_dyn 24 #define OBJ_FREE_DYN link_dyn_state_free 25 #else 26 void obj_format_link_emit_disabled(LinkImage*, Writer*); 27 void obj_format_layout_dyn_disabled(Linker*, LinkImage*); 28 void obj_format_free_dyn_disabled(LinkImage*); 29 #define OBJ_LINK_EMIT_ELF obj_format_link_emit_disabled 30 #define OBJ_LINK_EMIT_MACHO obj_format_link_emit_disabled 31 #define OBJ_LINK_EMIT_COFF obj_format_link_emit_disabled 32 #define OBJ_LAYOUT_DYN obj_format_layout_dyn_disabled 33 #define OBJ_FREE_DYN obj_format_free_dyn_disabled 34 #endif 35 36 extern const ObjFormatEmuOps elf_emu_ops; 37 38 #if KIT_AR_ENABLED 39 int coff_classify_obj_input(Compiler*, ObjBuilder*, Sym* soname_out); 40 Sym coff_archive_hint(Compiler*, const char* archive_name); 41 ObjFormatArchiveAction coff_archive_member(Compiler*, 42 const ObjFormatArchiveMember*, 43 ObjBuilder** out); 44 #else 45 int obj_format_classify_obj_input_disabled(Compiler*, ObjBuilder*, 46 Sym* soname_out); 47 Sym obj_format_archive_hint_disabled(Compiler*, const char* archive_name); 48 ObjFormatArchiveAction obj_format_archive_member_disabled( 49 Compiler*, const ObjFormatArchiveMember*, ObjBuilder** out); 50 #define coff_classify_obj_input obj_format_classify_obj_input_disabled 51 #define coff_archive_hint obj_format_archive_hint_disabled 52 #define coff_archive_member obj_format_archive_member_disabled 53 #endif 54 55 #if KIT_LINK_ENABLED 56 void aa64_emit_macho_stub(u8* dst, u64 stub_vaddr, u64 got_slot_vaddr); 57 void aa64_emit_coff_iat_stub(u8* dst, u64 stub_vaddr, u64 iat_slot_vaddr); 58 void x64_emit_macho_stub(u8* dst, u64 stub_vaddr, u64 got_slot_vaddr); 59 void x64_emit_coff_iat_stub(u8* dst, u64 stub_vaddr, u64 iat_slot_vaddr); 60 /* COFF synthetic-input hook body. Needs Linker internals (LinkInput append, 61 * link_arch_desc_for for the __chkstk stub), so it lives in src/link; wired 62 * here as the only registry row that registers synth_inputs. */ 63 void link_synth_coff_ctor_dtor_list(Linker*); 64 #define OBJ_COFF_SYNTH_INPUTS link_synth_coff_ctor_dtor_list 65 #else 66 void obj_format_macho_stub_disabled(u8* dst, u64 stub_vaddr, 67 u64 got_slot_vaddr); 68 void obj_format_coff_stub_disabled(u8* dst, u64 stub_vaddr, u64 iat_slot_vaddr); 69 #define aa64_emit_macho_stub obj_format_macho_stub_disabled 70 #define aa64_emit_coff_iat_stub obj_format_coff_stub_disabled 71 #define x64_emit_macho_stub obj_format_macho_stub_disabled 72 #define x64_emit_coff_iat_stub obj_format_coff_stub_disabled 73 #define OBJ_COFF_SYNTH_INPUTS NULL 74 #endif 75 76 #if KIT_OBJ_ELF_ENABLED 77 static const ObjElfArchOps obj_elf_arch_ops[] = { 78 #if KIT_ARCH_AA64_ENABLED 79 { 80 .arch = KIT_ARCH_ARM_64, 81 .e_machine = EM_AARCH64, 82 .e_flags = 0, 83 .default_musl_interp = "/lib/ld-musl-aarch64.so.1", 84 .r_relative = ELF_R_AARCH64_RELATIVE, 85 .r_glob_dat = ELF_R_AARCH64_GLOB_DAT, 86 .r_jump_slot = ELF_R_AARCH64_JUMP_SLOT, 87 .r_irelative = ELF_R_AARCH64_IRELATIVE, 88 /* AAPCS64 variant-I: tp points at a 16-byte TCB ahead of the image. */ 89 .tls_tp_bias = 16u, 90 .reloc_to = elf_aarch64_reloc_to, 91 .reloc_from = elf_aarch64_reloc_from, 92 .reloc_name = elf_aarch64_reloc_name, 93 .float_abi_from_e_flags = NULL, 94 }, 95 #endif 96 #if KIT_ARCH_X64_ENABLED 97 { 98 .arch = KIT_ARCH_X86_64, 99 .e_machine = EM_X86_64, 100 .e_flags = 0, 101 .default_musl_interp = "/lib/ld-musl-x86_64.so.1", 102 .r_relative = ELF_R_X86_64_RELATIVE, 103 .r_glob_dat = ELF_R_X86_64_GLOB_DAT, 104 .r_jump_slot = ELF_R_X86_64_JUMP_SLOT, 105 .r_irelative = ELF_R_X86_64_IRELATIVE, 106 /* SysV variant-II: tp sits *past* the image; no positive TCB bias. */ 107 .tls_tp_bias = 0u, 108 .reloc_to = elf_x86_64_reloc_to, 109 .reloc_from = elf_x86_64_reloc_from, 110 .reloc_name = elf_x86_64_reloc_name, 111 .float_abi_from_e_flags = NULL, 112 }, 113 #endif 114 #if KIT_ARCH_RV64_ENABLED 115 { 116 .arch = KIT_ARCH_RV64, 117 .e_machine = EM_RISCV, 118 .e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE, 119 .default_musl_interp = "/lib/ld-musl-riscv64.so.1", 120 .r_relative = ELF_R_RISCV_RELATIVE, 121 .r_glob_dat = ELF_R_RISCV_64, 122 .r_jump_slot = ELF_R_RISCV_JUMP_SLOT, 123 .r_irelative = ELF_R_RISCV_IRELATIVE, 124 /* Variant-I: kit's freestanding start.c biases tp by a 16-byte TCB to 125 * match AArch64. Hosted RISC-V wants +0; that hosted-vs-freestanding 126 * split is applied by the caller (src/obj/elf/link.c). */ 127 .tls_tp_bias = 16u, 128 .reloc_to = elf_riscv64_reloc_to, 129 .reloc_from = elf_riscv64_reloc_from, 130 .reloc_name = elf_riscv_reloc_name, 131 .float_abi_from_e_flags = elf_riscv_float_abi_from_e_flags, 132 }, 133 #endif 134 #if KIT_ARCH_RV32_ENABLED 135 { 136 /* RV32 shares EM_RISCV with RV64; obj_elf_machine_class uses 137 * EI_CLASS to disambiguate. Default float ABI is ilp32f (SINGLE: 138 * float in FP regs, double always soft). Static-only — no musl 139 * interp; the dyn r_* fields are unused on the static path. */ 140 .arch = KIT_ARCH_RV32, 141 .e_machine = EM_RISCV, 142 .e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_SINGLE, 143 .default_musl_interp = NULL, 144 .r_relative = ELF_R_RISCV_RELATIVE, 145 .r_glob_dat = ELF_R_RISCV_32, 146 .r_jump_slot = ELF_R_RISCV_JUMP_SLOT, 147 .r_irelative = ELF_R_RISCV_IRELATIVE, 148 /* See RV64: freestanding +16 TCB; hosted split applied by caller. */ 149 .tls_tp_bias = 16u, 150 .reloc_to = elf_riscv32_reloc_to, 151 .reloc_from = elf_riscv32_reloc_from, 152 .reloc_name = elf_riscv_reloc_name, 153 .float_abi_from_e_flags = elf_riscv_float_abi_from_e_flags, 154 }, 155 #endif 156 #if KIT_ARCH_ARM32_ENABLED 157 { 158 /* arm-none-eabi: ELFCLASS32, EABI version 5, soft-float default. 159 * Static-only freestanding — no musl interp; the dyn r_* fields are 160 * unused on the static path. EM_ARM is unique (no 64-bit ARM here), 161 * so obj_elf_machine_class matches on e_machine alone. */ 162 .arch = KIT_ARCH_ARM_32, 163 .e_machine = EM_ARM, 164 /* EABI v5; soft-float objects carry no float-ABI bit (the ecosystem 165 * convention — clang/gas mark only hard-float). */ 166 .e_flags = EF_ARM_EABI_VER5, 167 .default_musl_interp = NULL, 168 /* AAELF32 mandates SHT_REL (addend in-field, no r_addend slot). 169 * Stock ARM linkers (ld.lld, GNU ld) expect `.rel.<name>`. */ 170 .uses_rel = 1, 171 .r_relative = ELF_R_ARM_NONE, 172 .r_glob_dat = ELF_R_ARM_NONE, 173 .r_jump_slot = ELF_R_ARM_NONE, 174 .r_irelative = ELF_R_ARM_NONE, 175 /* ARM EABI TLS variant-I: tp points at an 8-byte (2-word) TCB. */ 176 .tls_tp_bias = 8u, 177 .reloc_to = elf_arm_reloc_to, 178 .reloc_from = elf_arm_reloc_from, 179 .reloc_name = elf_arm_reloc_name, 180 .float_abi_from_e_flags = elf_arm_float_abi_from_e_flags, 181 .reloc_field_addend = elf_arm_reloc_field_addend, 182 /* ARM EABI emits a `.ARM.attributes` vendor section; the emitter 183 * synthesizes it via this hook instead of an e_machine == EM_ARM gate. */ 184 .build_attributes = elf_arm_build_attributes, 185 }, 186 #endif 187 #if !KIT_ARCH_AA64_ENABLED && !KIT_ARCH_X64_ENABLED && \ 188 !KIT_ARCH_RV64_ENABLED && !KIT_ARCH_RV32_ENABLED && !KIT_ARCH_ARM32_ENABLED 189 {.arch = KIT_ARCH_WASM}, 190 #endif 191 }; 192 193 static const ObjElfArchOps* obj_elf_arch(KitArchKind arch) { 194 u32 i; 195 for (i = 0; i < (u32)(sizeof obj_elf_arch_ops / sizeof obj_elf_arch_ops[0]); 196 ++i) { 197 if (obj_elf_arch_ops[i].arch == arch) return &obj_elf_arch_ops[i]; 198 } 199 return NULL; 200 } 201 202 static const ObjElfArchOps* obj_elf_machine(u32 e_machine) { 203 u32 i; 204 for (i = 0; i < (u32)(sizeof obj_elf_arch_ops / sizeof obj_elf_arch_ops[0]); 205 ++i) { 206 if (obj_elf_arch_ops[i].e_machine && 207 obj_elf_arch_ops[i].e_machine == e_machine) 208 return &obj_elf_arch_ops[i]; 209 } 210 return NULL; 211 } 212 213 /* EI_CLASS-aware variant. EM_RISCV is shared by RV32 (ELFCLASS32) and 214 * RV64 (ELFCLASS64); selecting by e_machine alone would always pick the 215 * first table entry. For RISC-V we narrow the match by ptr-class so the 216 * reader resolves the correct reloc_from table; non-RISC-V archs (one 217 * entry per e_machine) match on e_machine as before. */ 218 const ObjElfArchOps* obj_elf_machine_class(u32 e_machine, u8 ei_class) { 219 u32 i; 220 for (i = 0; i < (u32)(sizeof obj_elf_arch_ops / sizeof obj_elf_arch_ops[0]); 221 ++i) { 222 const ObjElfArchOps* a = &obj_elf_arch_ops[i]; 223 if (!a->e_machine || a->e_machine != e_machine) continue; 224 if (e_machine == EM_RISCV) { 225 int want32 = (ei_class == ELFCLASS32); 226 int is_rv32 = (a->arch == KIT_ARCH_RV32); 227 if (want32 != is_rv32) continue; 228 } 229 return a; 230 } 231 return NULL; 232 } 233 #else 234 const ObjElfArchOps* obj_elf_machine_class(u32 e_machine, u8 ei_class) { 235 (void)e_machine; 236 (void)ei_class; 237 return NULL; 238 } 239 #endif 240 241 #if KIT_OBJ_MACHO_ENABLED 242 static const ObjMachoArchOps obj_macho_arch_ops[] = { 243 #if KIT_ARCH_AA64_ENABLED 244 { 245 .arch = KIT_ARCH_ARM_64, 246 .cputype = CPU_TYPE_ARM64, 247 .cpusubtype = CPU_SUBTYPE_ARM64_ALL, 248 .stub_size = 12u, 249 .emit_stub = aa64_emit_macho_stub, 250 .reloc_to = macho_aarch64_reloc_to, 251 .reloc_pcrel = macho_aarch64_reloc_pcrel, 252 .reloc_length = macho_aarch64_reloc_length, 253 .reloc_decode = macho_aarch64_reloc_decode, 254 }, 255 #endif 256 #if KIT_ARCH_X64_ENABLED 257 { 258 .arch = KIT_ARCH_X86_64, 259 .cputype = CPU_TYPE_X86_64, 260 .cpusubtype = CPU_SUBTYPE_X86_64_ALL, 261 .stub_size = 6u, 262 .emit_stub = x64_emit_macho_stub, 263 .reloc_to = macho_x86_64_reloc_to, 264 .reloc_pcrel = macho_x86_64_reloc_pcrel, 265 .reloc_length = macho_x86_64_reloc_length, 266 .reloc_decode = macho_x86_64_reloc_decode, 267 }, 268 #endif 269 #if !KIT_ARCH_AA64_ENABLED && !KIT_ARCH_X64_ENABLED 270 {.arch = KIT_ARCH_WASM}, 271 #endif 272 }; 273 274 static const ObjMachoArchOps* obj_macho_arch(KitArchKind arch) { 275 u32 i; 276 for (i = 0; 277 i < (u32)(sizeof obj_macho_arch_ops / sizeof obj_macho_arch_ops[0]); 278 ++i) { 279 if (obj_macho_arch_ops[i].arch == arch) return &obj_macho_arch_ops[i]; 280 } 281 return NULL; 282 } 283 284 static const ObjMachoArchOps* obj_macho_cputype(u32 cputype) { 285 u32 i; 286 for (i = 0; 287 i < (u32)(sizeof obj_macho_arch_ops / sizeof obj_macho_arch_ops[0]); 288 ++i) { 289 if (obj_macho_arch_ops[i].cputype && 290 obj_macho_arch_ops[i].cputype == cputype) 291 return &obj_macho_arch_ops[i]; 292 } 293 return NULL; 294 } 295 #endif 296 297 #if KIT_OBJ_COFF_ENABLED 298 static const ObjCoffArchOps obj_coff_arch_ops[] = { 299 #if KIT_ARCH_AA64_ENABLED 300 { 301 .arch = KIT_ARCH_ARM_64, 302 .machine = IMAGE_FILE_MACHINE_ARM64, 303 .stub_size = 12u, 304 .emit_iat_stub = aa64_emit_coff_iat_stub, 305 .reloc_to = coff_aarch64_reloc_to, 306 .reloc_from = coff_aarch64_reloc_from, 307 }, 308 #endif 309 #if KIT_ARCH_X64_ENABLED 310 { 311 .arch = KIT_ARCH_X86_64, 312 .machine = IMAGE_FILE_MACHINE_AMD64, 313 .stub_size = 6u, 314 .emit_iat_stub = x64_emit_coff_iat_stub, 315 .reloc_to = coff_x86_64_reloc_to, 316 .reloc_from = coff_x86_64_reloc_from, 317 }, 318 #endif 319 #if !KIT_ARCH_AA64_ENABLED && !KIT_ARCH_X64_ENABLED 320 {.arch = KIT_ARCH_WASM}, 321 #endif 322 }; 323 324 static const ObjCoffArchOps* obj_coff_arch(KitArchKind arch) { 325 u32 i; 326 for (i = 0; i < (u32)(sizeof obj_coff_arch_ops / sizeof obj_coff_arch_ops[0]); 327 ++i) { 328 if (obj_coff_arch_ops[i].arch == arch) return &obj_coff_arch_ops[i]; 329 } 330 return NULL; 331 } 332 333 static const ObjCoffArchOps* obj_coff_machine(u16 machine) { 334 u32 i; 335 if (machine == 0xA641u) machine = IMAGE_FILE_MACHINE_ARM64; 336 for (i = 0; i < (u32)(sizeof obj_coff_arch_ops / sizeof obj_coff_arch_ops[0]); 337 ++i) { 338 if (obj_coff_arch_ops[i].machine && obj_coff_arch_ops[i].machine == machine) 339 return &obj_coff_arch_ops[i]; 340 } 341 return NULL; 342 } 343 #endif 344 345 /* ---- Target detection from header bytes (ObjFormatImpl.detect_target) ---- 346 * 347 * Each detector resolves the build target an object encodes. They live here, 348 * next to the arch reverse-maps they consume, so a format compiled out of the 349 * build drops its detector with it; kit_detect_target (src/api) is then a pure 350 * registry dispatch with no format-identity #if. */ 351 #if KIT_OBJ_ELF_ENABLED || KIT_OBJ_MACHO_ENABLED || KIT_OBJ_COFF_ENABLED 352 static void detect_target_defaults(KitTargetSpec* t) { 353 t->big_endian = 0; 354 t->pic = KIT_PIC_NONE; 355 t->code_model = KIT_CM_DEFAULT; 356 t->float_abi = KIT_FLOAT_ABI_DEFAULT; 357 } 358 359 /* kit_arch_ptr_size (cg.h) is the single source of truth for an arch's byte 360 * pointer width; ptr_align tracks ptr_size for every arch these detectors 361 * resolve. The wasm detector sets its own spec and does not call this. */ 362 static void detect_set_ptr(KitTargetSpec* t, KitArchKind arch) { 363 uint8_t w = kit_arch_ptr_size(arch); 364 t->arch = arch; 365 t->ptr_size = w; 366 t->ptr_align = w; 367 } 368 #endif 369 370 #if KIT_OBJ_ELF_ENABLED 371 static int detect_elf_read_uint(const u8* d, size_t len, u64 off, u32 width, 372 int big_endian, u64* out) { 373 u64 value = 0; 374 size_t pos; 375 if (!out || (width != 2u && width != 4u && width != 8u) || off > len || 376 width > len - (size_t)off) 377 return 0; 378 pos = (size_t)off; 379 for (u32 i = 0; i < width; ++i) { 380 u32 shift = big_endian ? (width - i - 1u) * 8u : i * 8u; 381 value |= (u64)d[pos + i] << shift; 382 } 383 *out = value; 384 return 1; 385 } 386 387 /* FreeBSD toolchains commonly leave EI_OSABI at System V, including on 388 * shared libraries such as libc.so.7, and carry the platform identity in an 389 * NT_FREEBSD_ABI_TAG note instead. The object registry owns target identity, 390 * so recognize that standard note here rather than teaching the linker a 391 * filename/sysroot exception. */ 392 static int detect_elf_note_region_is_freebsd(const u8* d, size_t len, u64 off, 393 u64 size, int big_endian) { 394 static const u8 owner[8] = {'F', 'r', 'e', 'e', 'B', 'S', 'D', 0}; 395 size_t pos, end; 396 if (off > len || size > len - (size_t)off) return 0; 397 pos = (size_t)off; 398 end = pos + (size_t)size; 399 while (end - pos >= 12u) { 400 u64 namesz64, descsz64, type64; 401 size_t namesz, descsz, name_span, desc_span, entry_size; 402 if (!detect_elf_read_uint(d, len, pos, 4u, big_endian, &namesz64) || 403 !detect_elf_read_uint(d, len, pos + 4u, 4u, big_endian, &descsz64) || 404 !detect_elf_read_uint(d, len, pos + 8u, 4u, big_endian, &type64)) 405 return 0; 406 namesz = (size_t)namesz64; 407 descsz = (size_t)descsz64; 408 name_span = (namesz + 3u) & ~(size_t)3u; 409 desc_span = (descsz + 3u) & ~(size_t)3u; 410 if (name_span < namesz || desc_span < descsz || 411 name_span > end - pos - 12u || 412 desc_span > end - pos - 12u - name_span) 413 return 0; 414 entry_size = 12u + name_span + desc_span; 415 if (type64 == NT_FREEBSD_ABI_TAG && namesz == sizeof owner && 416 memcmp(d + pos + 12u, owner, sizeof owner) == 0) 417 return 1; 418 pos += entry_size; 419 } 420 return 0; 421 } 422 423 static int detect_elf_table_has_freebsd_note( 424 const u8* d, size_t len, u64 table_off, u64 entry_size, u64 entry_count, 425 u32 type_off, u32 note_type, u32 offset_off, u32 size_off, u32 value_width, 426 int big_endian) { 427 u64 min_size = (u64)size_off + value_width; 428 if (entry_size < min_size || table_off > len || entry_size == 0u) return 0; 429 for (u64 i = 0; i < entry_count; ++i) { 430 u64 entry_off, kind, note_off, note_size; 431 if (i > ((u64)len - table_off) / entry_size) break; 432 entry_off = table_off + i * entry_size; 433 if (entry_off > len || entry_size > (u64)len - entry_off) break; 434 if (!detect_elf_read_uint(d, len, entry_off + type_off, 4u, big_endian, 435 &kind) || 436 kind != note_type) 437 continue; 438 if (!detect_elf_read_uint(d, len, entry_off + offset_off, value_width, 439 big_endian, ¬e_off) || 440 !detect_elf_read_uint(d, len, entry_off + size_off, value_width, 441 big_endian, ¬e_size)) 442 continue; 443 if (detect_elf_note_region_is_freebsd(d, len, note_off, note_size, 444 big_endian)) 445 return 1; 446 } 447 return 0; 448 } 449 450 static int detect_elf_has_freebsd_note(const u8* d, size_t len, u8 ei_class, 451 u8 ei_data) { 452 int big_endian = ei_data == ELFDATA2MSB; 453 u64 phoff, shoff, phentsize, phnum, shentsize, shnum; 454 u32 phoff_off, shoff_off, phentsize_off, phnum_off, shentsize_off, 455 shnum_off, word_width, ph_offset_off, ph_size_off, sh_offset_off, 456 sh_size_off; 457 if (ei_class == ELFCLASS64) { 458 phoff_off = 32u; 459 shoff_off = 40u; 460 phentsize_off = 54u; 461 phnum_off = 56u; 462 shentsize_off = 58u; 463 shnum_off = 60u; 464 word_width = 8u; 465 ph_offset_off = 8u; 466 ph_size_off = 32u; 467 sh_offset_off = 24u; 468 sh_size_off = 32u; 469 } else if (ei_class == ELFCLASS32) { 470 phoff_off = 28u; 471 shoff_off = 32u; 472 phentsize_off = 42u; 473 phnum_off = 44u; 474 shentsize_off = 46u; 475 shnum_off = 48u; 476 word_width = 4u; 477 ph_offset_off = 4u; 478 ph_size_off = 16u; 479 sh_offset_off = 16u; 480 sh_size_off = 20u; 481 } else { 482 return 0; 483 } 484 if (!detect_elf_read_uint(d, len, phoff_off, word_width, big_endian, 485 &phoff) || 486 !detect_elf_read_uint(d, len, shoff_off, word_width, big_endian, 487 &shoff) || 488 !detect_elf_read_uint(d, len, phentsize_off, 2u, big_endian, 489 &phentsize) || 490 !detect_elf_read_uint(d, len, phnum_off, 2u, big_endian, &phnum) || 491 !detect_elf_read_uint(d, len, shentsize_off, 2u, big_endian, 492 &shentsize) || 493 !detect_elf_read_uint(d, len, shnum_off, 2u, big_endian, &shnum)) 494 return 0; 495 496 if (detect_elf_table_has_freebsd_note( 497 d, len, phoff, phentsize, phnum, 0u, PT_NOTE, ph_offset_off, 498 ph_size_off, word_width, big_endian)) 499 return 1; 500 return detect_elf_table_has_freebsd_note( 501 d, len, shoff, shentsize, shnum, 4u, SHT_NOTE, sh_offset_off, 502 sh_size_off, word_width, big_endian); 503 } 504 505 static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) { 506 u8 ei_class, ei_data, ei_osabi; 507 u16 e_machine; 508 if (len < 20) return KIT_MALFORMED; 509 ei_class = d[4]; 510 ei_data = d[5]; 511 ei_osabi = d[7]; 512 if (ei_data == 1) { 513 e_machine = (u16)d[18] | ((u16)d[19] << 8); 514 } else if (ei_data == 2) { 515 e_machine = (u16)d[19] | ((u16)d[18] << 8); 516 } else { 517 return KIT_MALFORMED; 518 } 519 520 detect_target_defaults(out); 521 out->big_endian = (ei_data == 2); 522 out->obj = KIT_OBJ_ELF; 523 524 /* Resolve the arch through the ELF format's machine reverse map 525 * (obj_elf_machine_class also splits EM_RISCV into RV32/RV64 by 526 * EI_CLASS). The registry models only the link/codegen arches, so the 527 * legacy 32-bit ABI-classifiable machines it does not carry (EM_386, 528 * EM_ARM) are mapped here explicitly to preserve detection. */ 529 { 530 const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); 531 if (ops) { 532 detect_set_ptr(out, ops->arch); 533 } else if (e_machine == 0x03) { /* EM_386 */ 534 detect_set_ptr(out, KIT_ARCH_X86_32); 535 } else if (e_machine == 0x28) { /* EM_ARM */ 536 detect_set_ptr(out, KIT_ARCH_ARM_32); 537 } else { 538 return KIT_UNSUPPORTED; 539 } 540 } 541 /* EI_CLASS must agree with the arch's pointer width: 32-bit arches are 542 * ELFCLASS32, 64-bit arches ELFCLASS64. EM_RISCV is already disambiguated 543 * by class above; this also rejects a class/machine mismatch such as a 544 * 64-bit arch object whose EI_CLASS byte claims ELFCLASS32. */ 545 if (ei_class != ((out->ptr_size == 4) ? 1u : 2u)) return KIT_MALFORMED; 546 if (ei_osabi == ELFOSABI_NONE && 547 detect_elf_has_freebsd_note(d, len, ei_class, ei_data)) 548 out->os = KIT_OS_FREEBSD; 549 else if (ei_osabi == ELFOSABI_NONE || ei_osabi == ELFOSABI_GNU) 550 out->os = KIT_OS_LINUX; 551 else if (ei_osabi == ELFOSABI_FREEBSD) 552 out->os = KIT_OS_FREEBSD; 553 else 554 out->os = KIT_OS_FREESTANDING; 555 556 /* Recover the float ABI from e_flags via the per-arch decoder so a detected 557 * target selects the matching runtime variant (rv32 ilp32 soft vs ilp32f 558 * single share an arch + pointer width and differ only here). e_flags is a 559 * 4-byte LE field after the three native-width addr fields: offset 36 on 560 * ELFCLASS32, 48 on ELFCLASS64. Only RISC-V supplies a decoder 561 * (float_abi_from_e_flags non-NULL); other arches leave the default. The 562 * decoder is only consulted when the header carries the full e_flags word, 563 * leaving DEFAULT (the wildcard) when it is truncated. */ 564 { 565 const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); 566 if (ops && ops->float_abi_from_e_flags) { 567 size_t flags_off = (ei_class == 1) ? 36u : 48u; 568 if (len >= flags_off + 4u) { 569 u32 e_flags; 570 if (ei_data == 1) 571 e_flags = (u32)d[flags_off] | ((u32)d[flags_off + 1] << 8) | 572 ((u32)d[flags_off + 2] << 16) | 573 ((u32)d[flags_off + 3] << 24); 574 else 575 e_flags = (u32)d[flags_off + 3] | ((u32)d[flags_off + 2] << 8) | 576 ((u32)d[flags_off + 1] << 16) | ((u32)d[flags_off] << 24); 577 out->float_abi = (u8)ops->float_abi_from_e_flags(e_flags); 578 } 579 } 580 } 581 return KIT_OK; 582 } 583 #endif 584 585 #if KIT_OBJ_COFF_ENABLED 586 /* Resolve a COFF Machine number to a KitArchKind through the registry's 587 * coff_machine reverse map (which aliases ARM64EC -> ARM64). The registry 588 * models only the link/codegen arches (AMD64 / ARM64); the legacy 589 * ABI-classifiable machines it does not carry are mapped explicitly to 590 * preserve detection. Returns 1 and writes *out on success; 0 if the 591 * machine is unsupported. Shared by the bare-.obj and PE-image paths. */ 592 static int coff_machine_to_arch(u16 machine, KitArchKind* out) { 593 const ObjCoffArchOps* ops = obj_coff_machine(machine); 594 if (ops) { 595 *out = ops->arch; 596 } else if (machine == 0x014Cu) { /* IMAGE_FILE_MACHINE_I386 */ 597 *out = KIT_ARCH_X86_32; 598 } else if (machine == 0x01C4u) { /* IMAGE_FILE_MACHINE_ARMNT */ 599 *out = KIT_ARCH_ARM_32; 600 } else if (machine == 0x5032u) { /* IMAGE_FILE_MACHINE_RISCV32 */ 601 *out = KIT_ARCH_RV32; 602 } else if (machine == 0x5064u) { /* IMAGE_FILE_MACHINE_RISCV64 */ 603 *out = KIT_ARCH_RV64; 604 } else { 605 return 0; 606 } 607 return 1; 608 } 609 610 /* COFF covers both a bare .obj (COFF Machine word at offset 0) and a linked 611 * PE image (DOS 'MZ' stub + "PE\0\0" signature, with the Machine word at 612 * e_lfanew+4). kit_detect_fmt classifies the PE image as KIT_BIN_PE and the 613 * dispatcher routes it here, so this one detector handles both by branching 614 * on the 'MZ' magic. */ 615 static KitStatus detect_coff(const u8* d, size_t len, KitTargetSpec* out) { 616 u16 machine; 617 KitArchKind arch; 618 if (len >= COFF_IMPORT_OBJECT_HEADER_SIZE && 619 coff_rd_u16(d + 0u) == IMPORT_OBJECT_HDR_SIG1 && 620 coff_rd_u16(d + 2u) == IMPORT_OBJECT_HDR_SIG2) { 621 /* Microsoft short-import members put Machine at offset 6 rather than in 622 * the ordinary COFF header's first word. They still participate in the 623 * public link-session target check before the archive handler consumes 624 * their import metadata. */ 625 machine = coff_rd_u16(d + 6u); 626 } else if (len >= 2 && d[0] == 'M' && d[1] == 'Z') { 627 u32 e_lfanew, pe_sig; 628 if (len < 64) return KIT_MALFORMED; /* DOS header */ 629 e_lfanew = (u32)d[60] | ((u32)d[61] << 8) | ((u32)d[62] << 16) | 630 ((u32)d[63] << 24); 631 /* Need the 4-byte PE signature + the 20-byte IMAGE_FILE_HEADER. */ 632 if ((u64)e_lfanew + 4u + 20u > (u64)len) return KIT_MALFORMED; 633 pe_sig = (u32)d[e_lfanew] | ((u32)d[e_lfanew + 1] << 8) | 634 ((u32)d[e_lfanew + 2] << 16) | ((u32)d[e_lfanew + 3] << 24); 635 if (pe_sig != 0x00004550u) return KIT_MALFORMED; /* "PE\0\0" */ 636 machine = (u16)d[e_lfanew + 4] | ((u16)d[e_lfanew + 5] << 8); 637 } else { 638 if (len < 2) return KIT_MALFORMED; 639 machine = (u16)d[0] | ((u16)d[1] << 8); 640 } 641 if (!coff_machine_to_arch(machine, &arch)) return KIT_UNSUPPORTED; 642 detect_target_defaults(out); 643 out->obj = KIT_OBJ_COFF; 644 out->os = KIT_OS_WINDOWS; 645 detect_set_ptr(out, arch); 646 return KIT_OK; 647 } 648 #endif 649 650 #if KIT_OBJ_MACHO_ENABLED 651 static KitStatus detect_macho(const u8* d, size_t len, KitTargetSpec* out) { 652 u32 magic, cputype; 653 KitOSKind os = KIT_OS_MACOS; 654 int swap; 655 if (len < 8) return KIT_MALFORMED; 656 magic = (u32)d[0] | ((u32)d[1] << 8) | ((u32)d[2] << 16) | ((u32)d[3] << 24); 657 switch (magic) { 658 case 0xFEEDFACEu: 659 case 0xFEEDFACFu: 660 swap = 0; 661 break; 662 case 0xCEFAEDFEu: 663 case 0xCFFAEDFEu: 664 swap = 1; 665 break; 666 default: 667 return KIT_MALFORMED; 668 } 669 if (!swap) { 670 cputype = 671 (u32)d[4] | ((u32)d[5] << 8) | ((u32)d[6] << 16) | ((u32)d[7] << 24); 672 } else { 673 cputype = 674 (u32)d[7] | ((u32)d[6] << 8) | ((u32)d[5] << 16) | ((u32)d[4] << 24); 675 } 676 detect_target_defaults(out); 677 out->obj = KIT_OBJ_MACHO; 678 if (!swap && len >= MACHO_HDR64_SIZE) { 679 u32 ncmds = (u32)d[16] | ((u32)d[17] << 8) | ((u32)d[18] << 16) | 680 ((u32)d[19] << 24); 681 u32 sizeofcmds = (u32)d[20] | ((u32)d[21] << 8) | ((u32)d[22] << 16) | 682 ((u32)d[23] << 24); 683 u64 pos = MACHO_HDR64_SIZE; 684 u64 end = pos + sizeofcmds; 685 if (end <= len) { 686 u32 i; 687 for (i = 0; i < ncmds && pos + 16u <= end; ++i) { 688 u32 cmd = (u32)d[pos] | ((u32)d[pos + 1] << 8) | 689 ((u32)d[pos + 2] << 16) | ((u32)d[pos + 3] << 24); 690 u32 cmdsize = (u32)d[pos + 4] | ((u32)d[pos + 5] << 8) | 691 ((u32)d[pos + 6] << 16) | 692 ((u32)d[pos + 7] << 24); 693 if (cmdsize < 8u || pos + cmdsize > end) break; 694 if (cmd == LC_BUILD_VERSION && cmdsize >= 24u) { 695 u32 platform = (u32)d[pos + 8] | ((u32)d[pos + 9] << 8) | 696 ((u32)d[pos + 10] << 16) | 697 ((u32)d[pos + 11] << 24); 698 os = macho_os_for_platform(platform); 699 break; 700 } 701 pos += cmdsize; 702 } 703 } 704 } 705 out->os = os; 706 707 /* Resolve the arch through the Mach-O format's cputype reverse map. The 708 * registry models only the link/codegen arches (ARM64 / X86_64); the 709 * legacy 32-bit ABI-classifiable cputypes it does not carry (CPU_TYPE_X86, 710 * CPU_TYPE_ARM) are mapped explicitly to preserve detection. */ 711 { 712 const ObjMachoArchOps* ops = obj_macho_cputype(cputype); 713 if (ops) { 714 detect_set_ptr(out, ops->arch); 715 } else if (cputype == 0x00000007u) { /* CPU_TYPE_X86 */ 716 detect_set_ptr(out, KIT_ARCH_X86_32); 717 } else if (cputype == 0x0000000Cu) { /* CPU_TYPE_ARM */ 718 detect_set_ptr(out, KIT_ARCH_ARM_32); 719 } else { 720 return KIT_UNSUPPORTED; 721 } 722 } 723 return KIT_OK; 724 } 725 #endif 726 727 #if KIT_OBJ_WASM_ENABLED 728 static KitStatus detect_wasm(const u8* d, size_t len, KitTargetSpec* out) { 729 KitTargetSpec t; 730 (void)d; 731 (void)len; 732 t.big_endian = 0; 733 t.pic = KIT_PIC_NONE; 734 t.code_model = KIT_CM_DEFAULT; 735 t.float_abi = KIT_FLOAT_ABI_DEFAULT; 736 t.arch = KIT_ARCH_WASM; 737 t.ptr_size = 4; 738 t.ptr_align = 4; 739 t.obj = KIT_OBJ_WASM; 740 t.os = KIT_OS_WASI; 741 *out = t; 742 return KIT_OK; 743 } 744 745 static const ObjFormatImpl obj_format_impl_wasm = { 746 .kind = KIT_OBJ_WASM, 747 .name = "wasm", 748 /* read_wasm parses a core module into sections + function symbols for 749 * inspection (objdump -f/-h/-t/-s/-d). It is not a linkable-object reader: 750 * tool-conventions `linking` / `reloc.*` support is still pending, so 751 * relocations are not recovered. */ 752 .read_name = "read_wasm", 753 .read_dso_name = NULL, 754 .emit = emit_wasm, 755 .read = read_wasm, 756 .read_dso = NULL, 757 .link_emit = NULL, 758 .c_label_prefix = "", 759 .default_entry_name = "_start", 760 .carries_file_only_debug = 0, 761 .builds_own_static_got = 0, 762 .weak_undef_pulls_archive_member = 0, 763 .global_archive_fixpoint = 0, 764 .tls_symbol_features = 0, 765 .alias_via_thunk = 0, 766 .weak_undef_attr = "weak", 767 /* Wasm has no TLS model: a module instance owns one linear memory, so a 768 * thread-local is an ordinary data object. Keep `.tdata`/`.tbss` (laid 769 * out like `.data`); tls_addr_of lowers to a plain symbol address. */ 770 .secname_tdata = ".tdata", 771 .secname_tbss = ".tbss", 772 .detect_target = detect_wasm, 773 }; 774 #endif 775 776 #if KIT_OBJ_ELF_ENABLED 777 static const ObjFormatImpl obj_format_impl_elf = { 778 .kind = KIT_OBJ_ELF, 779 .name = "elf", 780 .read_name = "read_elf", 781 .read_dso_name = "read_elf_dso", 782 .emit = emit_elf, 783 .read = read_elf, 784 .read_dso = read_elf_dso, 785 .link_emit = OBJ_LINK_EMIT_ELF, 786 .layout_dyn = OBJ_LAYOUT_DYN, 787 .free_dyn = OBJ_FREE_DYN, 788 .emu = &elf_emu_ops, 789 .c_label_prefix = "", 790 .default_entry_name = "_start", 791 .carries_file_only_debug = 1, 792 .builds_own_static_got = 0, 793 .weak_undef_pulls_archive_member = 0, 794 .global_archive_fixpoint = 0, 795 .tls_symbol_features = 1, 796 .alias_via_thunk = 0, 797 .weak_undef_attr = "weak", 798 .secname_init_array = ".init_array", 799 .secname_fini_array = ".fini_array", 800 .secname_preinit_array = ".preinit_array", 801 .secname_tdata = ".tdata", 802 .secname_tbss = ".tbss", 803 .elf_arch = obj_elf_arch, 804 .elf_machine = obj_elf_machine, 805 .detect_target = detect_elf, 806 .rewrite_linked = obj_rewrite_elf, 807 }; 808 #endif 809 810 #if KIT_OBJ_MACHO_ENABLED 811 static const ObjFormatImpl obj_format_impl_macho = { 812 .kind = KIT_OBJ_MACHO, 813 .name = "macho", 814 .read_name = "read_macho", 815 .read_dso_name = "read_macho_dso", 816 .emit = emit_macho, 817 .read = read_macho, 818 .read_dso = read_macho_dso, 819 .link_emit = OBJ_LINK_EMIT_MACHO, 820 .split_sections_as_atoms = 1, 821 .c_label_prefix = "_", 822 .default_entry_name = "_main", 823 .carries_file_only_debug = 1, 824 .builds_own_static_got = 1, 825 .weak_undef_pulls_archive_member = 0, 826 .global_archive_fixpoint = 0, 827 .tls_symbol_features = 1, 828 .alias_via_thunk = 1, 829 .weak_undef_attr = "weak_import", 830 .secname_init_array = "__DATA,__mod_init_func", 831 .secname_fini_array = "__DATA,__mod_term_func", 832 /* Mach-O has no .preinit_array analogue — dyld runs S_MOD_INIT_FUNC 833 * pointers only — so preinit ctors are not representable (NULL panics 834 * until the linker routes them through __mod_init_func). */ 835 .secname_preinit_array = NULL, 836 .secname_tdata = "__DATA,__thread_data", 837 .secname_tbss = "__DATA,__thread_bss", 838 .macho_arch = obj_macho_arch, 839 .macho_cputype = obj_macho_cputype, 840 .detect_target = detect_macho, 841 .rewrite_linked = obj_rewrite_macho, 842 }; 843 #endif 844 845 #if KIT_OBJ_COFF_ENABLED 846 static const ObjFormatImpl obj_format_impl_coff = { 847 .kind = KIT_OBJ_COFF, 848 .name = "coff", 849 .read_name = "read_coff", 850 .read_dso_name = "read_coff_dso", 851 .emit = emit_coff, 852 .read = read_coff, 853 .read_dso = read_coff_dso, 854 .link_emit = OBJ_LINK_EMIT_COFF, 855 .c_label_prefix = "", 856 .default_entry_name = "mainCRTStartup", 857 .carries_file_only_debug = 0, 858 .builds_own_static_got = 0, 859 .weak_undef_pulls_archive_member = 1, 860 .global_archive_fixpoint = 1, 861 .tls_symbol_features = 0, 862 .alias_via_thunk = 0, 863 .weak_undef_attr = "weak", 864 .weak_extern_underscore_alias = 1, 865 /* CRT scans `.CRT$X[A-Z]` for ctor/dtor tables: XCU is the user-ctor 866 * bucket, XPU the user-dtor bucket, XIA user pre-init just after the 867 * CRT's own `.CRT$XI*` setup. TLS uses the MSVC `.tls$` convention 868 * (linker concatenates `.tls$*` sorted by suffix; ZZZ sorts last so 869 * `.tbss` lands at the tail zero-fill). See doc/OBJ.md. */ 870 .secname_init_array = ".CRT$XCU", 871 .secname_fini_array = ".CRT$XPU", 872 .secname_preinit_array = ".CRT$XIA", 873 .secname_tdata = ".tls$", 874 .secname_tbss = ".tls$ZZZ", 875 /* synth_inputs: the COFF __CTOR_LIST__/__DTOR_LIST__ + __chkstk 876 * synthesizer (link_synth_coff_ctor_dtor_list) genuinely needs Linker 877 * internals (LinkInput append, link_arch_desc_for); it cannot be 878 * expressed via ObjBuilder/Compiler/obj APIs alone. Wired to the 879 * src/link body (NULL when the linker subsystem is compiled out). */ 880 .synth_inputs = OBJ_COFF_SYNTH_INPUTS, 881 .coff_arch = obj_coff_arch, 882 .coff_machine = obj_coff_machine, 883 .detect_target = detect_coff, 884 .rewrite_linked = obj_rewrite_coff, 885 .classify_obj_input = coff_classify_obj_input, 886 .archive_hint = coff_archive_hint, 887 .archive_member = coff_archive_member, 888 }; 889 #endif 890 891 static const ObjFormatImpl* const obj_format_impls[] = { 892 #if KIT_OBJ_ELF_ENABLED 893 &obj_format_impl_elf, 894 #endif 895 #if KIT_OBJ_COFF_ENABLED 896 &obj_format_impl_coff, 897 #endif 898 #if KIT_OBJ_MACHO_ENABLED 899 &obj_format_impl_macho, 900 #endif 901 #if KIT_OBJ_WASM_ENABLED 902 &obj_format_impl_wasm, 903 #endif 904 }; 905 906 const ObjFormatImpl* obj_format_lookup(ObjFmt fmt) { 907 u32 i; 908 for (i = 0; i < (u32)(sizeof obj_format_impls / sizeof obj_format_impls[0]); 909 ++i) { 910 if (obj_format_impls[i]->kind == fmt) return obj_format_impls[i]; 911 } 912 return NULL; 913 } 914 915 const ObjFormatImpl* obj_format_lookup_bin(KitBinFmt fmt) { 916 switch (fmt) { 917 case KIT_BIN_ELF: 918 return obj_format_lookup(KIT_OBJ_ELF); 919 case KIT_BIN_COFF: 920 return obj_format_lookup(KIT_OBJ_COFF); 921 case KIT_BIN_MACHO: 922 return obj_format_lookup(KIT_OBJ_MACHO); 923 case KIT_BIN_WASM: 924 return obj_format_lookup(KIT_OBJ_WASM); 925 default: 926 return NULL; 927 } 928 } 929 930 /* Name<->KitObjFmt table. The canonical name comes first; trailing 931 * entries are accepted aliases (objcopy/objdump bfdname spellings). The 932 * canonical name for a fmt is always the first row whose `fmt` matches. */ 933 typedef struct ObjFmtNameRow { 934 KitObjFmt fmt; 935 const char* name; 936 } ObjFmtNameRow; 937 938 static const ObjFmtNameRow obj_fmt_names[] = { 939 {KIT_OBJ_ELF, "elf"}, {KIT_OBJ_COFF, "coff"}, {KIT_OBJ_COFF, "pe"}, 940 {KIT_OBJ_MACHO, "macho"}, {KIT_OBJ_WASM, "wasm"}, 941 }; 942 943 int obj_format_fmt_from_name(const char* name, KitObjFmt* out) { 944 u32 i; 945 if (!name) return 0; 946 for (i = 0; i < (u32)(sizeof obj_fmt_names / sizeof obj_fmt_names[0]); ++i) { 947 if (strcmp(obj_fmt_names[i].name, name) == 0) { 948 if (out) *out = obj_fmt_names[i].fmt; 949 return 1; 950 } 951 } 952 return 0; 953 } 954 955 const char* obj_format_fmt_name(KitObjFmt fmt) { 956 u32 i; 957 for (i = 0; i < (u32)(sizeof obj_fmt_names / sizeof obj_fmt_names[0]); ++i) { 958 if (obj_fmt_names[i].fmt == fmt) return obj_fmt_names[i].name; 959 } 960 return NULL; 961 } 962 963 int obj_format_dso_reader_for_bytes(const u8* data, size_t len, 964 KitBinFmt* bin_out, 965 ObjFormatDsoReader* out) { 966 const ObjFormatImpl* fmt; 967 KitBinFmt bin; 968 if (!out) return 0; 969 memset(out, 0, sizeof(*out)); 970 if (bin_out) *bin_out = KIT_BIN_UNKNOWN; 971 if (!data) return 0; 972 973 #if KIT_OBJ_MACHO_ENABLED 974 if (len >= 3 && data[0] == '-' && data[1] == '-' && data[2] == '-') { 975 out->format = &obj_format_impl_macho; 976 out->read = read_tbd; 977 out->name = "read_tbd"; 978 return 1; 979 } 980 #endif 981 982 bin = kit_detect_fmt(data, len); 983 if (bin_out) *bin_out = bin; 984 fmt = (bin == KIT_BIN_PE) ? obj_format_lookup(KIT_OBJ_COFF) 985 : obj_format_lookup_bin(bin); 986 if (!fmt || !fmt->read_dso) return 0; 987 out->format = fmt; 988 out->read = fmt->read_dso; 989 out->name = fmt->read_dso_name; 990 return 1; 991 }