kit

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

commit 73252b15fee3804dec7b25c929f612252cf0a678
parent 2efb3802d3b9f032447fcc69b8c3142bc305a466
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 08:16:36 -0700

Merge ld-link-share: ld consumes shared DriverLinkFlags + DriverLinkInputSet

ld.c stops duplicating the linker-flag and link-input models that cc/build
already share (driver/lib/link_flags.c, link_inputs.c): −470 lines, behavior
byte-identical (12/12 differential vs pre-refactor + hosted -lc link runs),
and gains the shared Windows two-position runtime-archive insert. ld keeps its
foreign-linker-compat surface (GNU -m/INPUT/GROUP, Apple -arch, MS link driver)
and its documented non-PIE default.

Diffstat:
Mdriver/cmd/ld.c | 1230++++++++++++++++++++++++-------------------------------------------------------
1 file changed, 378 insertions(+), 852 deletions(-)

diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -7,6 +7,8 @@ #include "driver.h" #include "hosted.h" #include "lib_resolve.h" +#include "link_flags.h" +#include "link_inputs.h" #include "runtime.h" /* `kit ld` — link object/archive inputs into an executable, shared @@ -56,27 +58,6 @@ #define LD_TOOL "ld" -/* Per-archive metadata mirroring the relevant subset of - * KitLinkArchiveInput plus driver-side ownership info. */ -typedef struct LdArchive { - const char* path; /* path used for both open and KitSliceInput.name */ - int owned; /* 1 if `path` was alloc'd by lib_resolve */ - size_t owned_size; /* allocation size (for driver_free) */ - uint8_t whole_archive; /* nonzero == --whole-archive */ - uint8_t link_mode; /* KitLinkMode (-Bstatic/-Bdynamic/--as-needed) */ - uint8_t group_id; /* cyclic resolution group id; 0 = single-pass */ -} LdArchive; - -/* Per-DSO ownership info. The DSO bytes are loaded straight off disk - * via env->file_io into the KitSlice passed to libkit; only - * the path itself may need to be free'd if it came from -l<name> - * resolution. */ -typedef struct LdDso { - const char* path; /* path used for both open and KitSliceInput.name */ - int owned; /* 1 if `path` was alloc'd by lib_resolve */ - size_t owned_size; /* allocation size (for driver_free) */ -} LdDso; - typedef struct LdOptions { DriverEnv* env; const char* driver_path; @@ -86,10 +67,7 @@ typedef struct LdOptions { const char* output_path; /* -o */ int output_seen; - const char* entry; /* -e */ const char* script_path; /* -T */ - int text_base_set; /* -Ttext seen */ - uint64_t text_base; /* -Ttext ADDR: static ET_EXEC image base */ int no_default_libs; /* -nostdlib / -nodefaultlibs / --no-default-libs */ int nostdlib; /* -nostdlib: no CRT startup or default libraries */ int nostartfiles; /* -nostartfiles: caller supplies CRT startup */ @@ -97,85 +75,48 @@ typedef struct LdOptions { int target_explicit; /* -target / --target seen */ const char* support_dir; /* --support-dir */ const char* sysroot; /* --sysroot / KIT_SYSROOT */ - uint16_t pe_subsystem; /* KitPeSubsystem */ - /* PT_INTERP path. NULL means "let libkit pick the target default - * (e.g. /lib/ld-musl-aarch64.so.1)". Set by -dynamic-linker / - * --dynamic-linker. */ - const char* interp_path; int pie; /* -pie was requested */ - const char** object_files; - uint32_t nobject_files; - - LdArchive* archives; - uint32_t narchives; + /* Shared linker-flag model: entry, pe_subsystem, interp/soname/rpath, the + * -Wl, comma flags (defsym/section-start/build-id/map/symbols/cref/...), and + * the owned FLAG-string pool that backs them. Populated via the + * driver_link_flags_record_* family and consumed by + * driver_link_flags_fill_options. */ + DriverLinkFlags lf; - /* Shared-object inputs (positional .so / .so.N or `-l<name>` under - * -Bdynamic). The runtime loader resolves these by SONAME at link - * time → DT_NEEDED entries. */ - LdDso* dsos; - uint32_t ndsos; - KitLinkInputOrder* order; - uint32_t norder; + /* Shared link-input model: object/archive/dso arrays, the ordered link-item + * list, the -L/-F search paths, and the positional archive flag state + * (cur_whole_archive/cur_link_mode/cur_group_id). ld resolves -l/-framework + * eagerly into archives/dsos rather than via the pending-lib model, to + * preserve GNU GROUP / positional link ordering. */ + DriverLinkInputSet inputs; - const char** lib_dirs; /* -L */ - uint32_t nlib_dirs; - const char** framework_dirs; /* -F */ - uint32_t nframework_dirs; int lib_dirs_precollected; /* first pass collected argv -L / --library-path */ - char** owned_paths; /* sysroot-expanded argv/search paths */ + char** owned_paths; /* INPUT-path strings: sysroot-rewritten -L/-F dirs and + * hosted libdir strings (the FLAG strings live in lf). */ size_t* owned_path_sizes; uint32_t nowned_paths; - size_t cap_owned_paths; /* grows independently of argv_bound: a single -Wl - * token (e.g. --section-start/--defsym) can mint - * several owned strings, so this is not argv-bounded */ + size_t cap_owned_paths; /* grows independently of argv_bound */ /* Shared-library output state. */ int shared; /* -shared */ int relocatable; /* -r / --relocatable */ - const char* soname; /* -soname NAME */ - const char** rpaths; /* -rpath DIR (repeatable) */ - uint32_t nrpaths; const char** rpath_links; /* -rpath-link DIR (advisory) */ uint32_t nrpath_links; - int new_dtags; /* 1=DT_RUNPATH (default), 0=DT_RPATH */ int export_dynamic; /* -E / --export-dynamic */ - int gc_sections; /* --gc-sections / --no-gc-sections */ - int strip_debug; /* -S / --strip-debug */ - int allow_undefined; /* explicit unresolved-symbol escape hatch */ - const char* map_path; /* --map / -Map */ - const char* symbols_path; - uint8_t symbols_format; /* KitLinkSymbolsFormat */ - const char* cref_path; /* --cref FILE */ - int print_memory_usage; /* --print-memory-usage */ - uint8_t orphan_handling; /* KitLinkOrphanHandling */ - int fatal_warnings; /* --fatal-warnings */ - /* --defsym NAME=EXPR (owned name/alias strings). */ - KitLinkDefsym* defsyms; - uint32_t ndefsyms; - uint32_t cap_defsyms; - /* --section-start / -Tdata / -Tbss section address overrides. */ - KitLinkSectionStart* section_starts; - uint32_t nsection_starts; - uint32_t cap_section_starts; int static_link; /* -static: hosted libc should pick static profile */ int wants_hosted_libc; /* libc request: expand crt + libc via hosted resolver */ int windows_ucrt_target; /* *-windows-gnullvm: MinGW UCRT hosted profile */ int explicit_crt_start; /* caller supplied crt1/Scrt1/rcrt1-style start */ int has_compiler_runtime; /* caller supplied compiler builtins/runtime */ DriverHostedPlan hosted; + /* Sink for the hosted profile's system includes/defines required by the + * shared driver_link_inputs_apply_hosted contract. ld never compiles, so + * these are populated but unused (only the plan's lib dirs + crt inputs + + * interp matter to the linker). */ + DriverCflags cf; - /* --build-id state */ - uint8_t build_id_mode; /* KitBuildIdMode */ - uint8_t* build_id_bytes; /* USER mode: parsed hex, owned */ - uint32_t build_id_len; - size_t build_id_alloc; /* allocation size for free */ - - /* Mutable parse state for positional archive flags / groups. */ - uint8_t cur_whole_archive; /* pending --whole-archive flag */ - uint8_t cur_link_mode; /* KitLinkMode for following inputs */ - uint8_t cur_group_id; /* 0 outside any --start-group */ - uint8_t next_group_id; /* increments on --start-group */ + uint8_t next_group_id; /* increments on --start-group / GROUP() */ /* Opt-in: treat `/...` arguments as MSVC link.exe flags. Off by * default so legacy paths like `/usr/lib/foo.o` remain inputs. */ @@ -291,48 +232,32 @@ void driver_help_ld(void) { static int ld_alloc_arrays(LdOptions* o, int argc) { size_t bound = (size_t)argc + 32u; o->argv_bound = bound; - o->object_files = - driver_alloc_zeroed(o->env, bound * sizeof(*o->object_files)); - o->archives = driver_alloc_zeroed(o->env, bound * sizeof(*o->archives)); - o->dsos = driver_alloc_zeroed(o->env, bound * sizeof(*o->dsos)); - o->order = driver_alloc_zeroed(o->env, bound * sizeof(*o->order)); - o->lib_dirs = driver_alloc_zeroed(o->env, bound * sizeof(*o->lib_dirs)); - o->framework_dirs = - driver_alloc_zeroed(o->env, bound * sizeof(*o->framework_dirs)); o->owned_paths = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_paths)); o->owned_path_sizes = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_sizes)); o->cap_owned_paths = bound; - o->rpaths = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpaths)); o->rpath_links = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpath_links)); - if (!o->object_files || !o->archives || !o->dsos || !o->order || - !o->lib_dirs || !o->framework_dirs || !o->owned_paths || - !o->owned_path_sizes || !o->rpaths || !o->rpath_links) { + if (!o->owned_paths || !o->owned_path_sizes || !o->rpath_links) { + driver_errf(LD_TOOL, "out of memory"); + return 1; + } + if (driver_link_flags_init(&o->lf, o->env, LD_TOOL, (uint32_t)bound) != 0) + return 1; + if (driver_link_inputs_init(&o->inputs, o->env, LD_TOOL, bound) != 0) + return 1; + /* Sized for the hosted profile's includes/defines only (ld parses no -D/-I); + * see the cf comment in LdOptions. */ + if (driver_cflags_init(&o->cf, o->env, + (int)(DRIVER_HOSTED_MAX_DEFINES + + DRIVER_HOSTED_MAX_INCLUDES)) != 0) { driver_errf(LD_TOOL, "out of memory"); return 1; } - o->new_dtags = 1; return 0; } /* ---------- positional archive bookkeeping ---------- */ -static void ld_push_order(LdOptions* o, uint8_t kind, uint32_t index) { - KitLinkInputOrder* slot = &o->order[o->norder++]; - slot->kind = kind; - slot->index = index; -} - -static void ld_insert_order(LdOptions* o, uint32_t pos, uint8_t kind, - uint32_t index) { - uint32_t i; - if (pos > o->norder) pos = o->norder; - for (i = o->norder; i > pos; --i) o->order[i] = o->order[i - 1u]; - o->order[pos].kind = kind; - o->order[pos].index = index; - o->norder++; -} - static int ld_str_prefix(const char* s, const char* prefix) { size_t n = driver_strlen(prefix); return driver_strneq(s, prefix, n); @@ -372,106 +297,80 @@ static void ld_note_object_path(LdOptions* o, const char* path) { if (ld_is_crt_start_object(path)) o->explicit_crt_start = 1; } -static void ld_push_object(LdOptions* o, const char* path) { - ld_note_object_path(o, path); - o->object_files[o->nobject_files++] = path; - ld_push_order(o, KIT_LINK_INPUT_OBJ_BYTES, o->nobject_files - 1u); +/* Fill the next free archive slot in the shared input set (without ordering) + * and return its index, noting compiler-runtime archives along the way. */ +static uint32_t ld_add_archive_slot(LdOptions* o, const char* path, int owned, + size_t owned_size, uint8_t whole_archive, + uint8_t link_mode, uint8_t group_id) { + DriverArchiveInput* a = &o->inputs.archives[o->inputs.narchives++]; + if (ld_is_compiler_runtime_archive(path)) o->has_compiler_runtime = 1; + a->path = path; + a->owned = owned; + a->owned_size = owned_size; + a->whole_archive = whole_archive; + a->link_mode = link_mode; + a->group_id = group_id; + return o->inputs.narchives - 1u; } -static void ld_insert_object(LdOptions* o, const char* path, uint32_t pos) { +static uint32_t ld_add_dso_slot(LdOptions* o, const char* path, int owned, + size_t owned_size) { + DriverDsoInput* d = &o->inputs.dsos[o->inputs.ndsos++]; + d->path = path; + d->owned = owned; + d->owned_size = owned_size; + return o->inputs.ndsos - 1u; +} + +static uint32_t ld_add_object_slot(LdOptions* o, const char* path) { ld_note_object_path(o, path); - o->object_files[o->nobject_files++] = path; - ld_insert_order(o, pos, KIT_LINK_INPUT_OBJ_BYTES, o->nobject_files - 1u); + o->inputs.object_files[o->inputs.nobject_files++] = path; + return o->inputs.nobject_files - 1u; } -static void ld_push_archive(LdOptions* o, const char* path, int owned, - size_t owned_size) { - LdArchive* a = &o->archives[o->narchives++]; - if (ld_is_compiler_runtime_archive(path)) o->has_compiler_runtime = 1; - a->path = path; - a->owned = owned; - a->owned_size = owned_size; - a->whole_archive = o->cur_whole_archive; - a->link_mode = o->cur_link_mode; - a->group_id = o->cur_group_id; - ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u); +static void ld_push_object(LdOptions* o, const char* path) { + uint32_t idx = ld_add_object_slot(o, path); + driver_link_inputs_push(&o->inputs, DRIVER_LINK_OBJECT, idx); } -static void ld_push_runtime_archive(LdOptions* o, DriverRuntimeArchive* rt) { - LdArchive* a = &o->archives[o->narchives++]; - a->path = rt->path; - a->owned = 1; - a->owned_size = rt->path_size; - a->whole_archive = rt->whole_archive; - a->link_mode = rt->link_mode; - a->group_id = rt->group_id; - rt->path = NULL; - rt->path_size = 0; - ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u); +static void ld_push_archive(LdOptions* o, const char* path, int owned, + size_t owned_size) { + uint32_t idx = ld_add_archive_slot( + o, path, owned, owned_size, o->inputs.cur_whole_archive, + o->inputs.cur_link_mode, o->inputs.cur_group_id); + driver_link_inputs_push(&o->inputs, DRIVER_LINK_ARCHIVE, idx); } static void ld_push_dso(LdOptions* o, const char* path, int owned, size_t owned_size) { - LdDso* d = &o->dsos[o->ndsos++]; - d->path = path; - d->owned = owned; - d->owned_size = owned_size; - ld_push_order(o, KIT_LINK_INPUT_DSO, o->ndsos - 1u); + uint32_t idx = ld_add_dso_slot(o, path, owned, owned_size); + driver_link_inputs_push(&o->inputs, DRIVER_LINK_DSO, idx); } static int ld_order_capacity(LdOptions* o, uint32_t nmore) { - if (o->norder + nmore <= o->argv_bound) return 0; + if (o->inputs.nlink_items + nmore <= o->argv_bound) return 0; driver_errf(LD_TOOL, "too many linker inputs"); return 1; } static int ld_archive_capacity(LdOptions* o) { - if (o->narchives < o->argv_bound) return 0; + if (o->inputs.narchives < o->argv_bound) return 0; driver_errf(LD_TOOL, "too many archive inputs"); return 1; } static int ld_dso_capacity(LdOptions* o) { - if (o->ndsos < o->argv_bound) return 0; + if (o->inputs.ndsos < o->argv_bound) return 0; driver_errf(LD_TOOL, "too many shared-object inputs"); return 1; } static int ld_object_capacity(LdOptions* o) { - if (o->nobject_files < o->argv_bound) return 0; + if (o->inputs.nobject_files < o->argv_bound) return 0; driver_errf(LD_TOOL, "too many object inputs"); return 1; } -static uint32_t ld_add_archive_slot(LdOptions* o, const char* path, int owned, - size_t owned_size, uint8_t whole_archive, - uint8_t link_mode, uint8_t group_id) { - LdArchive* a = &o->archives[o->narchives++]; - if (ld_is_compiler_runtime_archive(path)) o->has_compiler_runtime = 1; - a->path = path; - a->owned = owned; - a->owned_size = owned_size; - a->whole_archive = whole_archive; - a->link_mode = link_mode; - a->group_id = group_id; - return o->narchives - 1u; -} - -static uint32_t ld_add_dso_slot(LdOptions* o, const char* path, int owned, - size_t owned_size) { - LdDso* d = &o->dsos[o->ndsos++]; - d->path = path; - d->owned = owned; - d->owned_size = owned_size; - return o->ndsos - 1u; -} - -static uint32_t ld_add_object_slot(LdOptions* o, const char* path) { - ld_note_object_path(o, path); - o->object_files[o->nobject_files++] = path; - return o->nobject_files - 1u; -} - /* Filename ends in `.so` (with no further extension) or in `.so.N` * for some run of digits and dots. */ static int driver_is_so_filename(const char* path) { @@ -580,20 +479,20 @@ static int ld_own_path(LdOptions* o, char* path, size_t size, } static int ld_add_lib_dir(LdOptions* o, const char* dir) { - if (o->nlib_dirs >= o->argv_bound) { + if (o->inputs.nlib_search_paths >= o->argv_bound) { driver_errf(LD_TOOL, "too many library search paths"); return 1; } - o->lib_dirs[o->nlib_dirs++] = dir; + o->inputs.lib_search_paths[o->inputs.nlib_search_paths++] = dir; return 0; } static int ld_add_framework_dir(LdOptions* o, const char* dir) { - if (o->nframework_dirs >= o->argv_bound) { + if (o->inputs.nframework_search_paths >= o->argv_bound) { driver_errf(LD_TOOL, "too many framework search paths"); return 1; } - o->framework_dirs[o->nframework_dirs++] = dir; + o->inputs.framework_search_paths[o->inputs.nframework_search_paths++] = dir; return 0; } @@ -695,9 +594,10 @@ static int ld_resolve_exact_library(LdOptions* o, const char* leaf, LibResolveKind* out_kind) { uint32_t i; if (!leaf || !leaf[0]) return 1; - for (i = 0; i < o->nlib_dirs; ++i) { + for (i = 0; i < o->inputs.nlib_search_paths; ++i) { size_t size = 0; - char* cand = driver_path_join(o->env, o->lib_dirs[i], leaf, &size); + char* cand = + driver_path_join(o->env, o->inputs.lib_search_paths[i], leaf, &size); if (!cand) return 1; if (driver_path_exists(cand)) { *out_path = cand; @@ -727,23 +627,26 @@ static int ld_resolve_library(LdOptions* o, const char* name, const char* alias; if (name && name[0] == ':') return ld_resolve_exact_library(o, name + 1, out_path, out_size, out_kind); - if (driver_lib_resolve_for_os(o->env, name, mode, resolve_os, o->lib_dirs, - o->nlib_dirs, out_path, out_size, + if (driver_lib_resolve_for_os(o->env, name, mode, resolve_os, + o->inputs.lib_search_paths, + o->inputs.nlib_search_paths, out_path, out_size, out_kind) == 0) return 0; if (resolve_os != LIB_RESOLVE_OS_WINDOWS) return 1; alias = ld_windows_runtime_alias(o, name); if (!alias) return 1; - return driver_lib_resolve_for_os(o->env, alias, mode, resolve_os, o->lib_dirs, - o->nlib_dirs, out_path, out_size, out_kind); + return driver_lib_resolve_for_os(o->env, alias, mode, resolve_os, + o->inputs.lib_search_paths, + o->inputs.nlib_search_paths, out_path, + out_size, out_kind); } static int ld_resolve_framework(LdOptions* o, const char* name, char** out_path, size_t* out_size) { LibResolveKind kind; - return driver_framework_resolve(o->env, name, o->framework_dirs, - o->nframework_dirs, out_path, out_size, - &kind); + return driver_framework_resolve(o->env, name, o->inputs.framework_search_paths, + o->inputs.nframework_search_paths, out_path, + out_size, &kind); } static int ld_push_framework(LdOptions* o, const char* name) { @@ -864,182 +767,6 @@ static int ld_is_cc_driver_noop(const char* tok, size_t n) { ld_tok_prefix(tok, n, "--unwindlib="); } -static int ld_apply_z_option(LdOptions* o, const char* z, size_t n) { - if (ld_tok_eq(z, n, "defs")) { - o->allow_undefined = 0; - return 0; - } - if (ld_tok_eq(z, n, "nodefs")) { - o->allow_undefined = 1; - return 0; - } - if (ld_tok_eq(z, n, "noexecstack") || ld_tok_eq(z, n, "relro") || - ld_tok_eq(z, n, "now") || ld_tok_eq(z, n, "text")) { - return 0; - } - driver_errf(LD_TOOL, "unsupported -z option: %.*s", (int)n, z); - return 1; -} - -static int ld_record_symbols_format(LdOptions* o, const char* val, size_t n) { - if (n == 2 && driver_strneq(val, "nm", 2)) { - o->symbols_format = KIT_LINK_SYMBOLS_NM; - return 0; - } - driver_errf(LD_TOOL, "unsupported --symbols-format: %.*s", (int)n, val); - return 1; -} - -/* --defsym NAME=EXPR. `val`/`n` is the span after `--defsym `. */ -static int ld_record_defsym(LdOptions* o, const char* val, size_t n) { - size_t eq = 0; - const char* name_owned; - const char* rhs; - size_t rhs_len; - KitLinkDefsym* d; - while (eq < n && val[eq] != '=') ++eq; - if (eq == 0 || eq >= n) { - driver_errf(LD_TOOL, "--defsym requires NAME=EXPR: %.*s", (int)n, val); - return 1; - } - rhs = val + eq + 1u; - rhs_len = n - eq - 1u; - if (rhs_len == 0) { - driver_errf(LD_TOOL, "--defsym requires a value: %.*s", (int)n, val); - return 1; - } - if (o->ndefsyms >= o->cap_defsyms) { - uint32_t nc = o->cap_defsyms ? o->cap_defsyms * 2u : 4u; - KitLinkDefsym* nd = driver_alloc_zeroed(o->env, nc * sizeof(*nd)); - if (!nd) { - driver_errf(LD_TOOL, "out of memory"); - return 1; - } - if (o->defsyms) { - driver_memcpy(nd, o->defsyms, o->ndefsyms * sizeof(*nd)); - driver_free(o->env, o->defsyms, o->cap_defsyms * sizeof(*nd)); - } - o->defsyms = nd; - o->cap_defsyms = nc; - } - if (ld_span_to_owned_cstr(o, val, eq, &name_owned) != 0) return 1; - d = &o->defsyms[o->ndefsyms]; - memset(d, 0, sizeof(*d)); - d->name = kit_slice_cstr(name_owned); - if (rhs[0] >= '0' && rhs[0] <= '9') { - const char* num; - uint64_t v; - if (ld_span_to_owned_cstr(o, rhs, rhs_len, &num) != 0) return 1; - if (driver_parse_u64(num, &v) != 0) { - driver_errf(LD_TOOL, "--defsym: invalid value: %.*s", (int)rhs_len, rhs); - return 1; - } - d->value = v; - } else { - const char* alias; - if (ld_span_to_owned_cstr(o, rhs, rhs_len, &alias) != 0) return 1; - d->alias = kit_slice_cstr(alias); - } - o->ndefsyms++; - return 0; -} - -/* Store a NAME -> ADDR override where `name_owned` is ALREADY owned by - * o->owned_paths (so this neither dups nor frees it). Parses `addr` and grows - * the section_starts vector. */ -static int ld_store_section_start(LdOptions* o, const char* name_owned, - const char* addr) { - uint64_t v; - KitLinkSectionStart* ss; - if (!name_owned || !*name_owned || !addr || !*addr) { - driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR"); - return 1; - } - if (driver_parse_u64(addr, &v) != 0) { - driver_errf(LD_TOOL, "--section-start: invalid address: %s", addr); - return 1; - } - if (o->nsection_starts >= o->cap_section_starts) { - uint32_t nc = o->cap_section_starts ? o->cap_section_starts * 2u : 4u; - KitLinkSectionStart* ns = driver_alloc_zeroed(o->env, nc * sizeof(*ns)); - if (!ns) { - driver_errf(LD_TOOL, "out of memory"); - return 1; - } - if (o->section_starts) { - driver_memcpy(ns, o->section_starts, o->nsection_starts * sizeof(*ns)); - driver_free(o->env, o->section_starts, - o->cap_section_starts * sizeof(*ns)); - } - o->section_starts = ns; - o->cap_section_starts = nc; - } - ss = &o->section_starts[o->nsection_starts]; - memset(ss, 0, sizeof(*ss)); - ss->name = kit_slice_cstr(name_owned); - ss->addr = v; - o->nsection_starts++; - return 0; -} - -/* --section-start: store a NAME -> ADDR override (NAME may be ".data"/".bss" - * synthesized from -Tdata/-Tbss, or a not-yet-owned argv span). Owns `secname` - * once, then stores it. */ -static int ld_record_section_addr(LdOptions* o, const char* secname, - const char* addr) { - const char* name_owned; - if (!secname || !*secname) { - driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR"); - return 1; - } - if (ld_span_to_owned_cstr(o, secname, driver_strlen(secname), &name_owned) != - 0) - return 1; - return ld_store_section_start(o, name_owned, addr); -} - -static int ld_record_section_start(LdOptions* o, const char* val, size_t n) { - size_t eq = n; - size_t i; - const char* name_owned; - const char* addr_owned; - for (i = 0; i < n; ++i) - if (val[i] == '=') eq = i; - if (eq == n || eq == 0) { - driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR: %.*s", (int)n, - val); - return 1; - } - /* Own the name span ONCE here and store it directly — going through - * ld_record_section_addr would re-dup it, costing a third owned slot per - * --section-start (the overflow this fixes). */ - if (ld_span_to_owned_cstr(o, val, eq, &name_owned) != 0) return 1; - if (ld_span_to_owned_cstr(o, val + eq + 1u, n - eq - 1u, &addr_owned) != 0) - return 1; - return ld_store_section_start(o, name_owned, addr_owned); -} - -static int ld_record_orphan_handling(LdOptions* o, const char* val, size_t n) { - if (n == 5 && driver_strneq(val, "place", 5)) { - o->orphan_handling = KIT_LINK_ORPHAN_PLACE; - return 0; - } - if (n == 4 && driver_strneq(val, "warn", 4)) { - o->orphan_handling = KIT_LINK_ORPHAN_WARN; - return 0; - } - if (n == 5 && driver_strneq(val, "error", 5)) { - o->orphan_handling = KIT_LINK_ORPHAN_ERROR; - return 0; - } - if (n == 7 && driver_strneq(val, "discard", 7)) { - o->orphan_handling = KIT_LINK_ORPHAN_DISCARD; - return 0; - } - driver_errf(LD_TOOL, "unsupported --orphan-handling: %.*s", (int)n, val); - return 1; -} - static int ld_parse_wl(LdOptions* o, const char* arg) { const char* p = arg; int expect_rpath = 0; @@ -1066,9 +793,10 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { expect_section_start || expect_cref || expect_orphan || expect_framework || expect_framework_dir) { if (expect_defsym) { - if (ld_record_defsym(o, tok, n) != 0) return 1; + if (driver_link_flags_record_defsym(&o->lf, tok, n) != 0) return 1; } else if (expect_section_start) { - if (ld_record_section_start(o, tok, n) != 0) return 1; + if (driver_link_flags_record_section_start(&o->lf, tok, n) != 0) + return 1; } else if (expect_framework) { const char* owned; if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; @@ -1078,49 +806,32 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; if (ld_add_framework_dir(o, owned) != 0) return 1; } else if (expect_cref) { - const char* owned; - if (n == 0) { - driver_errf(LD_TOOL, "--cref requires a non-empty path"); - return 1; - } - if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->cref_path = owned; + if (driver_link_flags_record_cref(&o->lf, tok, n) != 0) return 1; } else if (expect_orphan) { - if (ld_record_orphan_handling(o, tok, n) != 0) return 1; + if (driver_link_flags_record_orphan_handling(&o->lf, tok, n) != 0) + return 1; } else if (expect_rpath) { const char* owned; if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->rpaths[o->nrpaths++] = owned; + o->lf.rpaths[o->lf.nrpaths++] = owned; } else if (expect_soname) { const char* owned; if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->soname = owned; + o->lf.soname = owned; } else if (expect_interp) { const char* owned; if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->interp_path = owned; + o->lf.interp_path = owned; } else if (expect_z) { - if (ld_apply_z_option(o, tok, n) != 0) return 1; + if (driver_link_flags_record_z(&o->lf, tok, n) != 0) return 1; } else if (expect_emulation) { const char* owned; if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; if (ld_set_gnu_emulation(o, owned) != 0) return 1; } else if (expect_map) { - const char* owned; - if (n == 0) { - driver_errf(LD_TOOL, "--map requires a non-empty path"); - return 1; - } - if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->map_path = owned; + if (driver_link_flags_record_map(&o->lf, tok, n) != 0) return 1; } else if (expect_symbols) { - const char* owned; - if (n == 0) { - driver_errf(LD_TOOL, "--symbols requires a non-empty path"); - return 1; - } - if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1; - o->symbols_path = owned; + if (driver_link_flags_record_symbols(&o->lf, tok, n) != 0) return 1; } expect_rpath = expect_soname = expect_interp = expect_z = expect_emulation = expect_map = expect_symbols = expect_defsym = @@ -1131,20 +842,20 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { if (ld_tok_eq(tok, n, "-dead_strip") || ld_tok_eq(tok, n, "--gc-sections")) { - o->gc_sections = 1; + o->lf.gc_sections = 1; continue; } if (ld_tok_eq(tok, n, "--no-gc-sections")) { - o->gc_sections = 0; + o->lf.gc_sections = 0; continue; } if (ld_tok_eq(tok, n, "--no-undefined")) { - o->allow_undefined = 0; + o->lf.allow_undefined = 0; continue; } if (ld_tok_eq(tok, n, "--allow-undefined") || ld_tok_eq(tok, n, "--allow-shlib-undefined")) { - o->allow_undefined = 1; + o->lf.allow_undefined = 1; continue; } if (ld_tok_eq(tok, n, "--map") || ld_tok_eq(tok, n, "-Map")) { @@ -1152,23 +863,15 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_prefix(tok, n, "--map=")) { - const char* owned; - if (n == 6) { - driver_errf(LD_TOOL, "--map requires a non-empty path"); - return 1; - } - if (ld_span_to_owned_cstr(o, tok + 6, n - 6u, &owned) != 0) return 1; - o->map_path = owned; + if (driver_link_flags_record_map(&o->lf, tok + 6, n - 6u) != 0) return 1; continue; } if (ld_tok_prefix(tok, n, "-Map=")) { - const char* owned; if (n == 5) { driver_errf(LD_TOOL, "-Map requires a non-empty path"); return 1; } - if (ld_span_to_owned_cstr(o, tok + 5, n - 5u, &owned) != 0) return 1; - o->map_path = owned; + if (driver_link_flags_record_map(&o->lf, tok + 5, n - 5u) != 0) return 1; continue; } if (ld_tok_eq(tok, n, "--symbols")) { @@ -1176,41 +879,38 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_prefix(tok, n, "--symbols=")) { - const char* owned; - if (n == 10) { - driver_errf(LD_TOOL, "--symbols requires a non-empty path"); + if (driver_link_flags_record_symbols(&o->lf, tok + 10, n - 10u) != 0) return 1; - } - if (ld_span_to_owned_cstr(o, tok + 10, n - 10u, &owned) != 0) return 1; - o->symbols_path = owned; continue; } if (ld_tok_prefix(tok, n, "--symbols-format=")) { - if (ld_record_symbols_format(o, tok + 17, n - 17u) != 0) return 1; + if (driver_link_flags_record_symbols_format(&o->lf, tok + 17, n - 17u) != + 0) + return 1; continue; } if (ld_tok_eq(tok, n, "-Bstatic")) { - o->cur_link_mode = KIT_LM_STATIC; + o->inputs.cur_link_mode = KIT_LM_STATIC; continue; } if (ld_tok_eq(tok, n, "-Bdynamic")) { - o->cur_link_mode = KIT_LM_DYNAMIC; + o->inputs.cur_link_mode = KIT_LM_DYNAMIC; continue; } if (ld_tok_eq(tok, n, "--as-needed")) { - o->cur_link_mode = KIT_LM_AS_NEEDED; + o->inputs.cur_link_mode = KIT_LM_AS_NEEDED; continue; } if (ld_tok_eq(tok, n, "--no-as-needed")) { - o->cur_link_mode = KIT_LM_DYNAMIC; + o->inputs.cur_link_mode = KIT_LM_DYNAMIC; continue; } if (ld_tok_eq(tok, n, "--whole-archive")) { - o->cur_whole_archive = 1; + o->inputs.cur_whole_archive = 1; continue; } if (ld_tok_eq(tok, n, "--no-whole-archive")) { - o->cur_whole_archive = 0; + o->inputs.cur_whole_archive = 0; continue; } if (ld_tok_eq(tok, n, "-z")) { @@ -1218,7 +918,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (n > 2 && tok[0] == '-' && tok[1] == 'z') { - if (ld_apply_z_option(o, tok + 2, n - 2) != 0) return 1; + if (driver_link_flags_record_z(&o->lf, tok + 2, n - 2) != 0) return 1; continue; } if (ld_tok_eq(tok, n, "--eh-frame-hdr") || @@ -1246,7 +946,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_eq(tok, n, "-S") || ld_tok_eq(tok, n, "--strip-debug")) { - o->strip_debug = 1; + o->lf.strip_debug = 1; continue; } if (ld_tok_eq(tok, n, "-E") || ld_tok_eq(tok, n, "--export-dynamic")) { @@ -1254,11 +954,11 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_eq(tok, n, "--enable-new-dtags")) { - o->new_dtags = 1; + o->lf.new_dtags = 1; continue; } if (ld_tok_eq(tok, n, "--disable-new-dtags")) { - o->new_dtags = 0; + o->lf.new_dtags = 0; continue; } if (ld_tok_eq(tok, n, "-rpath")) { @@ -1282,7 +982,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { if (ld_tok_prefix(tok, n, "-rpath=")) { const char* owned; if (ld_span_to_owned_cstr(o, tok + 7, n - 7u, &owned) != 0) return 1; - o->rpaths[o->nrpaths++] = owned; + o->lf.rpaths[o->lf.nrpaths++] = owned; continue; } if (ld_tok_eq(tok, n, "-soname")) { @@ -1292,7 +992,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { if (ld_tok_prefix(tok, n, "-soname=")) { const char* owned; if (ld_span_to_owned_cstr(o, tok + 8, n - 8u, &owned) != 0) return 1; - o->soname = owned; + o->lf.soname = owned; continue; } if (ld_tok_eq(tok, n, "-dynamic-linker") || @@ -1303,7 +1003,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { if (ld_tok_prefix(tok, n, "-dynamic-linker=")) { const char* owned; if (ld_span_to_owned_cstr(o, tok + 16, n - 16u, &owned) != 0) return 1; - o->interp_path = owned; + o->lf.interp_path = owned; continue; } /* -Tdata / -Tbss section base overrides. */ @@ -1319,23 +1019,30 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { while (atok[an] && atok[an] != ',') ++an; p = atok + an + (atok[an] == ',' ? 1u : 0u); if (ld_span_to_owned_cstr(o, atok, an, &addr_owned) != 0) return 1; - if (ld_record_section_addr(o, secname, addr_owned) != 0) return 1; + if (driver_link_flags_record_section_addr(&o->lf, secname, addr_owned) != + 0) + return 1; continue; } if (ld_tok_prefix(tok, n, "-Tdata=")) { const char* addr_owned; if (ld_span_to_owned_cstr(o, tok + 7, n - 7u, &addr_owned) != 0) return 1; - if (ld_record_section_addr(o, ".data", addr_owned) != 0) return 1; + if (driver_link_flags_record_section_addr(&o->lf, ".data", addr_owned) != + 0) + return 1; continue; } if (ld_tok_prefix(tok, n, "-Tbss=")) { const char* addr_owned; if (ld_span_to_owned_cstr(o, tok + 6, n - 6u, &addr_owned) != 0) return 1; - if (ld_record_section_addr(o, ".bss", addr_owned) != 0) return 1; + if (driver_link_flags_record_section_addr(&o->lf, ".bss", addr_owned) != + 0) + return 1; continue; } if (ld_tok_prefix(tok, n, "--defsym=")) { - if (ld_record_defsym(o, tok + 9, n - 9u) != 0) return 1; + if (driver_link_flags_record_defsym(&o->lf, tok + 9, n - 9u) != 0) + return 1; continue; } if (ld_tok_eq(tok, n, "--defsym")) { @@ -1343,7 +1050,8 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_prefix(tok, n, "--section-start=")) { - if (ld_record_section_start(o, tok + 16, n - 16u) != 0) return 1; + if (driver_link_flags_record_section_start(&o->lf, tok + 16, n - 16u) != 0) + return 1; continue; } if (ld_tok_eq(tok, n, "--section-start")) { @@ -1351,9 +1059,7 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_prefix(tok, n, "--cref=")) { - const char* owned; - if (ld_span_to_owned_cstr(o, tok + 7, n - 7u, &owned) != 0) return 1; - o->cref_path = owned; + if (driver_link_flags_record_cref(&o->lf, tok + 7, n - 7u) != 0) return 1; continue; } if (ld_tok_eq(tok, n, "--cref")) { @@ -1361,7 +1067,9 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_prefix(tok, n, "--orphan-handling=")) { - if (ld_record_orphan_handling(o, tok + 18, n - 18u) != 0) return 1; + if (driver_link_flags_record_orphan_handling(&o->lf, tok + 18, n - 18u) != + 0) + return 1; continue; } if (ld_tok_eq(tok, n, "--orphan-handling")) { @@ -1369,15 +1077,15 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { continue; } if (ld_tok_eq(tok, n, "--fatal-warnings")) { - o->fatal_warnings = 1; + o->lf.fatal_warnings = 1; continue; } if (ld_tok_eq(tok, n, "--no-fatal-warnings")) { - o->fatal_warnings = 0; + o->lf.fatal_warnings = 0; continue; } if (ld_tok_eq(tok, n, "--print-memory-usage")) { - o->print_memory_usage = 1; + o->lf.print_memory_usage = 1; continue; } @@ -1583,9 +1291,10 @@ static int ld_script_resolve_file(LdInputScript* s, const char* name, driver_free(o->env, cand, size); } - for (i = 0; i < o->nlib_dirs; ++i) { + for (i = 0; i < o->inputs.nlib_search_paths; ++i) { size_t size = 0; - char* cand = driver_path_join(o->env, o->lib_dirs[i], name, &size); + char* cand = + driver_path_join(o->env, o->inputs.lib_search_paths[i], name, &size); if (!cand) { driver_errf(LD_TOOL, "out of memory"); return 1; @@ -1610,16 +1319,19 @@ static int ld_script_resolve_file(LdInputScript* s, const char* name, return 1; } +/* The GNU INPUT()/GROUP() expander replaces the DSO-script link item at + * `order_pos` with the script's first input, then inserts the rest just after + * it — preserving the script's position in the overall link order. */ static int ld_script_add_order(LdInputScript* s, uint8_t kind, uint32_t index) { if (!s->added) { - s->o->order[s->order_pos].kind = kind; - s->o->order[s->order_pos].index = index; + s->o->inputs.link_items[s->order_pos].kind = kind; + s->o->inputs.link_items[s->order_pos].index = index; s->insert_pos = s->order_pos + 1u; s->added = 1; return 0; } if (ld_order_capacity(s->o, 1) != 0) return 1; - ld_insert_order(s->o, s->insert_pos, kind, index); + driver_link_inputs_insert(&s->o->inputs, s->insert_pos, kind, index); s->insert_pos++; return 0; } @@ -1631,10 +1343,10 @@ static int ld_script_add_path(LdInputScript* s, const char* path, int owned, if (driver_has_suffix(path, ".a") || driver_has_suffix(path, ".rlib")) { uint32_t idx; if (ld_archive_capacity(o) != 0) goto fail; - idx = ld_add_archive_slot(o, path, owned, owned_size, o->cur_whole_archive, - as_needed ? KIT_LM_AS_NEEDED : o->cur_link_mode, - group_id); - return ld_script_add_order(s, KIT_LINK_INPUT_ARCHIVE, idx); + idx = ld_add_archive_slot( + o, path, owned, owned_size, o->inputs.cur_whole_archive, + as_needed ? KIT_LM_AS_NEEDED : o->inputs.cur_link_mode, group_id); + return ld_script_add_order(s, DRIVER_LINK_ARCHIVE, idx); } if (driver_is_so_filename(path) || driver_has_suffix(path, ".dylib") || driver_has_suffix(path, ".tbd")) { @@ -1642,7 +1354,7 @@ static int ld_script_add_path(LdInputScript* s, const char* path, int owned, if (as_needed) goto skip; if (ld_dso_capacity(o) != 0) goto fail; idx = ld_add_dso_slot(o, path, owned, owned_size); - return ld_script_add_order(s, KIT_LINK_INPUT_DSO, idx); + return ld_script_add_order(s, DRIVER_LINK_DSO, idx); } { uint32_t idx; @@ -1654,7 +1366,7 @@ static int ld_script_add_path(LdInputScript* s, const char* path, int owned, } if (ld_object_capacity(o) != 0) return 1; idx = ld_add_object_slot(o, keep); - return ld_script_add_order(s, KIT_LINK_INPUT_OBJ_BYTES, idx); + return ld_script_add_order(s, DRIVER_LINK_OBJECT, idx); } skip: @@ -1679,7 +1391,7 @@ static int ld_script_add_token(LdInputScript* s, const char* tok, size_t n, char* resolved = NULL; size_t resolved_size = 0; LibResolveKind kind; - LibResolveMode mode = (o->cur_link_mode == KIT_LM_STATIC) + LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY : LIB_RESOLVE_DYNAMIC_PREFER; LibResolveOS resolve_os = (o->target.os == KIT_OS_WINDOWS) @@ -1818,15 +1530,15 @@ static int ld_script_parse_input(LdInputScript* s) { static int ld_try_expand_dso_script(LdOptions* o, uint32_t order_pos, int* changed) { const KitFileIO* io = &o->env->file_io; - const KitLinkInputOrder* ord = &o->order[order_pos]; + const DriverLinkItem* ord = &o->inputs.link_items[order_pos]; const char* path; KitFileData fd; LdInputScript s; const char* dir = NULL; int rc = 0; - if (ord->kind != KIT_LINK_INPUT_DSO) return 0; - if (ord->index >= o->ndsos) return 0; - path = o->dsos[ord->index].path; + if (ord->kind != DRIVER_LINK_DSO) return 0; + if (ord->index >= o->inputs.ndsos) return 0; + path = o->inputs.dsos[ord->index].path; if (!path || !io->read_all) return 0; memset(&fd, 0, sizeof fd); if (io->read_all(io->user, path, &fd) != KIT_OK) { @@ -1864,7 +1576,7 @@ static int ld_expand_dso_scripts(LdOptions* o) { for (pass = 0; pass < 8u; ++pass) { uint32_t i; int changed = 0; - for (i = 0; i < o->norder; ++i) { + for (i = 0; i < o->inputs.nlink_items; ++i) { if (ld_try_expand_dso_script(o, i, &changed) != 0) return 1; } if (!changed) return 0; @@ -1873,64 +1585,6 @@ static int ld_expand_dso_scripts(LdOptions* o) { return 1; } -/* ---------- --build-id parsing ---------- */ - -/* Parse `--build-id=...` argument into options. Accepts "none", "sha256", - * "uuid", or "0x<even-hex>". Returns 0 on success, 1 on bad value. */ -static int ld_parse_build_id(LdOptions* o, const char* val) { - if (driver_streq(val, "none")) { - o->build_id_mode = KIT_BUILDID_NONE; - return 0; - } - if (driver_streq(val, "sha256")) { - o->build_id_mode = KIT_BUILDID_SHA256; - return 0; - } - if (driver_streq(val, "uuid")) { - o->build_id_mode = KIT_BUILDID_UUID; - return 0; - } - - /* "0x<hex>" — must be even number of hex digits, at least one byte. */ - if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) { - const char* hex = val + 2; - size_t hex_len = driver_strlen(hex); - size_t nbytes; - size_t i; - uint8_t* buf; - if (hex_len == 0 || (hex_len & 1u)) { - driver_errf(LD_TOOL, - "--build-id=0xHEX requires an even number of hex digits"); - return 1; - } - nbytes = hex_len / 2; - buf = driver_alloc(o->env, nbytes); - if (!buf) { - driver_errf(LD_TOOL, "out of memory"); - return 1; - } - for (i = 0; i < nbytes; ++i) { - int hi = driver_hex_nibble(hex[2 * i]); - int lo = driver_hex_nibble(hex[2 * i + 1]); - if (hi < 0 || lo < 0) { - driver_errf(LD_TOOL, "--build-id: invalid hex digit"); - driver_free(o->env, buf, nbytes); - return 1; - } - buf[i] = (uint8_t)((hi << 4) | lo); - } - o->build_id_mode = KIT_BUILDID_USER; - o->build_id_bytes = buf; - o->build_id_len = (uint32_t)nbytes; - o->build_id_alloc = nbytes; - return 0; - } - - driver_errf(LD_TOOL, "--build-id: unknown value: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(val))); - return 1; -} - /* ---------- argv parser ---------- */ /* If `arg` starts with `prefix` followed by '=', returns the tail past the @@ -1976,34 +1630,6 @@ static int ms_flag_match(const char* arg, const char* key) { return arg[1 + klen] == '\0'; } -static int ld_subsystem_value_eq(const char* val, const char* want) { - size_t i; - for (i = 0; want[i]; ++i) { - char a = val[i]; - char b = want[i]; - if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A'); - if (b >= 'a' && b <= 'z') b = (char)(b - 'a' + 'A'); - if (a != b) return 0; - } - return val[i] == '\0' || val[i] == ','; -} - -static int ld_parse_pe_subsystem(LdOptions* o, const char* val) { - if (ld_subsystem_value_eq(val, "CONSOLE") || - ld_subsystem_value_eq(val, "CUI")) { - o->pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_CUI; - return 0; - } - if (ld_subsystem_value_eq(val, "WINDOWS") || - ld_subsystem_value_eq(val, "GUI")) { - o->pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_GUI; - return 0; - } - driver_errf(LD_TOOL, "unsupported subsystem: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(val))); - return 1; -} - /* Parse one MSVC-style argument. Recognized subset (others warn and * skip — match-but-no-op so legacy build scripts pass cleanly): * /OUT:path → o->output_path @@ -2031,7 +1657,7 @@ static int ld_try_ms_flag(LdOptions* o, const char* a) { return 1; } if ((val = ms_flag_value(a, "ENTRY")) != NULL) { - o->entry = val; + o->lf.entry = val; return 1; } if ((val = ms_flag_value(a, "LIBPATH")) != NULL) { @@ -2045,7 +1671,7 @@ static int ld_try_ms_flag(LdOptions* o, const char* a) { char* resolved; size_t resolved_size; LibResolveKind kind; - LibResolveMode mode = (o->cur_link_mode == KIT_LM_STATIC) + LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY : LIB_RESOLVE_DYNAMIC_PREFER; if (ld_resolve_library(o, val, mode, LIB_RESOLVE_OS_WINDOWS, &resolved, @@ -2062,7 +1688,9 @@ static int ld_try_ms_flag(LdOptions* o, const char* a) { return 1; } if ((val = ms_flag_value(a, "SUBSYSTEM")) != NULL) { - if (ld_parse_pe_subsystem(o, val) != 0) return -1; + if (driver_link_flags_record_pe_subsystem(&o->lf, val, + driver_strlen(val)) != 0) + return -1; return 1; } if (ms_flag_match(a, "NOLOGO") || ms_flag_match(a, "VERBOSE") || @@ -2335,11 +1963,11 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-e requires an argument"); return 1; } - o->entry = argv[i]; + o->lf.entry = argv[i]; continue; } if ((val = arg_eq_value(a, "--entry")) != NULL) { - o->entry = val; + o->lf.entry = val; continue; } if (driver_streq(a, "--entry")) { @@ -2347,11 +1975,13 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--entry requires an argument"); return 1; } - o->entry = argv[i]; + o->lf.entry = argv[i]; continue; } if ((val = arg_eq_value(a, "--subsystem")) != NULL) { - if (ld_parse_pe_subsystem(o, val) != 0) return 1; + if (driver_link_flags_record_pe_subsystem(&o->lf, val, + driver_strlen(val)) != 0) + return 1; continue; } if (driver_streq(a, "--subsystem")) { @@ -2359,7 +1989,9 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--subsystem requires an argument"); return 1; } - if (ld_parse_pe_subsystem(o, argv[i]) != 0) return 1; + if (driver_link_flags_record_pe_subsystem(&o->lf, argv[i], + driver_strlen(argv[i])) != 0) + return 1; continue; } { @@ -2444,13 +2076,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { /* -Ttext=ADDR */ } if (tval) { - uint64_t v; - if (driver_parse_u64(tval, &v) != 0) { - driver_errf(LD_TOOL, "-Ttext: invalid address: %s", tval); - return 1; - } - o->text_base = v; - o->text_base_set = 1; + if (driver_link_flags_record_text_base(&o->lf, tval) != 0) return 1; continue; } } @@ -2472,7 +2098,8 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { secname = ".bss"; } if (secname) { - if (ld_record_section_addr(o, secname, addr) != 0) return 1; + if (driver_link_flags_record_section_addr(&o->lf, secname, addr) != 0) + return 1; continue; } } @@ -2481,11 +2108,14 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--defsym requires NAME=EXPR"); return 1; } - if (ld_record_defsym(o, argv[i], driver_strlen(argv[i])) != 0) return 1; + if (driver_link_flags_record_defsym(&o->lf, argv[i], + driver_strlen(argv[i])) != 0) + return 1; continue; } if ((val = arg_eq_value(a, "--defsym")) != NULL) { - if (ld_record_defsym(o, val, driver_strlen(val)) != 0) return 1; + if (driver_link_flags_record_defsym(&o->lf, val, driver_strlen(val)) != 0) + return 1; continue; } if (driver_streq(a, "--section-start")) { @@ -2493,12 +2123,15 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR"); return 1; } - if (ld_record_section_start(o, argv[i], driver_strlen(argv[i])) != 0) + if (driver_link_flags_record_section_start( + &o->lf, argv[i], driver_strlen(argv[i])) != 0) return 1; continue; } if ((val = arg_eq_value(a, "--section-start")) != NULL) { - if (ld_record_section_start(o, val, driver_strlen(val)) != 0) return 1; + if (driver_link_flags_record_section_start(&o->lf, val, + driver_strlen(val)) != 0) + return 1; continue; } if (driver_streq(a, "--orphan-handling")) { @@ -2506,24 +2139,27 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--orphan-handling requires a mode"); return 1; } - if (ld_record_orphan_handling(o, argv[i], driver_strlen(argv[i])) != 0) + if (driver_link_flags_record_orphan_handling( + &o->lf, argv[i], driver_strlen(argv[i])) != 0) return 1; continue; } if ((val = arg_eq_value(a, "--orphan-handling")) != NULL) { - if (ld_record_orphan_handling(o, val, driver_strlen(val)) != 0) return 1; + if (driver_link_flags_record_orphan_handling(&o->lf, val, + driver_strlen(val)) != 0) + return 1; continue; } if (driver_streq(a, "--fatal-warnings")) { - o->fatal_warnings = 1; + o->lf.fatal_warnings = 1; continue; } if (driver_streq(a, "--no-fatal-warnings")) { - o->fatal_warnings = 0; + o->lf.fatal_warnings = 0; continue; } if (driver_streq(a, "--print-memory-usage")) { - o->print_memory_usage = 1; + o->lf.print_memory_usage = 1; continue; } if (driver_streq(a, "--cref")) { @@ -2531,20 +2167,14 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--cref requires a path"); return 1; } - { - const char* owned; - if (ld_span_to_owned_cstr(o, argv[i], driver_strlen(argv[i]), &owned) != - 0) - return 1; - o->cref_path = owned; - } + if (driver_link_flags_record_cref(&o->lf, argv[i], + driver_strlen(argv[i])) != 0) + return 1; continue; } if ((val = arg_eq_value(a, "--cref")) != NULL) { - const char* owned; - if (ld_span_to_owned_cstr(o, val, driver_strlen(val), &owned) != 0) + if (driver_link_flags_record_cref(&o->lf, val, driver_strlen(val)) != 0) return 1; - o->cref_path = owned; continue; } /* -nostdlib / --no-default-libs: do not auto-resolve and link kit's @@ -2654,7 +2284,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { if (ld_note_library_request(o, name)) continue; /* -Bstatic forces .a only; everything else (default, * -Bdynamic, --as-needed) prefers .so but falls back to .a. */ - mode = (o->cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY + mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY : LIB_RESOLVE_DYNAMIC_PREFER; resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS : LIB_RESOLVE_OS_POSIX; @@ -2675,7 +2305,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { char* resolved; size_t resolved_size; LibResolveKind kind; - LibResolveMode mode = (o->cur_link_mode == KIT_LM_STATIC) + LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY : LIB_RESOLVE_DYNAMIC_PREFER; LibResolveOS resolve_os = (o->target.os == KIT_OS_WINDOWS) @@ -2706,7 +2336,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { return 1; } if (ld_note_library_request(o, argv[i])) continue; - mode = (o->cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY + mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY : LIB_RESOLVE_DYNAMIC_PREFER; resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS : LIB_RESOLVE_OS_POSIX; @@ -2735,7 +2365,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { if (driver_streq(a, "-static")) { o->target.pic = KIT_PIC_NONE; o->static_link = 1; - o->cur_link_mode = KIT_LM_STATIC; + o->inputs.cur_link_mode = KIT_LM_STATIC; o->pic_explicit = 1; continue; } @@ -2743,7 +2373,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { o->target.pic = KIT_PIC_PIE; o->pie = 1; o->static_link = 1; - o->cur_link_mode = KIT_LM_STATIC; + o->inputs.cur_link_mode = KIT_LM_STATIC; o->pic_explicit = 1; continue; } @@ -2759,15 +2389,15 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-dynamic-linker requires an argument"); return 1; } - o->interp_path = argv[i]; + o->lf.interp_path = argv[i]; continue; } if (driver_streq(a, "--no-dynamic-linker")) { - o->interp_path = ""; + o->lf.interp_path = ""; continue; } if ((val = arg_eq_value(a, "--dynamic-linker")) != NULL) { - o->interp_path = val; + o->lf.interp_path = val; continue; } if (driver_streq(a, "-no-pie")) { @@ -2794,11 +2424,11 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-soname requires an argument"); return 1; } - o->soname = argv[i]; + o->lf.soname = argv[i]; continue; } if ((val = arg_eq_value(a, "-soname")) != NULL) { - o->soname = val; + o->lf.soname = val; continue; } if (driver_streq(a, "-rpath")) { @@ -2806,11 +2436,11 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-rpath requires an argument"); return 1; } - o->rpaths[o->nrpaths++] = argv[i]; + o->lf.rpaths[o->lf.nrpaths++] = argv[i]; continue; } if ((val = arg_eq_value(a, "-rpath")) != NULL) { - o->rpaths[o->nrpaths++] = val; + o->lf.rpaths[o->lf.nrpaths++] = val; continue; } if (driver_streq(a, "-rpath-link")) { @@ -2826,23 +2456,23 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { continue; } if (driver_streq(a, "--enable-new-dtags")) { - o->new_dtags = 1; + o->lf.new_dtags = 1; continue; } if (driver_streq(a, "--disable-new-dtags")) { - o->new_dtags = 0; + o->lf.new_dtags = 0; continue; } if (driver_streq(a, "--gc-sections")) { - o->gc_sections = 1; + o->lf.gc_sections = 1; continue; } if (driver_streq(a, "-dead_strip")) { - o->gc_sections = 1; + o->lf.gc_sections = 1; continue; } if (driver_streq(a, "--no-gc-sections")) { - o->gc_sections = 0; + o->lf.gc_sections = 0; continue; } if (driver_streq(a, "--map") || driver_streq(a, "-Map")) { @@ -2854,7 +2484,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "%s requires a non-empty path", a); return 1; } - o->map_path = argv[i]; + o->lf.map_path = argv[i]; continue; } if ((val = arg_eq_value(a, "--map")) != NULL) { @@ -2862,7 +2492,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--map requires a non-empty path"); return 1; } - o->map_path = val; + o->lf.map_path = val; continue; } if ((val = arg_eq_value(a, "-Map")) != NULL) { @@ -2870,7 +2500,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-Map requires a non-empty path"); return 1; } - o->map_path = val; + o->lf.map_path = val; continue; } if (driver_streq(a, "--symbols")) { @@ -2882,7 +2512,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--symbols requires a non-empty path"); return 1; } - o->symbols_path = argv[i]; + o->lf.symbols_path = argv[i]; continue; } if ((val = arg_eq_value(a, "--symbols")) != NULL) { @@ -2890,15 +2520,17 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "--symbols requires a non-empty path"); return 1; } - o->symbols_path = val; + o->lf.symbols_path = val; continue; } if ((val = arg_eq_value(a, "--symbols-format")) != NULL) { - if (ld_record_symbols_format(o, val, driver_strlen(val)) != 0) return 1; + if (driver_link_flags_record_symbols_format(&o->lf, val, + driver_strlen(val)) != 0) + return 1; continue; } if (driver_streq(a, "-S") || driver_streq(a, "--strip-debug")) { - o->strip_debug = 1; + o->lf.strip_debug = 1; continue; } if (driver_streq(a, "-E") || driver_streq(a, "--export-dynamic")) { @@ -2920,12 +2552,12 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { continue; } if (driver_streq(a, "--no-undefined")) { - o->allow_undefined = 0; + o->lf.allow_undefined = 0; continue; } if (driver_streq(a, "--allow-undefined") || driver_streq(a, "--allow-shlib-undefined")) { - o->allow_undefined = 1; + o->lf.allow_undefined = 1; continue; } if (driver_streq(a, "-z")) { @@ -2933,40 +2565,43 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-z requires an argument"); return 1; } - if (ld_apply_z_option(o, argv[i], driver_strlen(argv[i])) != 0) return 1; + if (driver_link_flags_record_z(&o->lf, argv[i], driver_strlen(argv[i])) != + 0) + return 1; continue; } if (a[0] == '-' && a[1] == 'z' && a[2] != '\0') { - if (ld_apply_z_option(o, a + 2, driver_strlen(a + 2)) != 0) return 1; + if (driver_link_flags_record_z(&o->lf, a + 2, driver_strlen(a + 2)) != 0) + return 1; continue; } if (driver_streq(a, "--whole-archive")) { - o->cur_whole_archive = 1; + o->inputs.cur_whole_archive = 1; continue; } if (driver_streq(a, "--no-whole-archive")) { - o->cur_whole_archive = 0; + o->inputs.cur_whole_archive = 0; continue; } if (driver_streq(a, "-Bstatic")) { - o->cur_link_mode = KIT_LM_STATIC; + o->inputs.cur_link_mode = KIT_LM_STATIC; continue; } if (driver_streq(a, "-Bdynamic")) { - o->cur_link_mode = KIT_LM_DYNAMIC; + o->inputs.cur_link_mode = KIT_LM_DYNAMIC; continue; } if (driver_streq(a, "--as-needed")) { - o->cur_link_mode = KIT_LM_AS_NEEDED; + o->inputs.cur_link_mode = KIT_LM_AS_NEEDED; continue; } if (driver_streq(a, "--no-as-needed")) { - o->cur_link_mode = KIT_LM_DYNAMIC; + o->inputs.cur_link_mode = KIT_LM_DYNAMIC; continue; } if (driver_streq(a, "--start-group")) { - if (o->cur_group_id != 0) { + if (o->inputs.cur_group_id != 0) { driver_errf(LD_TOOL, "nested --start-group is not supported"); return 1; } @@ -2975,25 +2610,25 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { return 1; } o->next_group_id++; - o->cur_group_id = o->next_group_id; + o->inputs.cur_group_id = o->next_group_id; continue; } if (driver_streq(a, "--end-group")) { - if (o->cur_group_id == 0) { + if (o->inputs.cur_group_id == 0) { driver_errf(LD_TOOL, "--end-group without --start-group"); return 1; } - o->cur_group_id = 0; + o->inputs.cur_group_id = 0; continue; } if ((val = arg_eq_value(a, "--build-id")) != NULL) { - if (ld_parse_build_id(o, val) != 0) return 1; + if (driver_link_flags_record_build_id(&o->lf, val) != 0) return 1; continue; } if (driver_streq(a, "--build-id")) { /* Bareword form defaults to sha256, matching GNU ld. */ - o->build_id_mode = KIT_BUILDID_SHA256; + o->lf.build_id_mode = KIT_BUILDID_SHA256; continue; } @@ -3020,11 +2655,12 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-o is required"); return 1; } - if (o->cur_group_id != 0) { + if (o->inputs.cur_group_id != 0) { driver_errf(LD_TOOL, "missing --end-group"); return 1; } - if (o->nobject_files == 0 && o->narchives == 0 && o->ndsos == 0) { + if (o->inputs.nobject_files == 0 && o->inputs.narchives == 0 && + o->inputs.ndsos == 0) { driver_errf(LD_TOOL, "no input files"); ld_usage(); return 1; @@ -3055,15 +2691,15 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-r and -pie are incompatible"); return 1; } - if (o->interp_path) { + if (o->lf.interp_path) { driver_errf(LD_TOOL, "-dynamic-linker requires executable output"); return 1; } - if (o->soname || o->nrpaths || o->nrpath_links) { + if (o->lf.soname || o->lf.nrpaths || o->nrpath_links) { driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared"); return 1; } - if (o->ndsos) { + if (o->inputs.ndsos) { driver_errf(LD_TOOL, "-r does not support shared-object inputs"); return 1; } @@ -3071,7 +2707,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { driver_errf(LD_TOOL, "-r does not support linker scripts yet"); return 1; } - if (o->map_path || o->symbols_path) { + if (o->lf.map_path || o->lf.symbols_path) { driver_errf(LD_TOOL, "--map/--symbols require executable output"); return 1; } @@ -3084,44 +2720,23 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { static void ld_options_release(LdOptions* o) { size_t bound = o->argv_bound; uint32_t i; - for (i = 0; i < o->narchives; ++i) { - LdArchive* a = &o->archives[i]; - if (a->owned && a->path) { - driver_free(o->env, (void*)a->path, a->owned_size); - } - } - for (i = 0; i < o->ndsos; ++i) { - LdDso* d = &o->dsos[i]; - if (d->owned && d->path) { - driver_free(o->env, (void*)d->path, d->owned_size); - } - } for (i = 0; i < o->nowned_paths; ++i) { if (o->owned_paths[i]) driver_free(o->env, o->owned_paths[i], o->owned_path_sizes[i]); } - if (o->build_id_bytes) { - driver_free(o->env, o->build_id_bytes, o->build_id_alloc); - } + /* The shared flag model owns its FLAG strings (defsym/section/map/...), the + * build-id bytes, and the rpath/defsym/section-start vectors. The shared + * input set owns the resolved archive/dso paths and its parallel arrays. */ + driver_link_flags_fini(&o->lf); + driver_link_inputs_fini(&o->inputs); + driver_cflags_fini(&o->cf, o->env); driver_hosted_plan_fini(o->env, &o->hosted); - driver_free(o->env, o->object_files, bound * sizeof(*o->object_files)); - driver_free(o->env, o->archives, bound * sizeof(*o->archives)); - driver_free(o->env, o->dsos, bound * sizeof(*o->dsos)); - driver_free(o->env, o->order, bound * sizeof(*o->order)); - driver_free(o->env, o->lib_dirs, bound * sizeof(*o->lib_dirs)); - driver_free(o->env, o->framework_dirs, bound * sizeof(*o->framework_dirs)); /* owned_paths/owned_path_sizes grow independently of argv_bound. */ driver_free(o->env, o->owned_paths, o->cap_owned_paths * sizeof(*o->owned_paths)); driver_free(o->env, o->owned_path_sizes, o->cap_owned_paths * sizeof(*o->owned_path_sizes)); - driver_free(o->env, o->rpaths, bound * sizeof(*o->rpaths)); driver_free(o->env, o->rpath_links, bound * sizeof(*o->rpath_links)); - if (o->defsyms) - driver_free(o->env, o->defsyms, o->cap_defsyms * sizeof(*o->defsyms)); - if (o->section_starts) - driver_free(o->env, o->section_starts, - o->cap_section_starts * sizeof(*o->section_starts)); } /* ---------- input loading ---------- */ @@ -3168,7 +2783,7 @@ static KitStatus ld_write_link_report(const LdOptions* o, const KitFileIO* io, KIT_SLICE_ARG(kit_slice_cstr(path))); return KIT_IO; } - st = symbols ? kit_link_session_write_symbols(link, o->symbols_format, w) + st = symbols ? kit_link_session_write_symbols(link, o->lf.symbols_format, w) : kit_link_session_write_map(link, w); if (st == KIT_OK) st = kit_writer_status(w); kit_writer_close(w); @@ -3185,10 +2800,10 @@ static KitStatus ld_write_cref(const LdOptions* o, const KitFileIO* io, KitLinkSession* link) { KitWriter* w = NULL; KitStatus st; - if (!o->cref_path) return KIT_OK; - if (io->open_writer(io->user, o->cref_path, &w) != KIT_OK) { + if (!o->lf.cref_path) return KIT_OK; + if (io->open_writer(io->user, o->lf.cref_path, &w) != KIT_OK) { driver_errf(LD_TOOL, "failed to open cref output: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(o->cref_path))); + KIT_SLICE_ARG(kit_slice_cstr(o->lf.cref_path))); return KIT_IO; } st = kit_link_session_write_cref(link, w); @@ -3196,7 +2811,7 @@ static KitStatus ld_write_cref(const LdOptions* o, const KitFileIO* io, kit_writer_close(w); if (st != KIT_OK) driver_errf(LD_TOOL, "failed to write cref: %.*s", - KIT_SLICE_ARG(kit_slice_cstr(o->cref_path))); + KIT_SLICE_ARG(kit_slice_cstr(o->lf.cref_path))); return st; } @@ -3204,7 +2819,7 @@ static KitStatus ld_write_cref(const LdOptions* o, const KitFileIO* io, static KitStatus ld_print_memory_usage(LdOptions* o, KitLinkSession* link) { KitWriter* w; KitStatus st; - if (!o->print_memory_usage) return KIT_OK; + if (!o->lf.print_memory_usage) return KIT_OK; w = driver_stdout_writer(o->env); if (!w) { driver_errf(LD_TOOL, "failed to print memory usage"); @@ -3217,83 +2832,18 @@ static KitStatus ld_print_memory_usage(LdOptions* o, KitLinkSession* link) { return st; } -static int ld_append_hosted_input(LdOptions* o, const DriverHostedInput* in, - uint32_t insert_pos, int insert) { - switch ((DriverHostedInputKind)in->kind) { - case DRIVER_HOSTED_INPUT_OBJECT: - if (insert) - ld_insert_object(o, in->path, insert_pos); - else - ld_push_object(o, in->path); - return 0; - case DRIVER_HOSTED_INPUT_ARCHIVE: { - LdArchive* a = &o->archives[o->narchives++]; - a->path = in->path; - a->owned = 0; - a->owned_size = 0; - a->whole_archive = 0; - a->link_mode = KIT_LM_DEFAULT; - a->group_id = 0; - if (insert) - ld_insert_order(o, insert_pos, KIT_LINK_INPUT_ARCHIVE, - o->narchives - 1u); - else - ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u); - return 0; - } - case DRIVER_HOSTED_INPUT_DSO: { - LdDso* d = &o->dsos[o->ndsos++]; - d->path = in->path; - d->owned = 0; - d->owned_size = 0; - if (insert) - ld_insert_order(o, insert_pos, KIT_LINK_INPUT_DSO, o->ndsos - 1u); - else - ld_push_order(o, KIT_LINK_INPUT_DSO, o->ndsos - 1u); - return 0; - } - default: - driver_errf(LD_TOOL, "internal error: unknown hosted input kind"); - return 1; - } -} - -static int ld_apply_hosted_before_after(LdOptions* o) { - DriverHostedRequest req; - uint32_t i; - uint32_t insert_pos = 0; +/* Resolve and apply the hosted libc profile: hosted lib-search dirs (so user + * -l flags resolve against the sysroot), the before/after/final crt inputs in + * link order, and the interpreter. The shared helper folds all of this (plus + * the system includes/defines that ld discards) into one call; the runtime + * archive is later inserted before the final group via + * driver_link_inputs_insert_runtime_archives. The wants-hosted / -shared guard + * stays here. */ +static int ld_apply_hosted(LdOptions* o) { if (!o->wants_hosted_libc || o->shared) return 0; - memset(&req, 0, sizeof req); - req.env = o->env; - req.tool = LD_TOOL; - req.target = o->target; - req.sysroot = o->sysroot; - req.static_link = o->static_link; - req.link_inputs = 1; - if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1; - /* Add hosted lib search dirs so user -l flags resolve against the sysroot. */ - for (i = 0; i < o->hosted.nlib_search_dirs; ++i) - if (ld_add_lib_dir(o, o->hosted.lib_search_dirs[i]) != 0) return 1; - for (i = 0; i < o->hosted.nbefore; ++i) { - if (ld_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0) - return 1; - insert_pos++; - } - for (i = 0; i < o->hosted.nafter; ++i) { - if (ld_append_hosted_input(o, &o->hosted.after[i], 0, 0) != 0) return 1; - } - if (!o->interp_path && o->hosted.interp_path) - o->interp_path = o->hosted.interp_path; - return 0; -} - -static int ld_apply_hosted_final(LdOptions* o) { - uint32_t i; - if (!o->hosted.profile_name) return 0; - for (i = 0; i < o->hosted.nfinal; ++i) { - if (ld_append_hosted_input(o, &o->hosted.final[i], 0, 0) != 0) return 1; - } - return 0; + return driver_link_inputs_apply_hosted(&o->inputs, &o->hosted, &o->cf, &o->lf, + o->target, o->sysroot, o->static_link, + o->nostartfiles, /*link_action=*/1); } /* ---------- link execution ---------- */ @@ -3314,11 +2864,13 @@ static int ld_run_link(LdOptions* o) { KitSlice* obj_in = NULL; KitLinkArchiveInput* arch_in = NULL; KitSlice* dso_in = NULL; + KitLinkInputOrder* order = NULL; KitLinkScript* script = NULL; KitLinkSession* link = NULL; DriverRuntimeSupport runtime = {0}; DriverRuntimeArchive rt_archive = {0}; uint32_t i; + uint32_t norder = 0; uint32_t initial_nobject_files; int runtime_resolved = 0; int rc = 1; @@ -3332,7 +2884,7 @@ static int ld_run_link(LdOptions* o) { * deciding which hosted CRT/libc profile and compiler-runtime archive to * add. The arrays are sized to the argv bound because hosted expansion may * append start files after target detection. */ - initial_nobject_files = o->nobject_files; + initial_nobject_files = o->inputs.nobject_files; if (o->argv_bound) { obj_lf = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_lf)); obj_in = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_in)); @@ -3342,7 +2894,7 @@ static int ld_run_link(LdOptions* o) { } } for (i = 0; i < initial_nobject_files; ++i) { - const char* path = o->object_files[i]; + const char* path = o->inputs.object_files[i]; if (load_file(io, path, &obj_lf[i]) != 0) { driver_errf(LD_TOOL, "failed to read: %.*s", KIT_SLICE_ARG(kit_slice_cstr(path))); @@ -3394,7 +2946,8 @@ static int ld_run_link(LdOptions* o) { } } - if (!o->relocatable && o->target.os == KIT_OS_FREESTANDING && o->ndsos) { + if (!o->relocatable && o->target.os == KIT_OS_FREESTANDING && + o->inputs.ndsos) { driver_errf(LD_TOOL, "freestanding executable links do not accept DSO inputs"); goto out; @@ -3415,13 +2968,13 @@ static int ld_run_link(LdOptions* o) { driver_errf(LD_TOOL, "freestanding link: input '%s' arch/format does not match " "the link target", - o->object_files[oi]); + o->inputs.object_files[oi]); goto out; } } } - if (ld_apply_hosted_before_after(o) != 0) goto out; + if (ld_apply_hosted(o) != 0) goto out; /* Hosted dynamic link: default to PIE, matching what `kit cc` does. * Without PIE the link_dyn path (PLT/GOT/.rela.plt) is not entered and @@ -3459,15 +3012,17 @@ static int ld_run_link(LdOptions* o) { if (driver_runtime_prepare_archive(o->env, LD_TOOL, &runtime, o->target, 0, &rt_archive) != 0) goto out; - ld_push_runtime_archive(o, &rt_archive); + /* Insert before the hosted plan's `final` (crt-end) group — the shared + * helper also does the Windows two-position insert that the old single + * append lacked. ld's runtime is never whole-archive, matching cc/build. */ + driver_link_inputs_insert_runtime_archives( + &o->inputs, &rt_archive, o->target, o->hosted.nfinal, o->hosted.nafter); } - if (ld_apply_hosted_final(o) != 0) goto out; - if (ld_expand_dso_scripts(o) != 0) goto out; - for (i = initial_nobject_files; i < o->nobject_files; ++i) { - const char* path = o->object_files[i]; + for (i = initial_nobject_files; i < o->inputs.nobject_files; ++i) { + const char* path = o->inputs.object_files[i]; if (load_file(io, path, &obj_lf[i]) != 0) { driver_errf(LD_TOOL, "failed to read: %.*s", KIT_SLICE_ARG(kit_slice_cstr(path))); @@ -3477,17 +3032,19 @@ static int ld_run_link(LdOptions* o) { obj_in[i].len = obj_lf[i].data.size; } - if (o->narchives) { - arch_lf = driver_alloc_zeroed(o->env, o->narchives * sizeof(*arch_lf)); - arch_in = driver_alloc_zeroed(o->env, o->narchives * sizeof(*arch_in)); + if (o->inputs.narchives) { + arch_lf = + driver_alloc_zeroed(o->env, o->inputs.narchives * sizeof(*arch_lf)); + arch_in = + driver_alloc_zeroed(o->env, o->inputs.narchives * sizeof(*arch_in)); if (!arch_lf || !arch_in) { driver_errf(LD_TOOL, "out of memory"); goto out; } } - if (o->ndsos) { - dso_lf = driver_alloc_zeroed(o->env, o->ndsos * sizeof(*dso_lf)); - dso_in = driver_alloc_zeroed(o->env, o->ndsos * sizeof(*dso_in)); + if (o->inputs.ndsos) { + dso_lf = driver_alloc_zeroed(o->env, o->inputs.ndsos * sizeof(*dso_lf)); + dso_in = driver_alloc_zeroed(o->env, o->inputs.ndsos * sizeof(*dso_in)); if (!dso_lf || !dso_in) { driver_errf(LD_TOOL, "out of memory"); goto out; @@ -3495,8 +3052,8 @@ static int ld_run_link(LdOptions* o) { } /* Load archives. */ - for (i = 0; i < o->narchives; ++i) { - const LdArchive* a = &o->archives[i]; + for (i = 0; i < o->inputs.narchives; ++i) { + const DriverArchiveInput* a = &o->inputs.archives[i]; if (load_file(io, a->path, &arch_lf[i]) != 0) { driver_errf(LD_TOOL, "failed to read: %.*s", KIT_SLICE_ARG(kit_slice_cstr(a->path))); @@ -3510,8 +3067,8 @@ static int ld_run_link(LdOptions* o) { arch_in[i].group_id = a->group_id; } /* Load shared objects. */ - for (i = 0; i < o->ndsos; ++i) { - const LdDso* d = &o->dsos[i]; + for (i = 0; i < o->inputs.ndsos; ++i) { + const DriverDsoInput* d = &o->inputs.dsos[i]; if (load_file(io, d->path, &dso_lf[i]) != 0) { driver_errf(LD_TOOL, "failed to read: %.*s", KIT_SLICE_ARG(kit_slice_cstr(d->path))); @@ -3558,7 +3115,19 @@ static int ld_run_link(LdOptions* o) { goto out; } - if (o->soname || o->nrpaths || o->nrpath_links) { + /* Translate the recorded link-item order into the engine's KitLinkInputOrder + * list. ld records no compiled-source items, so the source args are NULL. */ + if (o->inputs.nlink_items) { + order = + driver_alloc_zeroed(o->env, o->inputs.nlink_items * sizeof(*order)); + if (!order) { + driver_errf(LD_TOOL, "out of memory"); + goto out; + } + } + norder = driver_link_inputs_build_order(&o->inputs, NULL, NULL, 0, order); + + if (o->lf.soname || o->lf.nrpaths || o->nrpath_links) { if (!o->shared) { driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared"); goto out; @@ -3569,114 +3138,67 @@ static int ld_run_link(LdOptions* o) { KitLinkSessionOptions lopts; KitStatus st; KitSlice* rpath_slices = NULL; - /* Lift the argv-derived -rpath dirs into slices for the linker. */ - if (o->nrpaths) { - rpath_slices = - driver_alloc_zeroed(o->env, o->nrpaths * sizeof(*rpath_slices)); - if (!rpath_slices) { - driver_errf(LD_TOOL, "out of memory"); - goto out; - } - for (i = 0; i < o->nrpaths; ++i) - rpath_slices[i] = kit_slice_cstr(o->rpaths[i]); - } - memset(&lopts, 0, sizeof(lopts)); - lopts.output_kind = o->relocatable ? KIT_LINK_OUTPUT_RELOCATABLE - : o->shared ? KIT_LINK_OUTPUT_SHARED - : KIT_LINK_OUTPUT_EXE; - lopts.entry = kit_slice_cstr(o->entry); - lopts.text_base_set = o->text_base_set; - lopts.text_base = o->text_base; - lopts.defsyms = o->defsyms; - lopts.ndefsyms = o->ndefsyms; - lopts.section_starts = o->section_starts; - lopts.nsection_starts = o->nsection_starts; - lopts.orphan_handling = o->orphan_handling; - lopts.fatal_warnings = o->fatal_warnings ? true : false; - lopts.linker_script = script; - lopts.build_id_mode = o->build_id_mode; - lopts.build_id_bytes = o->build_id_bytes; - lopts.build_id_len = o->build_id_len; - lopts.gc_sections = o->gc_sections; - lopts.strip_debug = o->strip_debug; - /* GNU ld and lld default to a non-PIE ET_EXEC; PIE is opt-in via -pie. - * The linker imposes no hosted-PIE default the way a compiler driver - * does (kit cc still defaults hosted executables to PIE through the link - * API) — a freestanding or static link has no dynamic loader to apply a - * PIE image's relocations or choose its base, so a PIE default produces a - * binary that faults under a direct/qemu loader (its writable segments - * sit below mmap_min_addr at the vaddr-0 PIE base). -shared/-r never PIE. - * Callers that want a hosted PIE executable pass -pie explicitly (as the - * musl dynamic lane and real compiler drivers do). */ + uint8_t output_kind = o->relocatable ? KIT_LINK_OUTPUT_RELOCATABLE + : o->shared ? KIT_LINK_OUTPUT_SHARED + : KIT_LINK_OUTPUT_EXE; + /* The shared flag model fills entry/text-base/defsym/section-start/orphan/ + * fatal-warnings/build-id/gc/strip/pe-subsystem/interp/soname/rpath. It also + * derives lopts.pie via driver_link_pie, but ld overrides that below. */ + if (driver_link_flags_fill_options(&o->lf, o->target, o->pie, o->shared, + o->relocatable, output_kind, script, + &lopts, &rpath_slices) != 0) + goto out; + /* GNU ld and lld default to a non-PIE ET_EXEC; PIE is opt-in via -pie. The + * linker imposes no hosted-PIE default the way a compiler driver does (kit + * cc still defaults hosted executables to PIE through the link API) — a + * freestanding or static link has no dynamic loader to apply a PIE image's + * relocations or choose its base, so a PIE default produces a binary that + * faults under a direct/qemu loader (its writable segments sit below + * mmap_min_addr at the vaddr-0 PIE base). -shared/-r never PIE. Callers that + * want a hosted PIE executable pass -pie explicitly (as the musl dynamic + * lane and real compiler drivers do); the hosted-libc dynamic lane forces + * o->pie = 1 above. So override the shared driver_link_pie decision (which + * would default hosted target.pic == KIT_PIC_PIE targets to PIE) back to + * ld's policy. */ lopts.pie = o->pie && !o->shared && !o->relocatable; - lopts.pe_subsystem = o->pe_subsystem; - lopts.interp_path = kit_slice_cstr(o->interp_path); - lopts.soname = kit_slice_cstr(o->soname); - /* Per --enable-new-dtags / --disable-new-dtags: when new_dtags is - * set (the default), -rpath entries land in DT_RUNPATH; otherwise - * in DT_RPATH. -rpath-link is link-time-only and is forwarded as - * runpaths so the library has a record of the search paths used - * (advisory; ELF runtime ignores DT_RUNPATH entries it didn't - * write itself, so this matches GNU-ld behaviour where rpath-link - * does not appear in DT_*PATH). */ - if (o->new_dtags) { - lopts.runpaths = rpath_slices; - lopts.nrunpaths = o->nrpaths; - } else { - lopts.rpaths = rpath_slices; - lopts.nrpaths = o->nrpaths; - } - /* Executable links are strict by default; --allow-undefined is the - * explicit escape hatch for freestanding bring-up and diagnostics. */ - lopts.allow_undefined = o->allow_undefined ? 1 : 0; /* Strict freestanding policy: a `*-none-*` (FREESTANDING) static non-PIE * executable must reject dynamic-link artifacts and cross-input * target/format mismatches. Only the EXE lane imposes it; -r/-shared do * not. --allow-undefined still relaxes undef references but not these - * structural rejections. */ + * structural rejections. Set after fill_options, which derives lopts.pie. */ if (lopts.output_kind == KIT_LINK_OUTPUT_EXE && !lopts.pie && o->target.os == KIT_OS_FREESTANDING) lopts.freestanding_strict = true; (void)o->export_dynamic; st = kit_link_session_new(compiler, &lopts, &link); - if (o->order && o->norder) { - for (i = 0; i < o->norder && st == KIT_OK; ++i) { - const KitLinkInputOrder* ord = &o->order[i]; - switch ((KitLinkInputOrderKind)ord->kind) { - case KIT_LINK_INPUT_OBJ: - case KIT_LINK_INPUT_OBJ_BYTES: - st = kit_link_session_add_obj_bytes( - link, kit_slice_cstr(o->object_files[ord->index]), - &obj_in[ord->index]); - break; - case KIT_LINK_INPUT_ARCHIVE: - st = kit_link_session_add_archive_bytes(link, &arch_in[ord->index]); - break; - case KIT_LINK_INPUT_DSO: - st = kit_link_session_add_dso_bytes( - link, kit_slice_cstr(o->dsos[ord->index].path), - &dso_in[ord->index]); - break; - } + for (i = 0; i < norder && st == KIT_OK; ++i) { + const KitLinkInputOrder* ord = &order[i]; + switch ((KitLinkInputOrderKind)ord->kind) { + case KIT_LINK_INPUT_OBJ: + case KIT_LINK_INPUT_OBJ_BYTES: + st = kit_link_session_add_obj_bytes( + link, kit_slice_cstr(o->inputs.object_files[ord->index]), + &obj_in[ord->index]); + break; + case KIT_LINK_INPUT_ARCHIVE: + st = kit_link_session_add_archive_bytes(link, &arch_in[ord->index]); + break; + case KIT_LINK_INPUT_DSO: + st = kit_link_session_add_dso_bytes( + link, kit_slice_cstr(o->inputs.dsos[ord->index].path), + &dso_in[ord->index]); + break; } - } else { - for (i = 0; i < o->nobject_files && st == KIT_OK; ++i) - st = kit_link_session_add_obj_bytes( - link, kit_slice_cstr(o->object_files[i]), &obj_in[i]); - for (i = 0; i < o->narchives && st == KIT_OK; ++i) - st = kit_link_session_add_archive_bytes(link, &arch_in[i]); - for (i = 0; i < o->ndsos && st == KIT_OK; ++i) - st = kit_link_session_add_dso_bytes( - link, kit_slice_cstr(o->dsos[i].path), &dso_in[i]); } if (st == KIT_OK) st = kit_link_session_emit(link, writer); - if (st == KIT_OK) st = ld_write_link_report(o, io, link, o->map_path, 0); + if (st == KIT_OK) st = ld_write_link_report(o, io, link, o->lf.map_path, 0); if (st == KIT_OK) - st = ld_write_link_report(o, io, link, o->symbols_path, 1); + st = ld_write_link_report(o, io, link, o->lf.symbols_path, 1); if (st == KIT_OK) st = ld_write_cref(o, io, link); if (st == KIT_OK) st = ld_print_memory_usage(o, link); rc = st == KIT_OK ? 0 : 1; + driver_link_flags_free_rpath_slices(&o->lf, rpath_slices); } out: @@ -3698,15 +3220,19 @@ out: driver_runtime_archive_fini(o->env, &rt_archive); if (runtime_resolved) driver_runtime_support_fini(o->env, &runtime); release_file(&script_lf); - release_all(arch_lf, o->narchives); - release_all(obj_lf, o->nobject_files); - release_all(dso_lf, o->ndsos); - if (arch_in) driver_free(o->env, arch_in, o->narchives * sizeof(*arch_in)); - if (arch_lf) driver_free(o->env, arch_lf, o->narchives * sizeof(*arch_lf)); + release_all(arch_lf, o->inputs.narchives); + release_all(obj_lf, o->inputs.nobject_files); + release_all(dso_lf, o->inputs.ndsos); + if (arch_in) + driver_free(o->env, arch_in, o->inputs.narchives * sizeof(*arch_in)); + if (arch_lf) + driver_free(o->env, arch_lf, o->inputs.narchives * sizeof(*arch_lf)); if (obj_in) driver_free(o->env, obj_in, o->argv_bound * sizeof(*obj_in)); if (obj_lf) driver_free(o->env, obj_lf, o->argv_bound * sizeof(*obj_lf)); - if (dso_in) driver_free(o->env, dso_in, o->ndsos * sizeof(*dso_in)); - if (dso_lf) driver_free(o->env, dso_lf, o->ndsos * sizeof(*dso_lf)); + if (dso_in) driver_free(o->env, dso_in, o->inputs.ndsos * sizeof(*dso_in)); + if (dso_lf) driver_free(o->env, dso_lf, o->inputs.ndsos * sizeof(*dso_lf)); + if (order) + driver_free(o->env, order, o->inputs.nlink_items * sizeof(*order)); return rc; }