kit

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

commit 5660104434d3541184d794eff0f8b1a0af859bb1
parent 25d4039b4512c3bcc2ba654f684d7e3eb4e56519
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 12 Jun 2026 09:45:45 -0700

refactor(obj): move object target-detection into the format vtable

Per-format header-bytes -> KitTargetSpec detection (detect_elf/coff/pe/macho/wasm)
lived in src/api/object_detect.c behind a format-identity #if cascade. Promote it
to an ObjFormatImpl.detect_target hook implemented in src/obj/registry.c, next to
the arch reverse-maps it consumes, so a format compiled out of the build drops its
detector with it and kit_detect_target becomes a pure registry dispatch with no
format-identity #if in the API layer.

Diffstat:
Msrc/api/object_detect.c | 271++++---------------------------------------------------------------------------
Msrc/obj/format.h | 12++++++++++++
Msrc/obj/registry.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 255 insertions(+), 259 deletions(-)

diff --git a/src/api/object_detect.c b/src/api/object_detect.c @@ -1,16 +1,13 @@ /* Binary format and target detection from object header bytes. */ -#include <kit/cg.h> -#include <kit/config.h> #include <kit/object.h> #include "core/core.h" -#include "obj/elf/elf.h" #include "obj/format.h" /* COFF Machine numbers recognized as the COFF *binary* format by - * kit_detect_fmt. This is format membership only — the arch mapping (in - * detect_coff) routes through the registry's coff_machine reverse map. + * kit_detect_fmt. This is format membership only — the arch mapping (in the + * COFF detector) routes through the registry's coff_machine reverse map. * * The set here is intentionally broader than the registry's coff_machine * ops (which models only the link/codegen-supported AMD64 / ARM64): a COFF @@ -83,263 +80,19 @@ KitBinFmt kit_detect_fmt(const uint8_t* data, size_t len) { return KIT_BIN_UNKNOWN; } -static void detect_target_defaults(KitTargetSpec* t) { - t->big_endian = 0; - t->pic = KIT_PIC_NONE; - t->code_model = KIT_CM_DEFAULT; - t->float_abi = KIT_FLOAT_ABI_DEFAULT; -} - -static void detect_set_ptr(KitTargetSpec* t, KitArchKind arch) { - /* kit_arch_ptr_size is the single source of truth for an arch's byte - * pointer width (cg.h); ptr_align tracks ptr_size for every arch this - * detector resolves. wasm32 reports 4 here; the wasm path in - * kit_detect_target sets its own spec and does not call detect_set_ptr, - * so wasm64's 8-byte width is unaffected. */ - uint8_t w = kit_arch_ptr_size(arch); - t->arch = arch; - t->ptr_size = w; - t->ptr_align = w; -} - -static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) { - u8 ei_class, ei_data, ei_osabi; - u16 e_machine; - if (len < 20) return KIT_MALFORMED; - ei_class = d[4]; - ei_data = d[5]; - ei_osabi = d[7]; - if (ei_data == 1) { - e_machine = (u16)d[18] | ((u16)d[19] << 8); - } else if (ei_data == 2) { - e_machine = (u16)d[19] | ((u16)d[18] << 8); - } else { - return KIT_MALFORMED; - } - - detect_target_defaults(out); - out->big_endian = (ei_data == 2); - out->obj = KIT_OBJ_ELF; - - /* Resolve the arch through the ELF format's machine reverse map - * (obj_elf_machine_class also splits EM_RISCV into RV32/RV64 by - * EI_CLASS). The registry models only the link/codegen arches, so the - * legacy 32-bit ABI-classifiable machines it does not carry (EM_386, - * EM_ARM) are mapped here explicitly to preserve detection. */ - { - const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); - if (ops) { - detect_set_ptr(out, ops->arch); - } else if (e_machine == 0x03) { /* EM_386 */ - detect_set_ptr(out, KIT_ARCH_X86_32); - } else if (e_machine == 0x28) { /* EM_ARM */ - detect_set_ptr(out, KIT_ARCH_ARM_32); - } else { - return KIT_UNSUPPORTED; - } - } - /* EI_CLASS must agree with the arch's pointer width: 32-bit arches are - * ELFCLASS32, 64-bit arches ELFCLASS64. EM_RISCV is already disambiguated - * by class above; this also rejects a class/machine mismatch such as a - * 64-bit arch object whose EI_CLASS byte claims ELFCLASS32. */ - if (ei_class != ((out->ptr_size == 4) ? 1u : 2u)) return KIT_MALFORMED; - if (ei_osabi == 0 || ei_osabi == 3) - out->os = KIT_OS_LINUX; - else if (ei_osabi == 9) - out->os = KIT_OS_FREEBSD; - else - out->os = KIT_OS_FREESTANDING; - - /* Recover the float ABI from e_flags via the per-arch decoder so a detected - * target selects the matching runtime variant (rv32 ilp32 soft vs ilp32f - * single share an arch + pointer width and differ only here). e_flags is a - * 4-byte LE field after the three native-width addr fields: offset 36 on - * ELFCLASS32, 48 on ELFCLASS64. Only RISC-V supplies a decoder - * (float_abi_from_e_flags non-NULL); other arches leave the default. The - * decoder is only consulted when the header carries the full e_flags word, - * leaving DEFAULT (the wildcard) when it is truncated. */ - { - const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); - if (ops && ops->float_abi_from_e_flags) { - size_t flags_off = (ei_class == 1) ? 36u : 48u; - if (len >= flags_off + 4u) { - u32 e_flags; - if (ei_data == 1) - e_flags = (u32)d[flags_off] | ((u32)d[flags_off + 1] << 8) | - ((u32)d[flags_off + 2] << 16) | - ((u32)d[flags_off + 3] << 24); - else - e_flags = (u32)d[flags_off + 3] | ((u32)d[flags_off + 2] << 8) | - ((u32)d[flags_off + 1] << 16) | ((u32)d[flags_off] << 24); - out->float_abi = (u8)ops->float_abi_from_e_flags(e_flags); - } - } - } - return KIT_OK; -} - -/* Resolve a COFF Machine number to a KitArchKind through the registry's - * coff_machine reverse map (which aliases ARM64EC -> ARM64). The registry - * models only the link/codegen arches (AMD64 / ARM64); the legacy - * ABI-classifiable machines it does not carry are mapped explicitly to - * preserve detection. Returns 1 and writes *out on success; 0 if the - * machine is unsupported. Shared by detect_coff (.obj) and detect_pe - * (linked image). */ -static int coff_machine_to_arch(u16 machine, KitArchKind* out) { - const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_COFF); - const ObjCoffArchOps* ops = - (fmt && fmt->coff_machine) ? fmt->coff_machine(machine) : NULL; - if (ops) { - *out = ops->arch; - } else if (machine == 0x014Cu) { /* IMAGE_FILE_MACHINE_I386 */ - *out = KIT_ARCH_X86_32; - } else if (machine == 0x01C4u) { /* IMAGE_FILE_MACHINE_ARMNT */ - *out = KIT_ARCH_ARM_32; - } else if (machine == 0x5032u) { /* IMAGE_FILE_MACHINE_RISCV32 */ - *out = KIT_ARCH_RV32; - } else if (machine == 0x5064u) { /* IMAGE_FILE_MACHINE_RISCV64 */ - *out = KIT_ARCH_RV64; - } else { - return 0; - } - return 1; -} - -static KitStatus detect_coff(const u8* d, size_t len, KitTargetSpec* out) { - u16 machine; - KitArchKind arch; - if (len < 2) return KIT_MALFORMED; - machine = (u16)d[0] | ((u16)d[1] << 8); - if (!coff_machine_to_arch(machine, &arch)) return KIT_UNSUPPORTED; - detect_target_defaults(out); - out->obj = KIT_OBJ_COFF; - out->os = KIT_OS_WINDOWS; - detect_set_ptr(out, arch); - return KIT_OK; -} - -/* PE image (DOS 'MZ' stub + "PE\0\0" signature). Unlike a bare .obj, the - * COFF Machine word lives in the file header at e_lfanew+4, not at offset 0 - * (the DOS stub), so this can't reuse detect_coff's offset-0 read. Routes a - * well-formed image to KIT_OBJ_COFF / KIT_OS_WINDOWS; read_coff then - * dispatches the 'MZ' magic to read_coff_image. A 'MZ' prefix with no valid - * PE signature (a DOS-only stub) is rejected as malformed. */ -static KitStatus detect_pe(const u8* d, size_t len, KitTargetSpec* out) { - u32 e_lfanew, pe_sig; - u16 machine; - KitArchKind arch; - if (len < 64) return KIT_MALFORMED; /* DOS header */ - if (!(d[0] == 'M' && d[1] == 'Z')) return KIT_MALFORMED; - e_lfanew = - (u32)d[60] | ((u32)d[61] << 8) | ((u32)d[62] << 16) | ((u32)d[63] << 24); - /* Need the 4-byte PE signature + the 20-byte IMAGE_FILE_HEADER. */ - if ((u64)e_lfanew + 4u + 20u > (u64)len) return KIT_MALFORMED; - pe_sig = (u32)d[e_lfanew] | ((u32)d[e_lfanew + 1] << 8) | - ((u32)d[e_lfanew + 2] << 16) | ((u32)d[e_lfanew + 3] << 24); - if (pe_sig != 0x00004550u) return KIT_MALFORMED; /* "PE\0\0" */ - machine = (u16)d[e_lfanew + 4] | ((u16)d[e_lfanew + 5] << 8); - if (!coff_machine_to_arch(machine, &arch)) return KIT_UNSUPPORTED; - detect_target_defaults(out); - out->obj = KIT_OBJ_COFF; - out->os = KIT_OS_WINDOWS; - detect_set_ptr(out, arch); - return KIT_OK; -} - -static KitStatus detect_macho(const u8* d, size_t len, KitTargetSpec* out) { - u32 magic, cputype; - int swap, is64; - if (len < 8) return KIT_MALFORMED; - magic = (u32)d[0] | ((u32)d[1] << 8) | ((u32)d[2] << 16) | ((u32)d[3] << 24); - switch (magic) { - case 0xFEEDFACEu: - swap = 0; - is64 = 0; - break; - case 0xFEEDFACFu: - swap = 0; - is64 = 1; - break; - case 0xCEFAEDFEu: - swap = 1; - is64 = 0; - break; - case 0xCFFAEDFEu: - swap = 1; - is64 = 1; - break; - default: - return KIT_MALFORMED; - } - if (!swap) { - cputype = - (u32)d[4] | ((u32)d[5] << 8) | ((u32)d[6] << 16) | ((u32)d[7] << 24); - } else { - cputype = - (u32)d[7] | ((u32)d[6] << 8) | ((u32)d[5] << 16) | ((u32)d[4] << 24); - } - detect_target_defaults(out); - out->obj = KIT_OBJ_MACHO; - out->os = KIT_OS_MACOS; - - /* Resolve the arch through the Mach-O format's cputype reverse map. The - * registry models only the link/codegen arches (ARM64 / X86_64); the - * legacy 32-bit ABI-classifiable cputypes it does not carry (CPU_TYPE_X86, - * CPU_TYPE_ARM) are mapped explicitly to preserve detection. */ - { - const ObjFormatImpl* fmt = obj_format_lookup(KIT_OBJ_MACHO); - const ObjMachoArchOps* ops = - (fmt && fmt->macho_cputype) ? fmt->macho_cputype(cputype) : NULL; - if (ops) { - detect_set_ptr(out, ops->arch); - } else if (cputype == 0x00000007u) { /* CPU_TYPE_X86 */ - detect_set_ptr(out, KIT_ARCH_X86_32); - } else if (cputype == 0x0000000Cu) { /* CPU_TYPE_ARM */ - detect_set_ptr(out, KIT_ARCH_ARM_32); - } else { - return KIT_UNSUPPORTED; - } - } - (void)is64; - return KIT_OK; -} - KitStatus kit_detect_target(const uint8_t* data, size_t len, KitTargetSpec* out) { KitBinFmt bin; + const ObjFormatImpl* fmt; if (!data || !out) return KIT_INVALID; bin = kit_detect_fmt(data, len); - switch (bin) { -#if KIT_OBJ_ELF_ENABLED - case KIT_BIN_ELF: - return detect_elf(data, len, out); -#endif -#if KIT_OBJ_COFF_ENABLED - case KIT_BIN_PE: - return detect_pe(data, len, out); - case KIT_BIN_COFF: - return detect_coff(data, len, out); -#endif -#if KIT_OBJ_MACHO_ENABLED - case KIT_BIN_MACHO: - return detect_macho(data, len, out); -#endif -#if KIT_OBJ_WASM_ENABLED - case KIT_BIN_WASM: { - KitTargetSpec t; - t.big_endian = 0; - t.pic = KIT_PIC_NONE; - t.code_model = KIT_CM_DEFAULT; - t.arch = KIT_ARCH_WASM; - t.ptr_size = 4; - t.ptr_align = 4; - t.obj = KIT_OBJ_WASM; - t.os = KIT_OS_WASI; - *out = t; - return KIT_OK; - } -#endif - default: - return KIT_UNSUPPORTED; - } + /* A PE image classifies as KIT_BIN_PE but is read (and detected) by the + * COFF format; every other binary maps straight through. The per-format + * detector lives in the obj format vtable and is compiled out with its + * format, so a disabled format resolves to NULL here -> KIT_UNSUPPORTED + * (matching the AR / UNKNOWN cases). */ + fmt = (bin == KIT_BIN_PE) ? obj_format_lookup(KIT_OBJ_COFF) + : obj_format_lookup_bin(bin); + if (!fmt || !fmt->detect_target) return KIT_UNSUPPORTED; + return fmt->detect_target(data, len, out); } diff --git a/src/obj/format.h b/src/obj/format.h @@ -33,6 +33,15 @@ typedef void (*ObjFormatMachoStubFn)(u8* dst, u64 stub_vaddr, typedef void (*ObjFormatCoffStubFn)(u8* dst, u64 stub_vaddr, u64 iat_slot_vaddr); +/* Resolve a build target (arch/OS/obj/endian/float-ABI) from this format's + * on-disk header bytes. Invoked by kit_detect_target once kit_detect_fmt has + * classified the leading bytes to this format. The detector lives in the + * format vtable so a format compiled out of the build drops its detection + * logic with it — no format-identity #if in the API layer. The COFF detector + * additionally handles PE images (the dispatcher routes KIT_BIN_PE to COFF). */ +typedef KitStatus (*ObjFormatDetectTargetFn)(const u8* data, size_t len, + KitTargetSpec* out); + /* Synthetic-input hook: invoked before symbol resolution to inject a * synthetic input object (e.g. the COFF __CTOR_LIST__/__DTOR_LIST__ * boundary blob). The hook receives the Linker so it can append a @@ -224,6 +233,9 @@ typedef struct ObjFormatImpl { const ObjCoffArchOps* (*coff_arch)(KitArchKind); const ObjCoffArchOps* (*coff_machine)(u16 machine); + /* Header-bytes -> KitTargetSpec detector (see ObjFormatDetectTargetFn). */ + ObjFormatDetectTargetFn detect_target; + /* Optional format-specific linker ingestion policy. */ int (*classify_obj_input)(Compiler*, ObjBuilder*, Sym* soname_out); Sym (*archive_hint)(Compiler*, const char* archive_name); diff --git a/src/obj/registry.c b/src/obj/registry.c @@ -1,3 +1,4 @@ +#include <kit/cg.h> #include <kit/config.h> #include <string.h> @@ -309,7 +310,233 @@ static const ObjCoffArchOps* obj_coff_machine(u16 machine) { } #endif +/* ---- Target detection from header bytes (ObjFormatImpl.detect_target) ---- + * + * Each detector resolves the build target an object encodes. They live here, + * next to the arch reverse-maps they consume, so a format compiled out of the + * build drops its detector with it; kit_detect_target (src/api) is then a pure + * registry dispatch with no format-identity #if. */ +#if KIT_OBJ_ELF_ENABLED || KIT_OBJ_MACHO_ENABLED || KIT_OBJ_COFF_ENABLED +static void detect_target_defaults(KitTargetSpec* t) { + t->big_endian = 0; + t->pic = KIT_PIC_NONE; + t->code_model = KIT_CM_DEFAULT; + t->float_abi = KIT_FLOAT_ABI_DEFAULT; +} + +/* kit_arch_ptr_size (cg.h) is the single source of truth for an arch's byte + * pointer width; ptr_align tracks ptr_size for every arch these detectors + * resolve. The wasm detector sets its own spec and does not call this. */ +static void detect_set_ptr(KitTargetSpec* t, KitArchKind arch) { + uint8_t w = kit_arch_ptr_size(arch); + t->arch = arch; + t->ptr_size = w; + t->ptr_align = w; +} +#endif + +#if KIT_OBJ_ELF_ENABLED +static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) { + u8 ei_class, ei_data, ei_osabi; + u16 e_machine; + if (len < 20) return KIT_MALFORMED; + ei_class = d[4]; + ei_data = d[5]; + ei_osabi = d[7]; + if (ei_data == 1) { + e_machine = (u16)d[18] | ((u16)d[19] << 8); + } else if (ei_data == 2) { + e_machine = (u16)d[19] | ((u16)d[18] << 8); + } else { + return KIT_MALFORMED; + } + + detect_target_defaults(out); + out->big_endian = (ei_data == 2); + out->obj = KIT_OBJ_ELF; + + /* Resolve the arch through the ELF format's machine reverse map + * (obj_elf_machine_class also splits EM_RISCV into RV32/RV64 by + * EI_CLASS). The registry models only the link/codegen arches, so the + * legacy 32-bit ABI-classifiable machines it does not carry (EM_386, + * EM_ARM) are mapped here explicitly to preserve detection. */ + { + const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); + if (ops) { + detect_set_ptr(out, ops->arch); + } else if (e_machine == 0x03) { /* EM_386 */ + detect_set_ptr(out, KIT_ARCH_X86_32); + } else if (e_machine == 0x28) { /* EM_ARM */ + detect_set_ptr(out, KIT_ARCH_ARM_32); + } else { + return KIT_UNSUPPORTED; + } + } + /* EI_CLASS must agree with the arch's pointer width: 32-bit arches are + * ELFCLASS32, 64-bit arches ELFCLASS64. EM_RISCV is already disambiguated + * by class above; this also rejects a class/machine mismatch such as a + * 64-bit arch object whose EI_CLASS byte claims ELFCLASS32. */ + if (ei_class != ((out->ptr_size == 4) ? 1u : 2u)) return KIT_MALFORMED; + if (ei_osabi == 0 || ei_osabi == 3) + out->os = KIT_OS_LINUX; + else if (ei_osabi == 9) + out->os = KIT_OS_FREEBSD; + else + out->os = KIT_OS_FREESTANDING; + + /* Recover the float ABI from e_flags via the per-arch decoder so a detected + * target selects the matching runtime variant (rv32 ilp32 soft vs ilp32f + * single share an arch + pointer width and differ only here). e_flags is a + * 4-byte LE field after the three native-width addr fields: offset 36 on + * ELFCLASS32, 48 on ELFCLASS64. Only RISC-V supplies a decoder + * (float_abi_from_e_flags non-NULL); other arches leave the default. The + * decoder is only consulted when the header carries the full e_flags word, + * leaving DEFAULT (the wildcard) when it is truncated. */ + { + const ObjElfArchOps* ops = obj_elf_machine_class(e_machine, ei_class); + if (ops && ops->float_abi_from_e_flags) { + size_t flags_off = (ei_class == 1) ? 36u : 48u; + if (len >= flags_off + 4u) { + u32 e_flags; + if (ei_data == 1) + e_flags = (u32)d[flags_off] | ((u32)d[flags_off + 1] << 8) | + ((u32)d[flags_off + 2] << 16) | + ((u32)d[flags_off + 3] << 24); + else + e_flags = (u32)d[flags_off + 3] | ((u32)d[flags_off + 2] << 8) | + ((u32)d[flags_off + 1] << 16) | ((u32)d[flags_off] << 24); + out->float_abi = (u8)ops->float_abi_from_e_flags(e_flags); + } + } + } + return KIT_OK; +} +#endif + +#if KIT_OBJ_COFF_ENABLED +/* Resolve a COFF Machine number to a KitArchKind through the registry's + * coff_machine reverse map (which aliases ARM64EC -> ARM64). The registry + * models only the link/codegen arches (AMD64 / ARM64); the legacy + * ABI-classifiable machines it does not carry are mapped explicitly to + * preserve detection. Returns 1 and writes *out on success; 0 if the + * machine is unsupported. Shared by the bare-.obj and PE-image paths. */ +static int coff_machine_to_arch(u16 machine, KitArchKind* out) { + const ObjCoffArchOps* ops = obj_coff_machine(machine); + if (ops) { + *out = ops->arch; + } else if (machine == 0x014Cu) { /* IMAGE_FILE_MACHINE_I386 */ + *out = KIT_ARCH_X86_32; + } else if (machine == 0x01C4u) { /* IMAGE_FILE_MACHINE_ARMNT */ + *out = KIT_ARCH_ARM_32; + } else if (machine == 0x5032u) { /* IMAGE_FILE_MACHINE_RISCV32 */ + *out = KIT_ARCH_RV32; + } else if (machine == 0x5064u) { /* IMAGE_FILE_MACHINE_RISCV64 */ + *out = KIT_ARCH_RV64; + } else { + return 0; + } + return 1; +} + +/* COFF covers both a bare .obj (COFF Machine word at offset 0) and a linked + * PE image (DOS 'MZ' stub + "PE\0\0" signature, with the Machine word at + * e_lfanew+4). kit_detect_fmt classifies the PE image as KIT_BIN_PE and the + * dispatcher routes it here, so this one detector handles both by branching + * on the 'MZ' magic. */ +static KitStatus detect_coff(const u8* d, size_t len, KitTargetSpec* out) { + u16 machine; + KitArchKind arch; + if (len >= 2 && d[0] == 'M' && d[1] == 'Z') { + u32 e_lfanew, pe_sig; + if (len < 64) return KIT_MALFORMED; /* DOS header */ + e_lfanew = (u32)d[60] | ((u32)d[61] << 8) | ((u32)d[62] << 16) | + ((u32)d[63] << 24); + /* Need the 4-byte PE signature + the 20-byte IMAGE_FILE_HEADER. */ + if ((u64)e_lfanew + 4u + 20u > (u64)len) return KIT_MALFORMED; + pe_sig = (u32)d[e_lfanew] | ((u32)d[e_lfanew + 1] << 8) | + ((u32)d[e_lfanew + 2] << 16) | ((u32)d[e_lfanew + 3] << 24); + if (pe_sig != 0x00004550u) return KIT_MALFORMED; /* "PE\0\0" */ + machine = (u16)d[e_lfanew + 4] | ((u16)d[e_lfanew + 5] << 8); + } else { + if (len < 2) return KIT_MALFORMED; + machine = (u16)d[0] | ((u16)d[1] << 8); + } + if (!coff_machine_to_arch(machine, &arch)) return KIT_UNSUPPORTED; + detect_target_defaults(out); + out->obj = KIT_OBJ_COFF; + out->os = KIT_OS_WINDOWS; + detect_set_ptr(out, arch); + return KIT_OK; +} +#endif + +#if KIT_OBJ_MACHO_ENABLED +static KitStatus detect_macho(const u8* d, size_t len, KitTargetSpec* out) { + u32 magic, cputype; + int swap; + if (len < 8) return KIT_MALFORMED; + magic = (u32)d[0] | ((u32)d[1] << 8) | ((u32)d[2] << 16) | ((u32)d[3] << 24); + switch (magic) { + case 0xFEEDFACEu: + case 0xFEEDFACFu: + swap = 0; + break; + case 0xCEFAEDFEu: + case 0xCFFAEDFEu: + swap = 1; + break; + default: + return KIT_MALFORMED; + } + if (!swap) { + cputype = + (u32)d[4] | ((u32)d[5] << 8) | ((u32)d[6] << 16) | ((u32)d[7] << 24); + } else { + cputype = + (u32)d[7] | ((u32)d[6] << 8) | ((u32)d[5] << 16) | ((u32)d[4] << 24); + } + detect_target_defaults(out); + out->obj = KIT_OBJ_MACHO; + out->os = KIT_OS_MACOS; + + /* Resolve the arch through the Mach-O format's cputype reverse map. The + * registry models only the link/codegen arches (ARM64 / X86_64); the + * legacy 32-bit ABI-classifiable cputypes it does not carry (CPU_TYPE_X86, + * CPU_TYPE_ARM) are mapped explicitly to preserve detection. */ + { + const ObjMachoArchOps* ops = obj_macho_cputype(cputype); + if (ops) { + detect_set_ptr(out, ops->arch); + } else if (cputype == 0x00000007u) { /* CPU_TYPE_X86 */ + detect_set_ptr(out, KIT_ARCH_X86_32); + } else if (cputype == 0x0000000Cu) { /* CPU_TYPE_ARM */ + detect_set_ptr(out, KIT_ARCH_ARM_32); + } else { + return KIT_UNSUPPORTED; + } + } + return KIT_OK; +} +#endif + #if KIT_OBJ_WASM_ENABLED +static KitStatus detect_wasm(const u8* d, size_t len, KitTargetSpec* out) { + KitTargetSpec t; + (void)d; + (void)len; + t.big_endian = 0; + t.pic = KIT_PIC_NONE; + t.code_model = KIT_CM_DEFAULT; + t.float_abi = KIT_FLOAT_ABI_DEFAULT; + t.arch = KIT_ARCH_WASM; + t.ptr_size = 4; + t.ptr_align = 4; + t.obj = KIT_OBJ_WASM; + t.os = KIT_OS_WASI; + *out = t; + return KIT_OK; +} + static const ObjFormatImpl obj_format_impl_wasm = { .kind = KIT_OBJ_WASM, .name = "wasm", @@ -336,6 +563,7 @@ static const ObjFormatImpl obj_format_impl_wasm = { * out like `.data`); tls_addr_of lowers to a plain symbol address. */ .secname_tdata = ".tdata", .secname_tbss = ".tbss", + .detect_target = detect_wasm, }; #endif @@ -367,6 +595,7 @@ static const ObjFormatImpl obj_format_impl_elf = { .secname_tbss = ".tbss", .elf_arch = obj_elf_arch, .elf_machine = obj_elf_machine, + .detect_target = detect_elf, }; #endif @@ -399,6 +628,7 @@ static const ObjFormatImpl obj_format_impl_macho = { .secname_tbss = "__DATA,__thread_bss", .macho_arch = obj_macho_arch, .macho_cputype = obj_macho_cputype, + .detect_target = detect_macho, }; #endif @@ -439,6 +669,7 @@ static const ObjFormatImpl obj_format_impl_coff = { .synth_inputs = OBJ_COFF_SYNTH_INPUTS, .coff_arch = obj_coff_arch, .coff_machine = obj_coff_machine, + .detect_target = detect_coff, .classify_obj_input = coff_classify_obj_input, .archive_hint = coff_archive_hint, .archive_member = coff_archive_member,