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:
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,