kit

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

commit ba66f6d185d5821bdbd424aa95957d9af2fdfdca
parent 5e1e8ee566de76b632288cc242977c11e301a917
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 08:04:48 -0700

ld: embed DriverLinkInputSet (drop private input model)

Diffstat:
Mdriver/cmd/ld.c | 560++++++++++++++++++++++++++++++-------------------------------------------------
1 file changed, 214 insertions(+), 346 deletions(-)

diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -8,6 +8,7 @@ #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 @@ -57,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; @@ -104,31 +84,19 @@ typedef struct LdOptions { * driver_link_flags_fill_options. */ DriverLinkFlags lf; - const char** object_files; - uint32_t nobject_files; - - LdArchive* archives; - uint32_t narchives; + /* 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; - /* 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; - - 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 */ @@ -142,12 +110,13 @@ typedef struct LdOptions { 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; - /* 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. */ @@ -263,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->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->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; + } 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); @@ -344,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 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 void ld_insert_object(LdOptions* o, const char* path, uint32_t pos) { +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) { @@ -552,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; } @@ -667,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; @@ -699,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) { @@ -959,27 +890,27 @@ static int ld_parse_wl(LdOptions* o, const char* arg) { 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")) { @@ -1360,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; @@ -1387,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; } @@ -1408,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")) { @@ -1419,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; @@ -1431,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: @@ -1456,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) @@ -1595,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) { @@ -1641,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; @@ -1736,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, @@ -2349,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; @@ -2370,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) @@ -2401,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; @@ -2430,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; } @@ -2438,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; } @@ -2642,31 +2577,31 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { } 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; } @@ -2675,15 +2610,15 @@ 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; } @@ -2720,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; @@ -2763,7 +2699,7 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { 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; } @@ -2784,32 +2720,17 @@ 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]); } /* The shared flag model owns its FLAG strings (defsym/section/map/...), the - * build-id bytes, and the rpath/defsym/section-start vectors. */ + * 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)); @@ -2911,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->lf.interp_path && o->hosted.interp_path) - o->lf.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 ---------- */ @@ -3008,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; @@ -3026,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)); @@ -3036,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))); @@ -3088,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; @@ -3109,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 @@ -3153,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))); @@ -3171,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; @@ -3189,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))); @@ -3204,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))); @@ -3252,6 +3115,18 @@ static int ld_run_link(LdOptions* o) { goto out; } + /* 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"); @@ -3287,35 +3162,24 @@ static int ld_run_link(LdOptions* o) { (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->lf.map_path, 0); @@ -3346,15 +3210,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; }