target.c (14645B)
1 #include <stddef.h> 2 #include <stdint.h> 3 #include <stdio.h> 4 #include <string.h> 5 6 #include "driver.h" 7 8 /* Pure target-triple parsing. No host I/O — just string walking — so this 9 * lives outside driver/env.c (which is the syscall/host-state abstraction 10 * layer). */ 11 12 static int triple_tok_eq(const char* s, size_t n, const char* lit) { 13 size_t l = kit_slice_cstr(lit).len; 14 return n == l && memcmp(s, lit, n) == 0; 15 } 16 17 /* Prefix match for OS tokens that carry a trailing version, e.g. clang emits 18 * "freebsd15.0" / "freebsd14" rather than a bare "freebsd". */ 19 static int triple_tok_prefix(const char* s, size_t n, const char* lit) { 20 size_t l = kit_slice_cstr(lit).len; 21 return n >= l && memcmp(s, lit, l) == 0; 22 } 23 24 /* Recognize an architecture token, the single authority for the arch-name 25 * spellings the driver accepts. Writes arch + natural pointer size on a hit. 26 * Returns 0 on success, nonzero for an unrecognized token. Shared by the 27 * triple parser and the public driver_arch_from_name. */ 28 static int arch_from_tok(const char* s, size_t n, KitArchKind* arch_out, 29 uint8_t* ptr_size_out) { 30 KitArchKind arch; 31 uint8_t ptr_size; 32 if (triple_tok_eq(s, n, "x86_64") || triple_tok_eq(s, n, "amd64") || 33 triple_tok_eq(s, n, "x64")) { 34 arch = KIT_ARCH_X86_64; 35 ptr_size = 8; 36 } else if (triple_tok_eq(s, n, "i386") || triple_tok_eq(s, n, "i486") || 37 triple_tok_eq(s, n, "i586") || triple_tok_eq(s, n, "i686")) { 38 arch = KIT_ARCH_X86_32; 39 ptr_size = 4; 40 } else if (triple_tok_eq(s, n, "aarch64") || triple_tok_eq(s, n, "arm64") || 41 triple_tok_eq(s, n, "aa64")) { 42 arch = KIT_ARCH_ARM_64; 43 ptr_size = 8; 44 } else if (triple_tok_eq(s, n, "arm") || triple_tok_eq(s, n, "armv7")) { 45 arch = KIT_ARCH_ARM_32; 46 ptr_size = 4; 47 } else if (triple_tok_eq(s, n, "riscv64") || triple_tok_eq(s, n, "rv64")) { 48 arch = KIT_ARCH_RV64; 49 ptr_size = 8; 50 } else if (triple_tok_eq(s, n, "riscv32") || triple_tok_eq(s, n, "rv32")) { 51 arch = KIT_ARCH_RV32; 52 ptr_size = 4; 53 } else if (triple_tok_eq(s, n, "wasm32")) { 54 arch = KIT_ARCH_WASM; 55 ptr_size = 4; 56 } else if (triple_tok_eq(s, n, "wasm64")) { 57 arch = KIT_ARCH_WASM; 58 ptr_size = 8; 59 } else { 60 return 1; 61 } 62 if (arch_out) *arch_out = arch; 63 if (ptr_size_out) *ptr_size_out = ptr_size; 64 return 0; 65 } 66 67 int driver_arch_from_name(const char* name, KitArchKind* arch_out, 68 uint8_t* ptr_size_out) { 69 if (!name) return 1; 70 return arch_from_tok(name, kit_slice_cstr(name).len, arch_out, ptr_size_out); 71 } 72 73 KitPic driver_default_pic(KitObjFmt obj, KitOSKind os) { 74 /* WASM has no PIC/PIE concept; freestanding targets have no dynamic 75 * loader to apply load-time relocations. Everything else is hosted and 76 * defaults to PIE. */ 77 if (obj == KIT_OBJ_WASM) return KIT_PIC_NONE; 78 if (os == KIT_OS_FREESTANDING) return KIT_PIC_NONE; 79 return KIT_PIC_PIE; 80 } 81 82 int driver_link_pie(KitTargetSpec target, int explicit_pie, int shared, 83 int relocatable) { 84 if (explicit_pie) return 1; 85 if (shared || relocatable) return 0; 86 return target.pic == KIT_PIC_PIE; 87 } 88 89 const char* driver_default_exe_name(KitTargetSpec target) { 90 /* PE/COFF executables conventionally carry a `.exe` suffix; ELF/Mach-O 91 * default link output is the historical `a.out`. */ 92 return target.os == KIT_OS_WINDOWS ? "a.exe" : "a.out"; 93 } 94 95 void driver_default_obj_ext(KitTargetSpec target, const char** ext_out, 96 size_t* ext_len_out) { 97 /* Windows targets default to a `.obj` suffix; everyone else `.o`. Drivers 98 * accept both spellings as inputs, but tooling that scrapes default outputs 99 * expects the canonical platform extension. */ 100 if (target.os == KIT_OS_WINDOWS) { 101 if (ext_out) *ext_out = ".obj"; 102 if (ext_len_out) *ext_len_out = 4u; 103 } else { 104 if (ext_out) *ext_out = ".o"; 105 if (ext_len_out) *ext_len_out = 2u; 106 } 107 } 108 109 int driver_target_needs_sysroot_libdir(KitTargetSpec target) { 110 /* Windows targets fold `<sysroot>/lib` into the library search path (the 111 * mingw import-library tree). The POSIX hosted profiles enumerate their 112 * libdirs through the hosted resolver instead. */ 113 return target.os == KIT_OS_WINDOWS ? 1 : 0; 114 } 115 116 int driver_target_default_hosted_profile(KitTargetSpec target) { 117 /* Windows-COFF is the one target whose hosted libc profile is engaged by 118 * default (given a sysroot and no -nostdlib): the mingw/ucrt import 119 * libraries are mandatory to produce a runnable PE. Other targets stay 120 * freestanding unless the user opts in (-lc / explicit sysroot wiring). */ 121 return target.os == KIT_OS_WINDOWS && target.obj == KIT_OBJ_COFF ? 1 : 0; 122 } 123 124 static int target_features_grow(DriverTargetFeatures* tf) { 125 DriverEnv* env = tf->env; 126 uint32_t old_cap = tf->cap_features; 127 uint32_t new_cap = old_cap ? old_cap * 2u : 8u; 128 size_t old_sz = (size_t)old_cap * sizeof(*tf->features); 129 size_t new_sz = (size_t)new_cap * sizeof(*tf->features); 130 void* p = env->heap->realloc(env->heap, tf->features, old_sz, new_sz, 131 _Alignof(KitTargetFeature)); 132 if (!p) return 1; 133 tf->features = (KitTargetFeature*)p; 134 memset(tf->features + old_cap, 0, new_sz - old_sz); 135 tf->cap_features = new_cap; 136 return 0; 137 } 138 139 int driver_target_features_init(DriverTargetFeatures* tf, DriverEnv* env, 140 int argc_bound) { 141 size_t bound = argc_bound > 0 ? (size_t)argc_bound + 8u : 8u; 142 if (!tf || !env || !env->heap) return 1; 143 memset(tf, 0, sizeof(*tf)); 144 tf->env = env; 145 tf->cap_features = (uint32_t)bound; 146 tf->features = driver_alloc_zeroed(env, bound * sizeof(*tf->features)); 147 if (!tf->features) return 1; 148 return 0; 149 } 150 151 void driver_target_features_fini(DriverTargetFeatures* tf, DriverEnv* env) { 152 if (!tf || !env) return; 153 driver_free(env, tf->features, 154 (size_t)tf->cap_features * sizeof(*tf->features)); 155 memset(tf, 0, sizeof(*tf)); 156 } 157 158 static int target_features_record_feature(DriverTargetFeatures* tf, 159 DriverEnv* env, KitSlice name, 160 int enabled) { 161 KitTargetFeature* f; 162 (void)env; 163 if (!name.s || name.len == 0) return 1; 164 if (tf->nfeatures >= tf->cap_features && target_features_grow(tf) != 0) 165 return 1; 166 f = &tf->features[tf->nfeatures++]; 167 f->name = name; 168 f->enabled = enabled ? true : false; 169 return 0; 170 } 171 172 static const char* target_features_pull_value(const char* tool, int argc, 173 char** argv, int* i, 174 size_t prefix_len, 175 const char* flag_label) { 176 const char* a = argv[*i]; 177 if (a[prefix_len]) return a + prefix_len; 178 if (++(*i) >= argc) { 179 driver_errf(tool, "%.*s requires an argument", 180 KIT_SLICE_ARG(kit_slice_cstr(flag_label))); 181 return NULL; 182 } 183 return argv[*i]; 184 } 185 186 static int target_features_parse_mattr(DriverTargetFeatures* tf, DriverEnv* env, 187 const char* tool, const char* list) { 188 const char* p = list; 189 if (!p || !*p) { 190 driver_errf(tool, "-mattr= requires a comma-separated feature list"); 191 return 1; 192 } 193 while (*p) { 194 int enabled; 195 const char* start; 196 if (*p == '+') { 197 enabled = 1; 198 } else if (*p == '-') { 199 enabled = 0; 200 } else { 201 driver_errf(tool, "-mattr entries must start with '+' or '-': %.*s", 202 KIT_SLICE_ARG(kit_slice_cstr(p))); 203 return 1; 204 } 205 ++p; 206 start = p; 207 while (*p && *p != ',') ++p; 208 if (p == start) { 209 driver_errf(tool, "empty -mattr feature"); 210 return 1; 211 } 212 if (target_features_record_feature( 213 tf, env, (KitSlice){.s = start, .len = (size_t)(p - start)}, 214 enabled) != 0) { 215 driver_errf(tool, "out of memory"); 216 return 1; 217 } 218 if (*p == ',') ++p; 219 } 220 return 0; 221 } 222 223 int driver_target_features_try_consume(DriverTargetFeatures* tf, DriverEnv* env, 224 const char* tool, int argc, char** argv, 225 int* i) { 226 const char* a = argv[*i]; 227 const char* v; 228 if (!tf || !a) return 0; 229 230 if (driver_strneq(a, "-mattr=", 7)) { 231 return target_features_parse_mattr(tf, env, tool, a + 7) == 0 ? 1 : -1; 232 } 233 if (driver_streq(a, "-mattr")) { 234 if (++(*i) >= argc) { 235 driver_errf(tool, "-mattr requires an argument"); 236 return -1; 237 } 238 return target_features_parse_mattr(tf, env, tool, argv[*i]) == 0 ? 1 : -1; 239 } 240 if (driver_strneq(a, "-mfeature=", 10) || 241 driver_strneq(a, "-mno-feature=", 13)) { 242 return 0; 243 } 244 if (driver_strneq(a, "-march=", 7)) { 245 tf->isa = kit_slice_cstr(a + 7); 246 return 1; 247 } 248 if (driver_streq(a, "-march")) { 249 if (++(*i) >= argc) { 250 driver_errf(tool, "-march requires an argument"); 251 return -1; 252 } 253 tf->isa = kit_slice_cstr(argv[*i]); 254 return 1; 255 } 256 if (driver_strneq(a, "-mabi=", 6)) { 257 tf->abi = kit_slice_cstr(a + 6); 258 return 1; 259 } 260 if (driver_streq(a, "-mabi")) { 261 if (++(*i) >= argc) { 262 driver_errf(tool, "-mabi requires an argument"); 263 return -1; 264 } 265 tf->abi = kit_slice_cstr(argv[*i]); 266 return 1; 267 } 268 if (driver_strneq(a, "-mcpu=", 6)) { 269 tf->cpu = kit_slice_cstr(a + 6); 270 return 1; 271 } 272 if (driver_streq(a, "-mcpu")) { 273 v = target_features_pull_value(tool, argc, argv, i, 5, "-mcpu"); 274 if (!v) return -1; 275 tf->cpu = kit_slice_cstr(v); 276 return 1; 277 } 278 if (driver_strneq(a, "-mtune=", 7)) { 279 tf->tune = kit_slice_cstr(a + 7); 280 return 1; 281 } 282 if (driver_streq(a, "-mtune")) { 283 v = target_features_pull_value(tool, argc, argv, i, 6, "-mtune"); 284 if (!v) return -1; 285 tf->tune = kit_slice_cstr(v); 286 return 1; 287 } 288 if (driver_strneq(a, "-mno-", 5) && a[5] != '\0') { 289 if (target_features_record_feature(tf, env, kit_slice_cstr(a + 5), 0) != 290 0) { 291 driver_errf(tool, "out of memory"); 292 return -1; 293 } 294 return 1; 295 } 296 if (driver_strneq(a, "-m", 2) && a[2] != '\0') { 297 if (target_features_record_feature(tf, env, kit_slice_cstr(a + 2), 1) != 298 0) { 299 driver_errf(tool, "out of memory"); 300 return -1; 301 } 302 return 1; 303 } 304 return 0; 305 } 306 307 int driver_target_options(const DriverTargetFeatures* tf, const char* tool, 308 KitTargetSpec target, KitTargetOptions* out) { 309 (void)tool; 310 if (!out) return 1; 311 memset(out, 0, sizeof(*out)); 312 out->spec = target; 313 if (!tf) return 0; 314 out->isa = tf->isa; 315 out->cpu = tf->cpu; 316 out->tune = tf->tune; 317 out->abi = tf->abi; 318 out->features = tf->features; 319 out->nfeatures = tf->nfeatures; 320 return 0; 321 } 322 323 KitStatus driver_target_new(const KitContext* ctx, KitTargetSpec target, 324 const DriverTargetFeatures* tf, const char* tool, 325 KitTarget** out) { 326 KitTargetOptions opts; 327 if (driver_target_options(tf, tool, target, &opts) != 0) { 328 if (out) *out = NULL; 329 return KIT_INVALID; 330 } 331 return kit_target_new(ctx, &opts, out); 332 } 333 334 int driver_target_from_triple(const char* triple, KitTargetSpec* out) { 335 const char* parts[4]; 336 size_t plen[4]; 337 int np = 0; 338 const char* p; 339 KitTargetSpec t; 340 int os_set; 341 int i; 342 343 if (!triple || !out) return 1; 344 memset(&t, 0, sizeof(t)); 345 346 p = triple; 347 while (np < 4) { 348 const char* dash = driver_strchr(p, '-'); 349 parts[np] = p; 350 plen[np] = dash ? (size_t)(dash - p) : kit_slice_cstr(p).len; 351 if (plen[np] == 0) return 1; 352 np++; 353 if (!dash) break; 354 p = dash + 1; 355 } 356 357 if (arch_from_tok(parts[0], plen[0], &t.arch, &t.ptr_size) != 0) return 1; 358 359 os_set = 0; 360 for (i = 1; i < np; ++i) { 361 if (triple_tok_eq(parts[i], plen[i], "linux")) { 362 t.os = KIT_OS_LINUX; 363 t.obj = KIT_OBJ_ELF; 364 os_set = 1; 365 break; 366 } 367 if (triple_tok_eq(parts[i], plen[i], "darwin") || 368 triple_tok_eq(parts[i], plen[i], "macos")) { 369 t.os = KIT_OS_MACOS; 370 t.obj = KIT_OBJ_MACHO; 371 os_set = 1; 372 break; 373 } 374 if (triple_tok_eq(parts[i], plen[i], "windows") || 375 triple_tok_eq(parts[i], plen[i], "win32")) { 376 t.os = KIT_OS_WINDOWS; 377 t.obj = KIT_OBJ_COFF; 378 os_set = 1; 379 break; 380 } 381 if (triple_tok_prefix(parts[i], plen[i], "freebsd")) { 382 const char* ver = parts[i] + 7; /* skip "freebsd" */ 383 size_t rem = plen[i] - 7; 384 unsigned v = 0; 385 size_t j; 386 for (j = 0; j < rem && ver[j] >= '0' && ver[j] <= '9'; ++j) 387 v = v * 10 + (unsigned)(ver[j] - '0'); 388 t.os = KIT_OS_FREEBSD; 389 t.obj = KIT_OBJ_ELF; 390 t.os_version_major = (uint8_t)(v > 255 ? 0 : v); 391 os_set = 1; 392 break; 393 } 394 if (triple_tok_eq(parts[i], plen[i], "wasi")) { 395 t.os = KIT_OS_WASI; 396 t.obj = KIT_OBJ_WASM; 397 os_set = 1; 398 break; 399 } 400 if (triple_tok_eq(parts[i], plen[i], "none") || 401 triple_tok_eq(parts[i], plen[i], "freestanding")) { 402 t.os = KIT_OS_FREESTANDING; 403 t.obj = (t.arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF; 404 os_set = 1; 405 break; 406 } 407 } 408 if (!os_set) { 409 t.os = KIT_OS_FREESTANDING; 410 t.obj = (t.arch == KIT_ARCH_WASM) ? KIT_OBJ_WASM : KIT_OBJ_ELF; 411 } 412 413 t.ptr_align = t.ptr_size; 414 t.big_endian = 0; 415 t.pic = driver_default_pic(t.obj, t.os); 416 t.code_model = KIT_CM_DEFAULT; 417 418 *out = t; 419 return 0; 420 } 421 422 int driver_target_to_triple(KitTargetSpec target, char* buf, size_t cap) { 423 const char* arch; 424 const char* os; 425 int n; 426 if (!buf || cap == 0) return 1; 427 428 switch (target.arch) { 429 case KIT_ARCH_X86_64: 430 arch = "x86_64"; 431 break; 432 case KIT_ARCH_X86_32: 433 arch = "i386"; 434 break; 435 case KIT_ARCH_ARM_64: 436 arch = "aarch64"; 437 break; 438 case KIT_ARCH_ARM_32: 439 arch = "arm"; 440 break; 441 case KIT_ARCH_RV64: 442 arch = "riscv64"; 443 break; 444 case KIT_ARCH_RV32: 445 arch = "riscv32"; 446 break; 447 case KIT_ARCH_WASM: 448 arch = target.ptr_size == 8 ? "wasm64" : "wasm32"; 449 break; 450 default: 451 arch = "unknown"; 452 break; 453 } 454 455 switch (target.os) { 456 case KIT_OS_LINUX: 457 os = "linux"; 458 break; 459 case KIT_OS_MACOS: 460 os = "apple-darwin"; 461 break; 462 case KIT_OS_WINDOWS: 463 os = "windows"; 464 break; 465 case KIT_OS_FREEBSD: 466 os = "freebsd"; 467 break; 468 case KIT_OS_WASI: 469 os = "wasi"; 470 break; 471 case KIT_OS_FREESTANDING: 472 default: 473 os = "elf"; 474 break; 475 } 476 477 n = snprintf(buf, cap, "%.*s-%.*s", KIT_SLICE_ARG(kit_slice_cstr(arch)), 478 KIT_SLICE_ARG(kit_slice_cstr(os))); 479 return n < 0 || (size_t)n >= cap; 480 }