kit

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

target.c (28511B)


      1 /* Public target-triple parsing + per-target defaults.
      2  *
      3  * The triple<->KitTargetSpec string layer and the spec-derived defaults
      4  * (PIC/PIE, default output names/extensions, hosted-profile gating). Pure
      5  * computation: no host I/O, no diagnostics — callers turn a false/0 return into
      6  * whatever message they like. Hoisted out of the CLI driver so the spelling
      7  * tables here are the single authority every embedder shares.
      8  */
      9 
     10 #include <kit/target.h>
     11 
     12 #include <kit/config.h>
     13 #include <kit/os.h>
     14 #include <stddef.h>
     15 #include <stdint.h>
     16 #include <stdio.h>
     17 #include <string.h>
     18 
     19 static bool triple_tok_eq(const char* s, size_t n, const char* lit) {
     20   size_t l = strlen(lit);
     21   return n == l && memcmp(s, lit, n) == 0;
     22 }
     23 
     24 /* Prefix match for OS tokens that carry a trailing version, e.g. clang emits
     25  * "freebsd15.0" / "freebsd14" rather than a bare "freebsd". */
     26 static bool triple_tok_prefix(const char* s, size_t n, const char* lit) {
     27   size_t l = strlen(lit);
     28   return n >= l && memcmp(s, lit, l) == 0;
     29 }
     30 
     31 static bool triple_tok_is_ios(const char* s, size_t n) {
     32   if (triple_tok_eq(s, n, "iphoneos")) return true;
     33   if (!triple_tok_prefix(s, n, "ios")) return false;
     34   if (n == 3) return true;
     35   return (s[3] >= '0' && s[3] <= '9') || s[3] == '.';
     36 }
     37 
     38 static bool triple_tok_is_ios_sim(const char* s, size_t n) {
     39   return triple_tok_eq(s, n, "simulator") ||
     40          triple_tok_eq(s, n, "iossim") ||
     41          triple_tok_eq(s, n, "ios-simulator") ||
     42          triple_tok_eq(s, n, "iossimulator") ||
     43          triple_tok_eq(s, n, "iphonesimulator");
     44 }
     45 
     46 static bool triple_tok_is_android(const char* s, size_t n,
     47                                   uint8_t* api_out) {
     48   size_t l = strlen("android");
     49   unsigned v = 0;
     50   size_t i;
     51   if (n < l || memcmp(s, "android", l) != 0) return false;
     52   for (i = l; i < n && s[i] >= '0' && s[i] <= '9'; ++i)
     53     v = v * 10u + (unsigned)(s[i] - '0');
     54   if (i != n) return false;
     55   if (api_out) *api_out = (uint8_t)(v > 255u ? 0u : v);
     56   return true;
     57 }
     58 
     59 static bool riscv_arch_tok(const char* s, size_t n, const char* base) {
     60   size_t l = strlen(base);
     61   size_t i;
     62   if (!base) return false;
     63   if (n < l || memcmp(s, base, l) != 0) return false;
     64   for (i = l; i < n; ++i) {
     65     if ((s[i] < 'a' || s[i] > 'z') && (s[i] < '0' || s[i] > '9'))
     66       return false;
     67   }
     68   return true;
     69 }
     70 
     71 /* Single arch descriptor table: the one place that enumerates the arch set for
     72  * triple parsing, triple rendering, and diagnostic naming.
     73  *
     74  *   - `aliases`   : every exact arch-component spelling arch_from_tok accepts.
     75  *                   NULL-terminated; pad unused slots with NULL.
     76  *   - `riscv_base`: optional ISA-string prefix also accepted (e.g. "riscv64gc"
     77  *                   on top of the riscv64 base), NULL when there is none.
     78  *   - `triple_name`: the canonical spelling kit_target_to_triple emits. NULL
     79  *                    means the renderer derives it from pointer size (wasm).
     80  *   - `diag_name` : the human-facing name arch_kind_name returns.
     81  *   - `ptr_size`  : natural pointer size; 0 means "derive from the spelling"
     82  *                   (wasm32 vs wasm64), handled per-alias below.
     83  */
     84 #define ARCH_ALIAS_MAX 4
     85 struct ArchSpec {
     86   KitArchKind kind;
     87   const char* triple_name;
     88   const char* diag_name;
     89   uint8_t ptr_size;
     90   const char* riscv_base;
     91   const char* aliases[ARCH_ALIAS_MAX];
     92 };
     93 
     94 static const struct ArchSpec arch_specs[] = {
     95     {KIT_ARCH_X86_64, "x86_64", "x86_64", 8, NULL,
     96      {"x86_64", "amd64", "x64", NULL}},
     97     {KIT_ARCH_X86_32, "i386", "x86_32", 4, NULL,
     98      {"i386", "i486", "i586", "i686"}},
     99     {KIT_ARCH_ARM_64, "aarch64", "aarch64", 8, NULL,
    100      {"aarch64", "arm64", "aa64", NULL}},
    101     {KIT_ARCH_ARM_32, "arm", "arm32", 4, NULL, {"arm", "armv7", NULL, NULL}},
    102     {KIT_ARCH_RV64, "riscv64", "riscv64", 8, "riscv64",
    103      {"riscv64", "rv64", NULL, NULL}},
    104     {KIT_ARCH_RV32, "riscv32", "riscv32", 4, "riscv32",
    105      {"riscv32", "rv32", NULL, NULL}},
    106     /* wasm carries two spellings differing only by pointer size; triple_name
    107      * is NULL so the renderer picks wasm64/wasm32 from ptr_size. */
    108     {KIT_ARCH_WASM, NULL, "wasm", 4, NULL, {"wasm32", NULL, NULL, NULL}},
    109     {KIT_ARCH_WASM, NULL, "wasm", 8, NULL, {"wasm64", NULL, NULL, NULL}},
    110 };
    111 
    112 static const size_t arch_specs_count =
    113     sizeof arch_specs / sizeof arch_specs[0];
    114 
    115 /* Recognize an architecture token, the single authority for the arch-name
    116  * spellings kit accepts. Writes arch + natural pointer size on a hit. Returns
    117  * true on success, false for an unrecognized token. Shared by the triple parser
    118  * and the public kit_arch_from_name. */
    119 static bool arch_from_tok(const char* s, size_t n, KitArchKind* arch_out,
    120                           uint8_t* ptr_size_out) {
    121   size_t si;
    122   for (si = 0; si < arch_specs_count; ++si) {
    123     const struct ArchSpec* a = &arch_specs[si];
    124     size_t ai;
    125     bool hit = false;
    126     for (ai = 0; ai < ARCH_ALIAS_MAX && a->aliases[ai]; ++ai) {
    127       if (triple_tok_eq(s, n, a->aliases[ai])) {
    128         hit = true;
    129         break;
    130       }
    131     }
    132     if (!hit && a->riscv_base && riscv_arch_tok(s, n, a->riscv_base))
    133       hit = true;
    134     if (hit) {
    135       if (arch_out) *arch_out = a->kind;
    136       if (ptr_size_out) *ptr_size_out = a->ptr_size;
    137       return true;
    138     }
    139   }
    140   return false;
    141 }
    142 
    143 const char* kit_target_arch_diag_name(KitArchKind arch) {
    144   size_t si;
    145   for (si = 0; si < arch_specs_count; ++si) {
    146     if (arch_specs[si].kind == arch) return arch_specs[si].diag_name;
    147   }
    148   return "unknown";
    149 }
    150 
    151 /* Canonical arch component for the triple renderer: the table's triple_name,
    152  * or the pointer-size-derived spelling when triple_name is NULL (wasm).
    153  * For the ptr-size-derived case (wasm) the historical rule is "8 -> wasm64,
    154  * everything else -> wasm32", so we select the alias of the row whose ptr_size
    155  * matches, falling back to the smallest-ptr-size row. */
    156 static const char* arch_triple_name(KitArchKind arch, uint8_t ptr_size) {
    157   size_t si;
    158   const char* fallback = "unknown";
    159   for (si = 0; si < arch_specs_count; ++si) {
    160     const struct ArchSpec* a = &arch_specs[si];
    161     if (a->kind != arch) continue;
    162     if (a->triple_name) return a->triple_name;
    163     /* triple_name == NULL: pick the row whose pointer size matches, but keep
    164      * the 4-byte (wasm32) spelling as the default for any other size. */
    165     if (a->ptr_size == ptr_size) return a->aliases[0];
    166     if (a->ptr_size == 4) fallback = a->aliases[0];
    167   }
    168   return fallback;
    169 }
    170 
    171 bool kit_arch_from_name(const char* name, KitArchKind* arch_out,
    172                         uint8_t* ptr_size_out) {
    173   if (!name) return false;
    174   return arch_from_tok(name, strlen(name), arch_out, ptr_size_out);
    175 }
    176 
    177 /* Single OS descriptor table: the one place that ties an OS to its object
    178  * format and its canonical triple-render spelling.
    179  *
    180  *   - `triple_render`: the OS component kit_target_to_triple emits. Android is
    181  *                      special-cased in the renderer (it carries an API-level
    182  *                      suffix), so its triple_render is unused/NULL here.
    183  *   - `obj`          : the object format the OS implies. KIT_OBJ_WASM is used as
    184  *                      a sentinel on KIT_OS_FREESTANDING meaning "derive from
    185  *                      arch" (wasm -> WASM, otherwise ELF); see os_objfmt.
    186  *
    187  * The token-matching for OSes with version suffixes (android API level, the iOS
    188  * version, the ios-simulator look-ahead) stays in the triple_tok_* helpers,
    189  * which set the os; the obj and the render spelling come from this table. */
    190 struct OsSpec {
    191   KitOSKind os;
    192   const char* triple_render;
    193   KitObjFmt obj;
    194 };
    195 
    196 static const struct OsSpec os_specs[] = {
    197     {KIT_OS_LINUX, "linux", KIT_OBJ_ELF},
    198     {KIT_OS_MACOS, "apple-darwin", KIT_OBJ_MACHO},
    199     {KIT_OS_IOS, "apple-ios", KIT_OBJ_MACHO},
    200     {KIT_OS_IOS_SIMULATOR, "apple-ios-simulator", KIT_OBJ_MACHO},
    201     {KIT_OS_WINDOWS, "windows", KIT_OBJ_COFF},
    202     {KIT_OS_FREEBSD, "freebsd", KIT_OBJ_ELF},
    203     {KIT_OS_ANDROID, NULL, KIT_OBJ_ELF},
    204     {KIT_OS_WASI, "wasi", KIT_OBJ_WASM},
    205     /* FREESTANDING carries no fixed object format: WASM arch -> WASM, else ELF.
    206      * The WASM entry here is a sentinel resolved by os_objfmt(). The renderer
    207      * emits the historical "elf" spelling for this OS. */
    208     {KIT_OS_FREESTANDING, "elf", KIT_OBJ_WASM},
    209 };
    210 
    211 static const size_t os_specs_count = sizeof os_specs / sizeof os_specs[0];
    212 
    213 const char* kit_target_arch_name(KitArchKind arch) {
    214   switch (arch) {
    215     case KIT_ARCH_X86_32:
    216       return "x86_32";
    217     case KIT_ARCH_X86_64:
    218       return "x86_64";
    219     case KIT_ARCH_ARM_32:
    220       return "arm32";
    221     case KIT_ARCH_ARM_64:
    222       return "aarch64";
    223     case KIT_ARCH_RV32:
    224       return "riscv32";
    225     case KIT_ARCH_RV64:
    226       return "riscv64";
    227     case KIT_ARCH_WASM:
    228       return "wasm32";
    229   }
    230   return "unknown";
    231 }
    232 
    233 const char* kit_target_os_name(KitOSKind os) {
    234   switch (os) {
    235     case KIT_OS_FREESTANDING:
    236       return "freestanding";
    237     case KIT_OS_LINUX:
    238       return "linux";
    239     case KIT_OS_MACOS:
    240       return "macos";
    241     case KIT_OS_IOS:
    242       return "ios";
    243     case KIT_OS_IOS_SIMULATOR:
    244       return "ios-simulator";
    245     case KIT_OS_WINDOWS:
    246       return "windows";
    247     case KIT_OS_FREEBSD:
    248       return "freebsd";
    249     case KIT_OS_ANDROID:
    250       return "android";
    251     case KIT_OS_WASI:
    252       return "wasi";
    253   }
    254   return "unknown";
    255 }
    256 
    257 const char* kit_target_obj_name(KitObjFmt obj) {
    258   switch (obj) {
    259     case KIT_OBJ_ELF:
    260       return "elf";
    261     case KIT_OBJ_COFF:
    262       return "coff";
    263     case KIT_OBJ_MACHO:
    264       return "macho";
    265     case KIT_OBJ_WASM:
    266       return "wasm";
    267   }
    268   return "unknown";
    269 }
    270 
    271 const char* kit_target_libc_name(uint8_t libc) {
    272   switch ((KitTargetLibcModel)libc) {
    273     case KIT_TARGET_LIBC_NONE:
    274       return "none";
    275     case KIT_TARGET_LIBC_GLIBC:
    276       return "glibc";
    277     case KIT_TARGET_LIBC_MUSL:
    278       return "musl";
    279     case KIT_TARGET_LIBC_FREEBSD:
    280       return "freebsd-libc";
    281     case KIT_TARGET_LIBC_UCRT:
    282       return "ucrt";
    283     case KIT_TARGET_LIBC_BIONIC:
    284       return "bionic";
    285     case KIT_TARGET_LIBC_DARWIN:
    286       return "darwin-libsystem";
    287     case KIT_TARGET_LIBC_WASI:
    288       return "wasi-libc";
    289   }
    290   return "unknown";
    291 }
    292 
    293 const char* kit_target_sysroot_name(uint8_t sysroot) {
    294   switch ((KitTargetSysrootModel)sysroot) {
    295     case KIT_TARGET_SYSROOT_NONE:
    296       return "none";
    297     case KIT_TARGET_SYSROOT_EXTERNAL:
    298       return "external";
    299     case KIT_TARGET_SYSROOT_NATIVE_SDK:
    300       return "native-sdk";
    301   }
    302   return "unknown";
    303 }
    304 
    305 const char* kit_target_provisioning_name(uint8_t provisioning) {
    306   switch ((KitTargetProvisioningModel)provisioning) {
    307     case KIT_TARGET_PROVISION_BUILTIN:
    308       return "built-in";
    309     case KIT_TARGET_PROVISION_SYSROOT:
    310       return "sysroot";
    311     case KIT_TARGET_PROVISION_NATIVE:
    312       return "native";
    313     case KIT_TARGET_PROVISION_VM:
    314       return "vm";
    315     case KIT_TARGET_PROVISION_QEMU:
    316       return "qemu";
    317   }
    318   return "unknown";
    319 }
    320 
    321 static const struct OsSpec* os_spec_for(KitOSKind os) {
    322   size_t i;
    323   for (i = 0; i < os_specs_count; ++i) {
    324     if (os_specs[i].os == os) return &os_specs[i];
    325   }
    326   return NULL;
    327 }
    328 
    329 /* Object format an OS implies for a given arch. Pure table lookup, except
    330  * FREESTANDING which derives from the arch (wasm -> WASM, otherwise ELF). */
    331 static KitObjFmt os_objfmt(KitOSKind os, KitArchKind arch) {
    332   const struct OsSpec* s = os_spec_for(os);
    333   if (os == KIT_OS_FREESTANDING)
    334     return (arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF;
    335   return s ? s->obj : KIT_OBJ_ELF;
    336 }
    337 
    338 KitPic kit_target_default_pic(KitObjFmt obj, KitOSKind os) {
    339   /* WASM has no PIC/PIE concept; freestanding targets have no dynamic
    340    * loader to apply load-time relocations. Everything else is hosted and
    341    * defaults to PIE. */
    342   if (obj == KIT_OBJ_WASM) return KIT_PIC_NONE;
    343   if (os == KIT_OS_FREESTANDING) return KIT_PIC_NONE;
    344   return KIT_PIC_PIE;
    345 }
    346 
    347 int kit_target_link_pie(KitTargetSpec target, int explicit_pie, int shared,
    348                         int relocatable) {
    349   if (explicit_pie) return 1;
    350   if (shared || relocatable) return 0;
    351   return target.pic == KIT_PIC_PIE;
    352 }
    353 
    354 const char* kit_target_default_exe_name(KitTargetSpec target) {
    355   /* PE/COFF executables conventionally carry a `.exe` suffix; ELF/Mach-O
    356    * default link output is the historical `a.out`. */
    357   return target.os == KIT_OS_WINDOWS ? "a.exe" : "a.out";
    358 }
    359 
    360 void kit_target_default_obj_ext(KitTargetSpec target, const char** ext_out,
    361                                 size_t* ext_len_out) {
    362   /* Windows targets default to a `.obj` suffix; everyone else `.o`. Drivers
    363    * accept both spellings as inputs, but tooling that scrapes default outputs
    364    * expects the canonical platform extension. */
    365   if (target.os == KIT_OS_WINDOWS) {
    366     if (ext_out) *ext_out = ".obj";
    367     if (ext_len_out) *ext_len_out = 4u;
    368   } else {
    369     if (ext_out) *ext_out = ".o";
    370     if (ext_len_out) *ext_len_out = 2u;
    371   }
    372 }
    373 
    374 int kit_target_needs_sysroot_libdir(KitTargetSpec target) {
    375   const KitOsImpl* os = kit_os_lookup(target.os);
    376   if (!os || !os->hosted || os->hosted->obj != target.obj) return 0;
    377   return os->hosted->needs_sysroot_libdir;
    378 }
    379 
    380 int kit_target_default_hosted_profile(KitTargetSpec target) {
    381   const KitOsImpl* os = kit_os_lookup(target.os);
    382   if (!os || !os->hosted || os->hosted->obj != target.obj) return 0;
    383   return os->hosted->default_profile;
    384 }
    385 
    386 bool kit_target_from_triple(const char* triple, KitTargetSpec* out) {
    387   const char* parts[4];
    388   size_t plen[4];
    389   int np = 0;
    390   const char* p;
    391   KitTargetSpec t;
    392   int os_set;
    393   int i;
    394 
    395   if (!triple || !out) return false;
    396   memset(&t, 0, sizeof(t));
    397 
    398   p = triple;
    399   while (np < 4) {
    400     const char* dash = strchr(p, '-');
    401     parts[np] = p;
    402     plen[np] = dash ? (size_t)(dash - p) : strlen(p);
    403     if (plen[np] == 0) return false;
    404     np++;
    405     if (!dash) break;
    406     p = dash + 1;
    407   }
    408 
    409   if (!arch_from_tok(parts[0], plen[0], &t.arch, &t.ptr_size)) return false;
    410 
    411   /* Match an OS token, set t.os (+ any version suffix), then derive t.obj from
    412    * the OS table via os_objfmt() rather than open-coding the format per branch.
    413    * The token matchers (android API level, iOS version, ios-simulator
    414    * look-ahead) stay as triple_tok_* helpers. */
    415   os_set = 0;
    416   for (i = 1; i < np; ++i) {
    417     {
    418       uint8_t api = 0;
    419       if (triple_tok_is_android(parts[i], plen[i], &api)) {
    420         t.os = KIT_OS_ANDROID;
    421         t.os_version_major = api;
    422         os_set = 1;
    423         break;
    424       }
    425     }
    426     if (triple_tok_eq(parts[i], plen[i], "linux")) {
    427       int android = 0;
    428       uint8_t api = 0;
    429       int j;
    430       for (j = i + 1; j < np; ++j) {
    431         if (triple_tok_is_android(parts[j], plen[j], &api)) {
    432           android = 1;
    433           break;
    434         }
    435       }
    436       t.os = android ? KIT_OS_ANDROID : KIT_OS_LINUX;
    437       t.os_version_major = android ? api : 0;
    438       os_set = 1;
    439       break;
    440     }
    441     if (triple_tok_eq(parts[i], plen[i], "darwin") ||
    442         triple_tok_eq(parts[i], plen[i], "macos")) {
    443       t.os = KIT_OS_MACOS;
    444       os_set = 1;
    445       break;
    446     }
    447     if (triple_tok_is_ios_sim(parts[i], plen[i])) {
    448       t.os = KIT_OS_IOS_SIMULATOR;
    449       os_set = 1;
    450       break;
    451     }
    452     if (triple_tok_is_ios(parts[i], plen[i])) {
    453       int sim = 0;
    454       int j;
    455       for (j = i + 1; j < np; ++j) {
    456         if (triple_tok_is_ios_sim(parts[j], plen[j])) {
    457           sim = 1;
    458           break;
    459         }
    460       }
    461       t.os = sim ? KIT_OS_IOS_SIMULATOR : KIT_OS_IOS;
    462       os_set = 1;
    463       break;
    464     }
    465     if (triple_tok_eq(parts[i], plen[i], "windows") ||
    466         triple_tok_eq(parts[i], plen[i], "win32")) {
    467       t.os = KIT_OS_WINDOWS;
    468       os_set = 1;
    469       break;
    470     }
    471     if (triple_tok_prefix(parts[i], plen[i], "freebsd")) {
    472       const char* ver = parts[i] + 7; /* skip "freebsd" */
    473       size_t rem = plen[i] - 7;
    474       unsigned v = 0;
    475       size_t j;
    476       for (j = 0; j < rem && ver[j] >= '0' && ver[j] <= '9'; ++j)
    477         v = v * 10 + (unsigned)(ver[j] - '0');
    478       t.os = KIT_OS_FREEBSD;
    479       t.os_version_major = (uint8_t)(v > 255 ? 0 : v);
    480       os_set = 1;
    481       break;
    482     }
    483     if (triple_tok_eq(parts[i], plen[i], "wasi")) {
    484       t.os = KIT_OS_WASI;
    485       os_set = 1;
    486       break;
    487     }
    488     if (triple_tok_eq(parts[i], plen[i], "none") ||
    489         triple_tok_eq(parts[i], plen[i], "freestanding")) {
    490       t.os = KIT_OS_FREESTANDING;
    491       os_set = 1;
    492       break;
    493     }
    494   }
    495   if (!os_set) {
    496     /* No recognized OS token. This deliberately also covers bare-metal and
    497      * vendor-only triples that carry no OS at all -- e.g. "x86_64",
    498      * "aarch64-unknown", "riscv64-unknown-elf" -- which must keep resolving to
    499      * a freestanding ELF/WASM target. We cannot tell an unrecognized OS token
    500      * (e.g. "x86_64-pc-frobnicate") apart from those without a vendor/env token
    501      * table, so erroring here would regress valid bare-metal triples. Left as a
    502      * silent freestanding default by design; see findings note for B15. */
    503     t.os = KIT_OS_FREESTANDING;
    504   }
    505   t.obj = os_objfmt(t.os, t.arch);
    506 
    507   t.ptr_align = t.ptr_size;
    508   t.big_endian = 0;
    509   t.pic = kit_target_default_pic(t.obj, t.os);
    510   t.code_model = KIT_CM_DEFAULT;
    511 
    512   *out = t;
    513   return true;
    514 }
    515 
    516 bool kit_target_to_triple(KitTargetSpec target, char* buf, size_t cap) {
    517   const char* arch;
    518   const char* os;
    519   const struct OsSpec* os_spec;
    520   int n;
    521   if (!buf || cap == 0) return false;
    522 
    523   arch = arch_triple_name(target.arch, target.ptr_size);
    524 
    525   /* Android renders the "<arch>-linux-android[<api>]" form with an optional
    526    * API-level suffix; it is the one OS whose spelling is not a plain table
    527    * literal, so it stays special-cased here. */
    528   if (target.os == KIT_OS_ANDROID) {
    529     if (target.os_version_major)
    530       n = snprintf(buf, cap, "%s-linux-android%u", arch,
    531                    (unsigned)target.os_version_major);
    532     else
    533       n = snprintf(buf, cap, "%s-linux-android", arch);
    534     return !(n < 0 || (size_t)n >= cap);
    535   }
    536 
    537   os_spec = os_spec_for(target.os);
    538   /* Unknown OS or one without a render spelling falls back to the historical
    539    * freestanding "elf" component. */
    540   os = (os_spec && os_spec->triple_render) ? os_spec->triple_render : "elf";
    541 
    542   n = snprintf(buf, cap, "%s-%s", arch, os);
    543   return !(n < 0 || (size_t)n >= cap);
    544 }
    545 
    546 bool kit_target_code_model_from_name(const char* val, KitCodeModel* out) {
    547   KitCodeModel cm;
    548   if (!val) return false;
    549   if (strcmp(val, "small") == 0 || strcmp(val, "medlow") == 0) {
    550     cm = KIT_CM_SMALL;
    551   } else if (strcmp(val, "medium") == 0 || strcmp(val, "medany") == 0) {
    552     cm = KIT_CM_MEDIUM;
    553   } else if (strcmp(val, "large") == 0 || strcmp(val, "kernel") == 0) {
    554     cm = KIT_CM_LARGE;
    555   } else {
    556     return false;
    557   }
    558   if (out) *out = cm;
    559   return true;
    560 }
    561 
    562 /* The registry is intentionally data-only. Triple parsing remains the
    563  * authority for arbitrary user spellings; these rows describe the supported
    564  * release/provisioning profiles (which additionally distinguish libc lanes).
    565  */
    566 #define PROFILE_CAP_HOSTED_ELF                                                \
    567   (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK |  \
    568    KIT_TARGET_CAP_SHARED | KIT_TARGET_CAP_EXECUTE | KIT_TARGET_CAP_SELFHOST)
    569 #define PROFILE_CAP_HOSTED_IMAGE                                              \
    570   (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK |  \
    571    KIT_TARGET_CAP_EXECUTE | KIT_TARGET_CAP_SELFHOST)
    572 #define PROFILE_CAP_CROSS_ELF                                                 \
    573   (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK |  \
    574    KIT_TARGET_CAP_SHARED)
    575 #define PROFILE_CAP_CROSS_IMAGE                                               \
    576   (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK)
    577 #define PROFILE_CAP_BARE                                                      \
    578   (KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_ASSEMBLE | KIT_TARGET_CAP_LINK |  \
    579    KIT_TARGET_CAP_EXECUTE)
    580 #define PROFILE(sel_, triple_, arch_, os_, obj_, ptr_, libc_, sysroot_,      \
    581                 provision_, hosted_, caps_)                                  \
    582   {                                                                           \
    583     .selector = (sel_), .triple = (triple_),                                  \
    584     .spec = {.arch = (arch_),                                                 \
    585              .os = (os_),                                                     \
    586              .obj = (obj_),                                                   \
    587              .ptr_size = (ptr_),                                              \
    588              .ptr_align = (ptr_),                                             \
    589              .big_endian = false,                                             \
    590              .pic = ((os_) == KIT_OS_FREESTANDING ? KIT_PIC_NONE             \
    591                                                        : KIT_PIC_PIE),        \
    592              .code_model = KIT_CM_DEFAULT,                                    \
    593              .float_abi = KIT_FLOAT_ABI_DEFAULT},                             \
    594     .libc = (libc_), .sysroot = (sysroot_), .provisioning = (provision_),     \
    595     .hosted = (hosted_), .capabilities = (caps_)                              \
    596   }
    597 
    598 static const KitTargetProfile target_profiles[] = {
    599 #if KIT_OBJ_ELF_ENABLED && KIT_ARCH_AA64_ENABLED
    600     PROFILE("linux-glibc-aa64", "aarch64-linux-gnu", KIT_ARCH_ARM_64,
    601             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
    602             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    603             PROFILE_CAP_HOSTED_ELF),
    604     PROFILE("linux-musl-aa64", "aarch64-linux-musl", KIT_ARCH_ARM_64,
    605             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
    606             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    607             PROFILE_CAP_HOSTED_ELF),
    608     PROFILE("freebsd-aa64", "aarch64-freebsd", KIT_ARCH_ARM_64,
    609             KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
    610             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
    611             PROFILE_CAP_HOSTED_ELF),
    612     PROFILE("android-aa64", "aarch64-linux-android21", KIT_ARCH_ARM_64,
    613             KIT_OS_ANDROID, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_BIONIC,
    614             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    615             PROFILE_CAP_CROSS_ELF),
    616     PROFILE("freestanding-aa64", "aarch64-none-elf", KIT_ARCH_ARM_64,
    617             KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
    618             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
    619             PROFILE_CAP_BARE),
    620 #endif
    621 #if KIT_OBJ_ELF_ENABLED && KIT_ARCH_X64_ENABLED
    622     PROFILE("linux-glibc-x64", "x86_64-linux-gnu", KIT_ARCH_X86_64,
    623             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
    624             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    625             PROFILE_CAP_HOSTED_ELF),
    626     PROFILE("linux-musl-x64", "x86_64-linux-musl", KIT_ARCH_X86_64,
    627             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
    628             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    629             PROFILE_CAP_HOSTED_ELF),
    630     PROFILE("freebsd-x64", "x86_64-freebsd", KIT_ARCH_X86_64,
    631             KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
    632             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
    633             PROFILE_CAP_HOSTED_ELF),
    634     PROFILE("freestanding-x64", "x86_64-none-elf", KIT_ARCH_X86_64,
    635             KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
    636             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
    637             PROFILE_CAP_BARE),
    638 #endif
    639 #if KIT_OBJ_ELF_ENABLED && KIT_ARCH_RV64_ENABLED
    640     PROFILE("linux-glibc-rv64", "riscv64-linux-gnu", KIT_ARCH_RV64,
    641             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_GLIBC,
    642             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    643             PROFILE_CAP_HOSTED_ELF),
    644     PROFILE("linux-musl-rv64", "riscv64-linux-musl", KIT_ARCH_RV64,
    645             KIT_OS_LINUX, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_MUSL,
    646             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    647             PROFILE_CAP_HOSTED_ELF),
    648     PROFILE("freebsd-rv64", "riscv64-freebsd", KIT_ARCH_RV64,
    649             KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_FREEBSD,
    650             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
    651             PROFILE_CAP_HOSTED_ELF),
    652     PROFILE("freestanding-rv64", "riscv64-none-elf", KIT_ARCH_RV64,
    653             KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, KIT_TARGET_LIBC_NONE,
    654             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
    655             PROFILE_CAP_BARE),
    656 #endif
    657 #if KIT_OBJ_ELF_ENABLED && KIT_ARCH_RV32_ENABLED
    658     PROFILE("freestanding-rv32", "riscv32-none-elf", KIT_ARCH_RV32,
    659             KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, KIT_TARGET_LIBC_NONE,
    660             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
    661             PROFILE_CAP_BARE),
    662 #endif
    663 #if KIT_OBJ_ELF_ENABLED && KIT_ARCH_ARM32_ENABLED
    664     PROFILE("freestanding-arm32", "arm-none-eabi", KIT_ARCH_ARM_32,
    665             KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, KIT_TARGET_LIBC_NONE,
    666             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_QEMU, 0,
    667             PROFILE_CAP_BARE),
    668 #endif
    669 #if KIT_OBJ_COFF_ENABLED && KIT_ARCH_AA64_ENABLED
    670     PROFILE("windows-aa64", "aarch64-windows", KIT_ARCH_ARM_64,
    671             KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, KIT_TARGET_LIBC_UCRT,
    672             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
    673             PROFILE_CAP_HOSTED_IMAGE),
    674 #endif
    675 #if KIT_OBJ_COFF_ENABLED && KIT_ARCH_X64_ENABLED
    676     PROFILE("windows-x64", "x86_64-windows", KIT_ARCH_X86_64,
    677             KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, KIT_TARGET_LIBC_UCRT,
    678             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_VM, 1,
    679             PROFILE_CAP_HOSTED_IMAGE),
    680 #endif
    681 #if KIT_OBJ_MACHO_ENABLED && KIT_ARCH_AA64_ENABLED
    682     PROFILE("macos-aa64", "aarch64-apple-darwin", KIT_ARCH_ARM_64,
    683             KIT_OS_MACOS, KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
    684             KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_NATIVE, 1,
    685             PROFILE_CAP_HOSTED_IMAGE),
    686     PROFILE("ios-aa64", "aarch64-apple-ios", KIT_ARCH_ARM_64, KIT_OS_IOS,
    687             KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
    688             KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_SYSROOT, 1,
    689             PROFILE_CAP_CROSS_IMAGE),
    690     PROFILE("ios-simulator-aa64", "aarch64-apple-ios-simulator",
    691             KIT_ARCH_ARM_64, KIT_OS_IOS_SIMULATOR, KIT_OBJ_MACHO, 8,
    692             KIT_TARGET_LIBC_DARWIN, KIT_TARGET_SYSROOT_NATIVE_SDK,
    693             KIT_TARGET_PROVISION_SYSROOT, 1, PROFILE_CAP_CROSS_IMAGE),
    694 #endif
    695 #if KIT_OBJ_MACHO_ENABLED && KIT_ARCH_X64_ENABLED
    696     PROFILE("macos-x64", "x86_64-apple-darwin", KIT_ARCH_X86_64,
    697             KIT_OS_MACOS, KIT_OBJ_MACHO, 8, KIT_TARGET_LIBC_DARWIN,
    698             KIT_TARGET_SYSROOT_NATIVE_SDK, KIT_TARGET_PROVISION_NATIVE, 1,
    699             PROFILE_CAP_HOSTED_IMAGE),
    700     PROFILE("ios-simulator-x64", "x86_64-apple-ios-simulator",
    701             KIT_ARCH_X86_64, KIT_OS_IOS_SIMULATOR, KIT_OBJ_MACHO, 8,
    702             KIT_TARGET_LIBC_DARWIN, KIT_TARGET_SYSROOT_NATIVE_SDK,
    703             KIT_TARGET_PROVISION_SYSROOT, 1, PROFILE_CAP_CROSS_IMAGE),
    704 #endif
    705 #if KIT_OBJ_WASM_ENABLED && KIT_ARCH_WASM_ENABLED
    706     PROFILE("wasm32-wasi", "wasm32-wasi", KIT_ARCH_WASM, KIT_OS_WASI,
    707             KIT_OBJ_WASM, 4, KIT_TARGET_LIBC_WASI,
    708             KIT_TARGET_SYSROOT_EXTERNAL, KIT_TARGET_PROVISION_SYSROOT, 1,
    709             KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_LINK |
    710                 KIT_TARGET_CAP_EXECUTE),
    711     PROFILE("wasm32-freestanding", "wasm32-none", KIT_ARCH_WASM,
    712             KIT_OS_FREESTANDING, KIT_OBJ_WASM, 4, KIT_TARGET_LIBC_NONE,
    713             KIT_TARGET_SYSROOT_NONE, KIT_TARGET_PROVISION_BUILTIN, 0,
    714             KIT_TARGET_CAP_COMPILE | KIT_TARGET_CAP_LINK |
    715                 KIT_TARGET_CAP_EXECUTE),
    716 #endif
    717     /* A sentinel keeps the definition valid for component-minimal builds. */
    718     {0},
    719 };
    720 
    721 const KitTargetProfile* kit_target_profiles(size_t* count) {
    722   size_t n = sizeof target_profiles / sizeof target_profiles[0] - 1u;
    723   if (count) *count = n;
    724   return target_profiles;
    725 }
    726 
    727 const KitTargetProfile* kit_target_profile_lookup(const char* name) {
    728   size_t i, n;
    729   if (!name) return NULL;
    730   n = sizeof target_profiles / sizeof target_profiles[0] - 1u;
    731   for (i = 0; i < n; ++i) {
    732     if (strcmp(name, target_profiles[i].selector) == 0 ||
    733         strcmp(name, target_profiles[i].triple) == 0)
    734       return &target_profiles[i];
    735   }
    736   return NULL;
    737 }
    738 
    739 #undef PROFILE
    740 #undef PROFILE_CAP_BARE
    741 #undef PROFILE_CAP_CROSS_IMAGE
    742 #undef PROFILE_CAP_CROSS_ELF
    743 #undef PROFILE_CAP_HOSTED_IMAGE
    744 #undef PROFILE_CAP_HOSTED_ELF