hosted.c (33658B)
1 #include "hosted.h" 2 3 #include <stddef.h> 4 #include <stdint.h> 5 #include <string.h> 6 7 static char* hosted_join2(DriverEnv* env, const char* a, const char* b, 8 size_t* out_size) { 9 size_t alen = driver_strlen(a); 10 size_t blen = driver_strlen(b); 11 size_t slash = (alen > 0 && a[alen - 1] != '/') ? 1u : 0u; 12 size_t bytes = alen + slash + blen + 1u; 13 char* out = driver_alloc(env, bytes); 14 size_t off = 0; 15 if (!out) return NULL; 16 if (alen) { 17 driver_memcpy(out + off, a, alen); 18 off += alen; 19 } 20 if (slash) out[off++] = '/'; 21 if (blen) { 22 driver_memcpy(out + off, b, blen); 23 off += blen; 24 } 25 out[off] = '\0'; 26 if (out_size) *out_size = bytes; 27 return out; 28 } 29 30 static int hosted_add_input(DriverHostedInput* items, uint32_t* n, uint32_t cap, 31 uint8_t kind, char* path, size_t path_size) { 32 DriverHostedInput* it; 33 if (*n >= cap) return 1; 34 it = &items[*n]; 35 it->kind = kind; 36 it->path = path; 37 it->owned_path = path; 38 it->owned_size = path_size; 39 (*n)++; 40 return 0; 41 } 42 43 /* ---------------- libdir search ---------------- */ 44 45 /* Return a freshly-owned `<libdir>/file` for the first libdir that has it, or 46 * NULL when none do (or on OOM -- the caller treats both as "not found"). */ 47 static char* hosted_find_in_libdirs(DriverEnv* env, 48 const DriverHostedDirs* dirs, 49 const char* file, size_t* out_size) { 50 uint32_t i; 51 for (i = 0; i < dirs->nlibdirs; ++i) { 52 size_t size = 0; 53 char* path = hosted_join2(env, dirs->libdirs[i], file, &size); 54 if (!path) return NULL; 55 if (driver_path_exists(path)) { 56 *out_size = size; 57 return path; 58 } 59 driver_free(env, path, size); 60 } 61 return NULL; 62 } 63 64 static int hosted_libdir_has(DriverEnv* env, const DriverHostedDirs* dirs, 65 const char* file) { 66 size_t size = 0; 67 char* path = hosted_find_in_libdirs(env, dirs, file, &size); 68 if (!path) return 0; 69 driver_free(env, path, size); 70 return 1; 71 } 72 73 /* Search the libdir list for a required crt/libc file, binding the first hit 74 * as an owned input. The bound file path persists in the plan (freed by 75 * driver_hosted_plan_fini); the dir list itself is transient scratch. */ 76 static int hosted_add_required_search(DriverHostedInput* items, uint32_t* n, 77 uint32_t cap, 78 const DriverHostedRequest* req, 79 const DriverHostedDirs* dirs, 80 const char* file, uint8_t kind) { 81 size_t size = 0; 82 char* path = hosted_find_in_libdirs(req->env, dirs, file, &size); 83 if (!path) { 84 driver_errf(req->tool, 85 "hosted profile missing required file: %.*s (searched %u " 86 "library dir(s))", 87 KIT_SLICE_ARG(kit_slice_cstr(file)), (unsigned)dirs->nlibdirs); 88 return 1; 89 } 90 if (hosted_add_input(items, n, cap, kind, path, size) != 0) { 91 driver_errf(req->tool, "too many hosted inputs"); 92 driver_free(req->env, path, size); 93 return 1; 94 } 95 return 0; 96 } 97 98 /* ---------------- include dirs ---------------- */ 99 100 /* Promote one include dir into the plan as an owned copy, skipping it silently 101 * when it does not exist. The copy is required: plan->system_includes outlives 102 * the transient DriverHostedDirs (it feeds the preprocessor). */ 103 static int hosted_add_existing_include(DriverHostedPlan* plan, DriverEnv* env, 104 const char* dir) { 105 size_t len, bytes; 106 char* path; 107 if (plan->nsystem_includes >= DRIVER_HOSTED_MAX_INCLUDES) return 1; 108 if (!driver_path_exists(dir)) return 0; 109 len = driver_strlen(dir); 110 bytes = len + 1u; 111 path = driver_alloc(env, bytes); 112 if (!path) return 1; 113 driver_memcpy(path, dir, len); 114 path[len] = '\0'; 115 plan->system_includes[plan->nsystem_includes] = path; 116 plan->owned_system_includes[plan->nsystem_includes] = path; 117 plan->owned_system_include_sizes[plan->nsystem_includes] = bytes; 118 plan->nsystem_includes++; 119 return 0; 120 } 121 122 static int hosted_add_incdirs(DriverHostedPlan* plan, DriverEnv* env, 123 const DriverHostedDirs* dirs) { 124 uint32_t i; 125 for (i = 0; i < dirs->nincdirs; ++i) { 126 if (hosted_add_existing_include(plan, env, dirs->incdirs[i]) != 0) return 1; 127 } 128 return 0; 129 } 130 131 /* ---------------- defines ---------------- */ 132 133 static int hosted_add_define(DriverHostedPlan* plan, const char* name, 134 const char* body) { 135 if (plan->ndefines >= DRIVER_HOSTED_MAX_DEFINES) return 1; 136 plan->defines[plan->ndefines].name = kit_slice_cstr(name); 137 plan->defines[plan->ndefines].body = kit_slice_cstr(body); 138 plan->ndefines++; 139 return 0; 140 } 141 142 static int hosted_add_clang_compat_defines(DriverHostedPlan* plan) { 143 if (hosted_add_define(plan, "__clang__", "1") != 0 || 144 hosted_add_define(plan, "__clang_major__", "17") != 0 || 145 hosted_add_define(plan, "__clang_minor__", "0") != 0 || 146 hosted_add_define(plan, "__clang_patchlevel__", "0") != 0 || 147 hosted_add_define(plan, "__GNUC__", "4") != 0 || 148 hosted_add_define(plan, "__GNUC_MINOR__", "2") != 0 || 149 hosted_add_define(plan, "__GNUC_PATCHLEVEL__", "1") != 0 || 150 hosted_add_define(plan, "__has_builtin(x)", "0") != 0 || 151 hosted_add_define(plan, "__has_include(x)", "0") != 0 || 152 hosted_add_define(plan, "__has_include_next(x)", "0") != 0 || 153 hosted_add_define(plan, "__has_feature(x)", "0") != 0 || 154 hosted_add_define(plan, "__has_extension(x)", "0") != 0 || 155 hosted_add_define(plan, "__has_attribute(x)", "0") != 0) 156 return 1; 157 return 0; 158 } 159 160 static int hosted_add_darwin_defines(DriverHostedPlan* plan) { 161 /* __APPLE_CC__ is what the SDK's <TargetConditionals.h> actually keys on: 162 * its compiler-detection ladder treats us as "GCC/clang on Mac OS X" only 163 * when __GNUC__ AND one of __APPLE_CC__/__APPLE_CPP__/__MACOS_CLASSIC__ are 164 * defined. Without it the header can't map __arm64__/__x86_64__ onto a 165 * TARGET_CPU_* and hits its `#error unknown compiler`. 6000 is the value 166 * modern Apple clang advertises. */ 167 if (hosted_add_clang_compat_defines(plan) != 0 || 168 hosted_add_define(plan, "__APPLE__", "1") != 0 || 169 hosted_add_define(plan, "__APPLE_CC__", "6000") != 0 || 170 hosted_add_define(plan, "__MACH__", "1") != 0 || 171 hosted_add_define(plan, "__DYNAMIC__", "1") != 0) 172 return 1; 173 return 0; 174 } 175 176 static int hosted_add_linux_defines(DriverHostedPlan* plan, int gnu) { 177 if (hosted_add_clang_compat_defines(plan) != 0 || 178 hosted_add_define(plan, "__linux__", "1") != 0 || 179 hosted_add_define(plan, "__linux", "1") != 0 || 180 hosted_add_define(plan, "linux", "1") != 0 || 181 hosted_add_define(plan, "__ELF__", "1") != 0) 182 return 1; 183 if (gnu && hosted_add_define(plan, "__gnu_linux__", "1") != 0) return 1; 184 return 0; 185 } 186 187 static const char* freebsd_version_str(uint8_t major) { 188 /* Static strings indexed by major version; 0 = unspecified → "15". */ 189 static const char* const tab[21] = { 190 "15", 191 "1","2","3","4","5","6","7","8","9","10", 192 "11","12","13","14","15","16","17","18","19","20" 193 }; 194 return (major < 21) ? tab[major] : "15"; 195 } 196 197 static int hosted_add_freebsd_defines(DriverHostedPlan* plan, uint8_t version_major) { 198 /* __FreeBSD__ must be present and version-encoded or the base headers fail to 199 * compile. The version is derived from the triple; 0 falls back to 15. */ 200 if (hosted_add_clang_compat_defines(plan) != 0 || 201 hosted_add_define(plan, "__FreeBSD__", freebsd_version_str(version_major)) != 0 || 202 hosted_add_define(plan, "__ELF__", "1") != 0 || 203 hosted_add_define(plan, "__unix__", "1") != 0 || 204 hosted_add_define(plan, "__unix", "1") != 0 || 205 hosted_add_define(plan, "unix", "1") != 0) 206 return 1; 207 return 0; 208 } 209 210 /* ---------------- interpreters ---------------- */ 211 212 static const char* hosted_glibc_interp(KitArchKind arch) { 213 switch (arch) { 214 case KIT_ARCH_ARM_64: 215 return "/lib/ld-linux-aarch64.so.1"; 216 case KIT_ARCH_X86_64: 217 return "/lib64/ld-linux-x86-64.so.2"; 218 case KIT_ARCH_RV64: 219 return "/lib/ld-linux-riscv64-lp64d.so.1"; 220 default: 221 return NULL; 222 } 223 } 224 225 static const char* hosted_musl_interp(KitArchKind arch) { 226 switch (arch) { 227 case KIT_ARCH_ARM_64: 228 return "/lib/ld-musl-aarch64.so.1"; 229 case KIT_ARCH_X86_64: 230 return "/lib/ld-musl-x86_64.so.1"; 231 case KIT_ARCH_RV64: 232 return "/lib/ld-musl-riscv64.so.1"; 233 default: 234 return NULL; 235 } 236 } 237 238 /* ---------------- per-OS resolvers ---------------- 239 * Each consumes a DriverHostedDirs (include roots + library search dirs). An 240 * empty dir list means no sysroot was given and no host default was found, so 241 * the resolver emits its "requires --sysroot/KIT_SYSROOT" error. */ 242 243 static int hosted_resolve_darwin(const DriverHostedRequest* req, 244 const DriverHostedDirs* dirs, 245 DriverHostedPlan* plan) { 246 if (dirs->nlibdirs == 0) { 247 driver_errf(req->tool, 248 "Darwin hosted profile requires a macOS SDK; pass --sysroot, " 249 "set KIT_SYSROOT, or install the Command Line Tools (try: " 250 "--sysroot \"$(xcrun --sdk macosx --show-sdk-path)\")"); 251 return 1; 252 } 253 plan->profile_name = "macos-libSystem"; 254 if (hosted_add_darwin_defines(plan) != 0 || 255 hosted_add_incdirs(plan, req->env, dirs) != 0) { 256 driver_errf(req->tool, "out of memory"); 257 return 1; 258 } 259 if (!req->link_inputs) return 0; 260 { 261 size_t size = 0; 262 char* libsystem = 263 hosted_find_in_libdirs(req->env, dirs, "libSystem.tbd", &size); 264 if (!libsystem) 265 libsystem = 266 hosted_find_in_libdirs(req->env, dirs, "libSystem.dylib", &size); 267 if (!libsystem) { 268 driver_errf(req->tool, 269 "hosted profile missing required file: " 270 "libSystem.tbd or libSystem.dylib"); 271 return 1; 272 } 273 if (hosted_add_input(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, 274 DRIVER_HOSTED_INPUT_DSO, libsystem, size) != 0) { 275 driver_free(req->env, libsystem, size); 276 driver_errf(req->tool, "too many hosted inputs"); 277 return 1; 278 } 279 } 280 return 0; 281 } 282 283 static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req, 284 const DriverHostedDirs* dirs, 285 DriverHostedPlan* plan) { 286 plan->profile_name = "linux-musl-static"; 287 if (hosted_add_linux_defines(plan, 0) != 0 || 288 hosted_add_incdirs(plan, req->env, dirs) != 0) { 289 driver_errf(req->tool, "out of memory"); 290 return 1; 291 } 292 if (!req->link_inputs) return 0; 293 if (hosted_add_required_search(plan->before, &plan->nbefore, 294 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crt1.o", 295 DRIVER_HOSTED_INPUT_OBJECT) != 0 || 296 hosted_add_required_search(plan->before, &plan->nbefore, 297 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crti.o", 298 DRIVER_HOSTED_INPUT_OBJECT) != 0) 299 return 1; 300 if (hosted_add_required_search(plan->after, &plan->nafter, 301 DRIVER_HOSTED_MAX_AFTER, req, dirs, "libc.a", 302 DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 303 return 1; 304 if (hosted_add_required_search(plan->final, &plan->nfinal, 305 DRIVER_HOSTED_MAX_FINAL, req, dirs, "crtn.o", 306 DRIVER_HOSTED_INPUT_OBJECT) != 0) 307 return 1; 308 return 0; 309 } 310 311 static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req, 312 const DriverHostedDirs* dirs, 313 DriverHostedPlan* plan) { 314 const char* interp = hosted_musl_interp(req->target.arch); 315 if (!interp) { 316 driver_errf(req->tool, "no hosted musl profile for target architecture"); 317 return 1; 318 } 319 plan->profile_name = "linux-musl-dynamic"; 320 plan->interp_path = interp; 321 if (hosted_add_linux_defines(plan, 0) != 0 || 322 hosted_add_incdirs(plan, req->env, dirs) != 0) { 323 driver_errf(req->tool, "out of memory"); 324 return 1; 325 } 326 if (!req->link_inputs) return 0; 327 if (hosted_add_required_search(plan->before, &plan->nbefore, 328 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "Scrt1.o", 329 DRIVER_HOSTED_INPUT_OBJECT) != 0 || 330 hosted_add_required_search(plan->before, &plan->nbefore, 331 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crti.o", 332 DRIVER_HOSTED_INPUT_OBJECT) != 0) 333 return 1; 334 if (hosted_add_required_search(plan->after, &plan->nafter, 335 DRIVER_HOSTED_MAX_AFTER, req, dirs, "libc.so", 336 DRIVER_HOSTED_INPUT_DSO) != 0) 337 return 1; 338 if (hosted_add_required_search(plan->final, &plan->nfinal, 339 DRIVER_HOSTED_MAX_FINAL, req, dirs, "crtn.o", 340 DRIVER_HOSTED_INPUT_OBJECT) != 0) 341 return 1; 342 return 0; 343 } 344 345 static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, 346 const DriverHostedDirs* dirs, 347 DriverHostedPlan* plan) { 348 const char* interp = hosted_glibc_interp(req->target.arch); 349 if (!interp) { 350 driver_errf(req->tool, "no hosted glibc profile for target architecture"); 351 return 1; 352 } 353 plan->profile_name = "linux-glibc-dynamic"; 354 plan->interp_path = interp; 355 /* The arch multiarch include (include/<triple>) is already in the dir list, 356 * so glibc adds the same include set as the musl profiles. */ 357 if (hosted_add_linux_defines(plan, 1) != 0 || 358 hosted_add_incdirs(plan, req->env, dirs) != 0) { 359 driver_errf(req->tool, "out of memory"); 360 return 1; 361 } 362 if (!req->link_inputs) return 0; 363 if (hosted_add_required_search(plan->before, &plan->nbefore, 364 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "Scrt1.o", 365 DRIVER_HOSTED_INPUT_OBJECT) != 0 || 366 hosted_add_required_search(plan->before, &plan->nbefore, 367 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crti.o", 368 DRIVER_HOSTED_INPUT_OBJECT) != 0) 369 return 1; 370 if (hosted_add_required_search(plan->after, &plan->nafter, 371 DRIVER_HOSTED_MAX_AFTER, req, dirs, 372 "libc.so.6", DRIVER_HOSTED_INPUT_DSO) != 0 || 373 hosted_add_required_search( 374 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 375 "libc_nonshared.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 376 return 1; 377 if (hosted_add_required_search(plan->final, &plan->nfinal, 378 DRIVER_HOSTED_MAX_FINAL, req, dirs, "crtn.o", 379 DRIVER_HOSTED_INPUT_OBJECT) != 0) 380 return 1; 381 return 0; 382 } 383 384 static int hosted_resolve_linux(const DriverHostedRequest* req, 385 const DriverHostedDirs* dirs, 386 DriverHostedPlan* plan) { 387 int has_libc_a, has_libc_so, has_libc_so6, has_glibc_nonshared; 388 if (dirs->nlibdirs == 0) { 389 driver_errf(req->tool, 390 "Linux hosted profile requires --sysroot or KIT_SYSROOT"); 391 return 1; 392 } 393 /* Booleans mean "exists in some libdir". With a single sysroot libdir this is 394 * bit-identical to the historical single-dir probe; with the multi-dir host 395 * probe, glibc's split (crt + libc_nonshared.a in /usr/lib/<triple>, 396 * libc.so.6 in /lib/<triple>) is handled by the per-file ordered search. */ 397 has_libc_a = hosted_libdir_has(req->env, dirs, "libc.a"); 398 has_libc_so = hosted_libdir_has(req->env, dirs, "libc.so"); 399 has_libc_so6 = hosted_libdir_has(req->env, dirs, "libc.so.6"); 400 has_glibc_nonshared = hosted_libdir_has(req->env, dirs, "libc_nonshared.a"); 401 if (!req->static_link && has_libc_so6 && has_glibc_nonshared) 402 return hosted_resolve_linux_glibc_dynamic(req, dirs, plan); 403 if (!req->static_link && has_libc_so) 404 return hosted_resolve_linux_musl_dynamic(req, dirs, plan); 405 if (has_libc_a && !(has_libc_so6 && has_glibc_nonshared)) 406 return hosted_resolve_linux_musl_static(req, dirs, plan); 407 driver_errf(req->tool, 408 "no supported Linux hosted libc found (searched %u library " 409 "dir(s))", 410 (unsigned)dirs->nlibdirs); 411 return 1; 412 } 413 414 /* FreeBSD base-system hosted profile. libc binds libc.so.7 directly -- 415 * /usr/lib/libc.so is a GNU ld linker script kit's linker cannot parse. */ 416 static int hosted_resolve_freebsd(const DriverHostedRequest* req, 417 const DriverHostedDirs* dirs, 418 DriverHostedPlan* plan) { 419 int static_link; 420 if (dirs->nlibdirs == 0) { 421 driver_errf(req->tool, 422 "FreeBSD hosted profile requires --sysroot or KIT_SYSROOT"); 423 return 1; 424 } 425 static_link = req->static_link && hosted_libdir_has(req->env, dirs, "libc.a"); 426 plan->profile_name = static_link ? "freebsd-static" : "freebsd-dynamic"; 427 if (!static_link) plan->interp_path = "/libexec/ld-elf.so.1"; 428 if (hosted_add_freebsd_defines(plan, req->target.os_version_major) != 0 || 429 hosted_add_incdirs(plan, req->env, dirs) != 0) { 430 driver_errf(req->tool, "out of memory"); 431 return 1; 432 } 433 if (!req->link_inputs) return 0; 434 if (hosted_add_required_search(plan->before, &plan->nbefore, 435 DRIVER_HOSTED_MAX_BEFORE, req, dirs, 436 static_link ? "crt1.o" : "Scrt1.o", 437 DRIVER_HOSTED_INPUT_OBJECT) != 0 || 438 hosted_add_required_search(plan->before, &plan->nbefore, 439 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crti.o", 440 DRIVER_HOSTED_INPUT_OBJECT) != 0) 441 return 1; 442 if (static_link) { 443 /* FreeBSD 15 split the raw syscall stubs out of libc into libsys; the 444 * compiler builtins / soft-float helpers (e.g. rv64's binary128 __multf3, 445 * which libc references because the RISC-V psABI makes long double a 446 * 128-bit quad) live in libcompiler_rt.a (a.k.a. libgcc.a). libc, libsys 447 * and libcompiler_rt are mutually recursive, so after the first libc.a we 448 * append the ones the sysroot provides and re-list libc.a to pick up the 449 * back-references they introduce -- kit resolves each archive against the 450 * inputs before it and has no --start-group. */ 451 int has_libsys = hosted_libdir_has(req->env, dirs, "libsys.a"); 452 int has_crt = hosted_libdir_has(req->env, dirs, "libcompiler_rt.a"); 453 if (hosted_add_required_search(plan->after, &plan->nafter, 454 DRIVER_HOSTED_MAX_AFTER, req, dirs, "libc.a", 455 DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 456 return 1; 457 if (has_libsys && 458 hosted_add_required_search( 459 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 460 "libsys.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 461 return 1; 462 if (has_crt && hosted_add_required_search(plan->after, &plan->nafter, 463 DRIVER_HOSTED_MAX_AFTER, req, 464 dirs, "libcompiler_rt.a", 465 DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 466 return 1; 467 if ((has_libsys || has_crt) && 468 hosted_add_required_search(plan->after, &plan->nafter, 469 DRIVER_HOSTED_MAX_AFTER, req, dirs, "libc.a", 470 DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 471 return 1; 472 } else { 473 if (hosted_add_required_search(plan->after, &plan->nafter, 474 DRIVER_HOSTED_MAX_AFTER, req, dirs, 475 "libc.so.7", DRIVER_HOSTED_INPUT_DSO) != 0) 476 return 1; 477 if (hosted_libdir_has(req->env, dirs, "libsys.so.7") && 478 hosted_add_required_search(plan->after, &plan->nafter, 479 DRIVER_HOSTED_MAX_AFTER, req, dirs, 480 "libsys.so.7", DRIVER_HOSTED_INPUT_DSO) != 0) 481 return 1; 482 } 483 if (hosted_add_required_search(plan->final, &plan->nfinal, 484 DRIVER_HOSTED_MAX_FINAL, req, dirs, "crtn.o", 485 DRIVER_HOSTED_INPUT_OBJECT) != 0) 486 return 1; 487 return 0; 488 } 489 490 static int hosted_resolve_windows_mingw(const DriverHostedRequest* req, 491 const DriverHostedDirs* dirs, 492 DriverHostedPlan* plan) { 493 if (dirs->nlibdirs == 0) { 494 driver_errf(req->tool, 495 "Windows hosted profile requires --sysroot or KIT_SYSROOT"); 496 return 1; 497 } 498 plan->profile_name = "windows-mingw-ucrt"; 499 if (hosted_add_incdirs(plan, req->env, dirs) != 0) { 500 driver_errf(req->tool, "out of memory"); 501 return 1; 502 } 503 if (!req->link_inputs) return 0; 504 if (hosted_add_required_search(plan->before, &plan->nbefore, 505 DRIVER_HOSTED_MAX_BEFORE, req, dirs, "crt2.o", 506 DRIVER_HOSTED_INPUT_OBJECT) != 0 || 507 hosted_add_required_search(plan->before, &plan->nbefore, 508 DRIVER_HOSTED_MAX_BEFORE, req, dirs, 509 "crtbegin.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) 510 return 1; 511 if (hosted_add_required_search( 512 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 513 "libmingw32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 514 hosted_add_required_search( 515 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 516 "libmoldname.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 517 hosted_add_required_search( 518 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 519 "libmingwex.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 520 /* winpthreads provides mingw's POSIX time/clock/threading entry points 521 * (nanosleep64, clock_gettime64, ...). <time.h> pulls in pthread_time.h's 522 * inline wrappers that call these, so it belongs in the default mingw 523 * runtime set (llvm-mingw ships it as a core lib). Static archive -> no 524 * libwinpthread-1.dll dependency; archive semantics keep it out of links 525 * that reference none of its members. */ 526 hosted_add_required_search( 527 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 528 "libwinpthread.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 529 hosted_add_required_search( 530 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 531 "libucrt.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 532 hosted_add_required_search( 533 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 534 "libadvapi32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 535 hosted_add_required_search( 536 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 537 "libshell32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 538 hosted_add_required_search( 539 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 540 "libuser32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 541 hosted_add_required_search( 542 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 543 "libkernel32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 544 hosted_add_required_search( 545 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 546 "libmingw32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 547 hosted_add_required_search( 548 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 549 "libmoldname.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 550 hosted_add_required_search( 551 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 552 "libmingwex.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 553 hosted_add_required_search( 554 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 555 "libwinpthread.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 556 hosted_add_required_search( 557 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 558 "libucrt.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0 || 559 hosted_add_required_search( 560 plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, dirs, 561 "libkernel32.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0) 562 return 1; 563 if (hosted_add_required_search(plan->final, &plan->nfinal, 564 DRIVER_HOSTED_MAX_FINAL, req, dirs, "crtend.o", 565 DRIVER_HOSTED_INPUT_OBJECT) != 0) 566 return 1; 567 return 0; 568 } 569 570 /* ---------------- sysroot expansion (Producer A) ---------------- */ 571 572 static const char* hosted_linux_inc_triple_sub(KitArchKind arch) { 573 switch (arch) { 574 case KIT_ARCH_ARM_64: 575 return "include/aarch64-linux-gnu"; 576 case KIT_ARCH_X86_64: 577 return "include/x86_64-linux-gnu"; 578 case KIT_ARCH_RV64: 579 return "include/riscv64-linux-gnu"; 580 default: 581 return NULL; 582 } 583 } 584 585 /* Expand an explicit --sysroot/KIT_SYSROOT into the dir list, per target-OS 586 * layout. Mirrors the per-host probes' shape so both producers converge on the 587 * same resolver contract. Dirs are added unconditionally (existence is checked 588 * when the resolver consumes them); returns nonzero only on alloc/overflow. 589 * One function per hosted OS; selected by the HostedProfile table below. */ 590 591 static int hosted_sysroot_layout_darwin(DriverHostedDirs* dirs, 592 KitTargetSpec target, 593 const char* sysroot) { 594 (void)target; 595 if (driver_hosted_dirs_add_inc_join(dirs, sysroot, "usr/include") != 0 || 596 driver_hosted_dirs_add_lib_join(dirs, sysroot, "usr/lib") != 0) 597 return 1; 598 return 0; 599 } 600 601 static int hosted_sysroot_layout_linux(DriverHostedDirs* dirs, 602 KitTargetSpec target, 603 const char* sysroot) { 604 const char* triple = hosted_linux_inc_triple_sub(target.arch); 605 if (driver_hosted_dirs_add_inc_join(dirs, sysroot, "include") != 0) return 1; 606 if (triple && driver_hosted_dirs_add_inc_join(dirs, sysroot, triple) != 0) 607 return 1; 608 if (driver_hosted_dirs_add_lib_join(dirs, sysroot, "lib") != 0) return 1; 609 return 0; 610 } 611 612 static int hosted_sysroot_layout_freebsd(DriverHostedDirs* dirs, 613 KitTargetSpec target, 614 const char* sysroot) { 615 (void)target; 616 if (driver_hosted_dirs_add_inc_join(dirs, sysroot, "usr/include") != 0 || 617 driver_hosted_dirs_add_lib_join(dirs, sysroot, "usr/lib") != 0 || 618 driver_hosted_dirs_add_lib_join(dirs, sysroot, "lib") != 0) 619 return 1; 620 return 0; 621 } 622 623 static int hosted_sysroot_layout_windows(DriverHostedDirs* dirs, 624 KitTargetSpec target, 625 const char* sysroot) { 626 (void)target; 627 if (driver_hosted_dirs_add_inc_join(dirs, sysroot, "include") != 0 || 628 driver_hosted_dirs_add_lib_join(dirs, sysroot, "lib") != 0) 629 return 1; 630 return 0; 631 } 632 633 /* ---------------- hosted-profile registry ---------------- * 634 * 635 * One row per hosted (OS, object-format) pairing, mirroring the 636 * src/{arch,obj,abi}/registry.c tables. A new hosted OS is a new row: its 637 * crt/libc resolver and its --sysroot directory layout. The dispatcher and 638 * the sysroot expander both go through hosted_profile_for so the (os,obj) 639 * decision lives in exactly one place. */ 640 typedef int (*HostedResolveFn)(const DriverHostedRequest*, 641 const DriverHostedDirs*, DriverHostedPlan*); 642 typedef int (*HostedSysrootLayoutFn)(DriverHostedDirs*, KitTargetSpec, 643 const char*); 644 645 typedef struct HostedProfile { 646 KitOSKind os; 647 KitObjFmt obj; 648 HostedResolveFn resolve; 649 HostedSysrootLayoutFn sysroot_layout; 650 } HostedProfile; 651 652 static const HostedProfile hosted_profiles[] = { 653 {KIT_OS_MACOS, KIT_OBJ_MACHO, hosted_resolve_darwin, 654 hosted_sysroot_layout_darwin}, 655 {KIT_OS_LINUX, KIT_OBJ_ELF, hosted_resolve_linux, 656 hosted_sysroot_layout_linux}, 657 {KIT_OS_FREEBSD, KIT_OBJ_ELF, hosted_resolve_freebsd, 658 hosted_sysroot_layout_freebsd}, 659 {KIT_OS_WINDOWS, KIT_OBJ_COFF, hosted_resolve_windows_mingw, 660 hosted_sysroot_layout_windows}, 661 }; 662 663 /* The crt/libc resolve dispatch keys on the full (os, obj) pairing -- a 664 * matching OS with a foreign object format is not a hosted profile. */ 665 static const HostedProfile* hosted_profile_for(KitTargetSpec target) { 666 size_t i; 667 for (i = 0; i < sizeof hosted_profiles / sizeof hosted_profiles[0]; ++i) { 668 if (hosted_profiles[i].os == target.os && 669 hosted_profiles[i].obj == target.obj) 670 return &hosted_profiles[i]; 671 } 672 return NULL; 673 } 674 675 /* The sysroot directory layout keys on OS alone (each hosted OS has a single 676 * canonical object format), matching the historical os-only expansion. */ 677 static const HostedProfile* hosted_profile_for_os(KitOSKind os) { 678 size_t i; 679 for (i = 0; i < sizeof hosted_profiles / sizeof hosted_profiles[0]; ++i) { 680 if (hosted_profiles[i].os == os) return &hosted_profiles[i]; 681 } 682 return NULL; 683 } 684 685 /* Expand an explicit sysroot into the dir list via the target's profile. 686 * Unknown targets leave the dir list empty (the dispatcher emits the error). */ 687 static int hosted_dirs_from_sysroot(DriverHostedDirs* dirs, 688 KitTargetSpec target, const char* sysroot) { 689 const HostedProfile* profile = hosted_profile_for_os(target.os); 690 dirs->root = 691 sysroot; /* a single explicit sysroot; -print-sysroot reports it */ 692 if (!profile) return 0; /* unsupported target; the dispatcher emits the error */ 693 return profile->sysroot_layout(dirs, target, sysroot); 694 } 695 696 /* ---------------- orchestration ---------------- */ 697 698 int driver_hosted_dirs_resolve(const DriverHostedRequest* req, 699 DriverHostedDirs* out) { 700 const char* sysroot; 701 if (!req || !req->env || !out) return 1; 702 memset(out, 0, sizeof(*out)); 703 out->env = req->env; 704 /* KIT_SYSROOT: the single, portable, explicit env override. Works on every 705 * OS and applies to any target including cross-compiles, exactly like 706 * --sysroot. */ 707 sysroot = (req->sysroot && req->sysroot[0]) ? req->sysroot : NULL; 708 if (!sysroot) { 709 const char* env_sysroot = driver_getenv("KIT_SYSROOT"); 710 if (env_sysroot && env_sysroot[0]) sysroot = env_sysroot; 711 } 712 if (sysroot) { 713 if (hosted_dirs_from_sysroot(out, req->target, sysroot) != 0) { 714 driver_hosted_dirs_fini(out); 715 return 1; 716 } 717 } else { 718 /* Host probe: auto-discover the host's own includes/libs. Gated on the 719 * target matching the host OS *and* arch so it never fires for any 720 * cross-compile -- a host SDK is meaningless for a foreign platform. 721 * Best-effort: leaves dirs empty when nothing is found, and the resolver 722 * then emits its "requires --sysroot" error. */ 723 KitTargetSpec host = driver_host_target(); 724 if (req->target.os == host.os && req->target.arch == host.arch) 725 (void)driver_default_hosted_dirs(req->env, req->target, out); 726 } 727 return 0; 728 } 729 730 int driver_hosted_resolve(const DriverHostedRequest* req, 731 DriverHostedPlan* out) { 732 DriverHostedPlan zero = {0}; 733 DriverHostedDirs dirs; 734 const HostedProfile* profile; 735 int rc; 736 if (!req || !out || !req->env || !req->tool) return 1; 737 *out = zero; 738 if (driver_hosted_dirs_resolve(req, &dirs) != 0) { 739 driver_errf(req->tool, "out of memory"); 740 return 1; 741 } 742 profile = hosted_profile_for(req->target); 743 if (profile) { 744 rc = profile->resolve(req, &dirs, out); 745 } else { 746 driver_errf(req->tool, "no hosted libc profile for target"); 747 rc = 1; 748 } 749 if (rc == 0) { 750 /* Transfer libdir ownership from dirs into the plan so callers can add 751 * them to their lib search path for user-specified -l resolution. */ 752 uint32_t j; 753 for (j = 0; j < dirs.nlibdirs && 754 j < DRIVER_HOSTED_MAX_LIB_SEARCH_DIRS; ++j) { 755 out->lib_search_dirs[j] = dirs.libdirs[j]; 756 out->lib_search_dir_sizes[j] = dirs.libdir_sizes[j]; 757 dirs.libdirs[j] = NULL; 758 dirs.libdir_sizes[j] = 0; 759 } 760 out->nlib_search_dirs = j; 761 } 762 driver_hosted_dirs_fini(&dirs); 763 if (rc != 0) driver_hosted_plan_fini(req->env, out); 764 return rc; 765 } 766 767 void driver_hosted_plan_fini(DriverEnv* env, DriverHostedPlan* plan) { 768 uint32_t i; 769 if (!env || !plan) return; 770 for (i = 0; i < plan->nbefore; ++i) { 771 if (plan->before[i].owned_path) 772 driver_free(env, plan->before[i].owned_path, plan->before[i].owned_size); 773 } 774 for (i = 0; i < plan->nafter; ++i) { 775 if (plan->after[i].owned_path) 776 driver_free(env, plan->after[i].owned_path, plan->after[i].owned_size); 777 } 778 for (i = 0; i < plan->nfinal; ++i) { 779 if (plan->final[i].owned_path) 780 driver_free(env, plan->final[i].owned_path, plan->final[i].owned_size); 781 } 782 for (i = 0; i < plan->nsystem_includes; ++i) { 783 if (plan->owned_system_includes[i]) 784 driver_free(env, plan->owned_system_includes[i], 785 plan->owned_system_include_sizes[i]); 786 } 787 for (i = 0; i < plan->nlib_search_dirs; ++i) { 788 if (plan->lib_search_dirs[i]) 789 driver_free(env, plan->lib_search_dirs[i], 790 plan->lib_search_dir_sizes[i]); 791 } 792 memset(plan, 0, sizeof(*plan)); 793 }