kit

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

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, &note_off) ||
    440         !detect_elf_read_uint(d, len, entry_off + size_off, value_width,
    441                               big_endian, &note_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 }