commit 4595b0529150f7885e25e7231e0a0d1196c440fd
parent ef340b4cc5bea1a6a715058fc4d15559549af74c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 16 Jun 2026 14:41:22 -0700
driver: wire kernel linker flags through cc/build-exe + ld
Route the new link policy through driver/lib/link_flags.* (shared by cc and
build-exe) and mirror it in driver/cmd/ld.c:
- --defsym NAME=EXPR (literal or alias), --section-start=.NAME=ADDR, and
-Tdata/-Tbss (folded into section_starts as .data/.bss; the build-exe
rejection is replaced). Both top-level and -Wl,/-Xlinker spellings.
- --orphan-handling=place|warn|error|discard, --fatal-warnings, --cref FILE,
--print-memory-usage. cref writes a side file; memory-usage prints to stdout.
- Freestanding strict policy: build-exe/ld set freestanding_strict for a
*-none-* (or -ffreestanding static non-PIE) EXE link, and reject foreign-arch
/ wrong-format object inputs up front via per-input kit_detect_target.
Diffstat:
4 files changed, 926 insertions(+), 12 deletions(-)
diff --git a/driver/cmd/build.c b/driver/cmd/build.c
@@ -2,6 +2,7 @@
#include <kit/compile.h>
#include <kit/core.h>
#include <kit/link.h>
+#include <kit/object.h>
#include <kit/preprocess.h>
#include <stdint.h>
#include <string.h>
@@ -729,11 +730,101 @@ static int build_parse(int argc, char** argv, BuildOptions* o) {
if (driver_link_flags_record_text_base(&o->link, a + 7) != 0) return 1;
continue;
}
- if (driver_streq(a, "-Tdata") || driver_strneq(a, "-Tdata=", 7) ||
- driver_streq(a, "-Tbss") || driver_strneq(a, "-Tbss=", 6)) {
- driver_errf(o->tool, "%.*s is not supported yet",
- KIT_SLICE_ARG(kit_slice_cstr(a)));
- return 1;
+ if (driver_streq(a, "-Tdata") || driver_streq(a, "-Tbss")) {
+ const char* secname = driver_streq(a, "-Tdata") ? ".data" : ".bss";
+ if (++i >= argc) {
+ driver_errf(o->tool, "%s requires an address", a);
+ return 1;
+ }
+ if (driver_link_flags_record_section_addr(&o->link, secname, argv[i]) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "-Tdata=", 7)) {
+ if (driver_link_flags_record_section_addr(&o->link, ".data", a + 7) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "-Tbss=", 6)) {
+ if (driver_link_flags_record_section_addr(&o->link, ".bss", a + 6) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "--defsym")) {
+ if (++i >= argc) {
+ driver_errf(o->tool, "--defsym requires NAME=EXPR");
+ return 1;
+ }
+ if (driver_link_flags_record_defsym(&o->link, argv[i],
+ driver_strlen(argv[i])) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "--defsym=", 9)) {
+ if (driver_link_flags_record_defsym(&o->link, a + 9,
+ driver_strlen(a + 9)) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "--section-start=", 16)) {
+ if (driver_link_flags_record_section_start(&o->link, a + 16,
+ driver_strlen(a + 16)) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "--section-start")) {
+ if (++i >= argc) {
+ driver_errf(o->tool, "--section-start requires .NAME=ADDR");
+ return 1;
+ }
+ if (driver_link_flags_record_section_start(&o->link, argv[i],
+ driver_strlen(argv[i])) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "--orphan-handling=", 18)) {
+ if (driver_link_flags_record_orphan_handling(
+ &o->link, a + 18, driver_strlen(a + 18)) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "--orphan-handling")) {
+ if (++i >= argc) {
+ driver_errf(o->tool, "--orphan-handling requires a mode");
+ return 1;
+ }
+ if (driver_link_flags_record_orphan_handling(
+ &o->link, argv[i], driver_strlen(argv[i])) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_streq(a, "--fatal-warnings")) {
+ o->link.fatal_warnings = 1;
+ continue;
+ }
+ if (driver_streq(a, "--no-fatal-warnings")) {
+ o->link.fatal_warnings = 0;
+ continue;
+ }
+ if (driver_streq(a, "--print-memory-usage")) {
+ o->link.print_memory_usage = 1;
+ continue;
+ }
+ if (driver_streq(a, "--cref")) {
+ if (++i >= argc) {
+ driver_errf(o->tool, "--cref requires a path");
+ return 1;
+ }
+ if (driver_link_flags_record_cref(&o->link, argv[i],
+ driver_strlen(argv[i])) != 0)
+ return 1;
+ continue;
+ }
+ if (driver_strneq(a, "--cref=", 7)) {
+ if (driver_link_flags_record_cref(&o->link, a + 7,
+ driver_strlen(a + 7)) != 0)
+ return 1;
+ continue;
}
if (driver_streq(a, "--gc-sections")) {
o->link.gc_sections = 1;
@@ -1437,6 +1528,37 @@ static KitStatus build_link_with_lto_reports(
if (st == KIT_OK &&
build_write_link_report(o, ctx, link, o->link.symbols_path, 1) != 0)
st = KIT_ERR;
+ /* --cref FILE: cross-reference table side file. */
+ if (st == KIT_OK && o->link.cref_path) {
+ KitWriter* w = NULL;
+ if (build_open_output(ctx, o->env, o->tool, o->link.cref_path, &w) != 0) {
+ st = KIT_ERR;
+ } else {
+ KitStatus cst = kit_link_session_write_cref(link, w);
+ if (cst == KIT_OK) cst = kit_writer_status(w);
+ kit_writer_close(w);
+ if (cst != KIT_OK) {
+ driver_errf(o->tool, "failed to write cref: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(o->link.cref_path)));
+ st = KIT_ERR;
+ }
+ }
+ }
+ /* --print-memory-usage: GNU-ld-style per-region summary to stdout. */
+ if (st == KIT_OK && o->link.print_memory_usage) {
+ KitWriter* w = NULL;
+ if (build_open_output(ctx, o->env, o->tool, "-", &w) != 0) {
+ st = KIT_ERR;
+ } else {
+ KitStatus mst = kit_link_session_write_memory_usage(link, w);
+ if (mst == KIT_OK) mst = kit_writer_status(w);
+ kit_writer_close(w);
+ if (mst != KIT_OK) {
+ driver_errf(o->tool, "failed to print memory usage");
+ st = KIT_ERR;
+ }
+ }
+ }
kit_link_session_free(link);
if (preserved.syms)
preserved.heap->free(preserved.heap, preserved.syms,
@@ -1515,6 +1637,24 @@ static int build_run_link(BuildOptions* o, KitCompiler* compiler,
goto out;
obj_names[i] = kit_slice_cstr(o->inputs.object_files[i]);
}
+ /* Strict freestanding policy: pre-built object inputs must agree with the
+ * link target's arch and object format (a `*-none-*` exe link must not
+ * silently accept a foreign-arch object). */
+ if (output_kind == KIT_LINK_OUTPUT_EXE &&
+ o->target.os == KIT_OS_FREESTANDING) {
+ for (i = 0; i < o->inputs.nobject_files; ++i) {
+ KitTargetSpec t;
+ if (kit_detect_target(obj_in[i].data, obj_in[i].len, &t) != KIT_OK)
+ continue;
+ if (t.arch != o->target.arch || t.obj != o->target.obj) {
+ driver_errf(o->tool,
+ "freestanding link: input '%s' arch/format does not match "
+ "the link target",
+ o->inputs.object_files[i]);
+ goto out;
+ }
+ }
+ }
for (i = 0; i < o->inputs.narchives; ++i) {
if (driver_load_bytes(io, o->tool, o->inputs.archives[i].path, &arch_lf[i],
&arch_in[i].bytes) != 0)
@@ -1577,6 +1717,15 @@ static int build_run_link(BuildOptions* o, KitCompiler* compiler,
output_kind == KIT_LINK_OUTPUT_RELOCATABLE, output_kind, script,
&lopts, &rpath_slices) != 0)
goto out;
+ /* Strict-by-default freestanding policy: a `*-none-*` target, or an
+ * explicit -ffreestanding static non-PIE executable, must reject
+ * dynamic-link artifacts (PT_INTERP, .dynamic/.dynsym, PLT/GOT imports,
+ * DSO inputs) and cross-input target/format mismatches. The relocatable
+ * partial-link lane never imposes it. */
+ if (output_kind == KIT_LINK_OUTPUT_EXE &&
+ (o->target.os == KIT_OS_FREESTANDING ||
+ (o->freestanding && !lopts.pie && o->static_link)))
+ lopts.freestanding_strict = true;
memset(&li, 0, sizeof(li));
li.objs = objs;
li.nobjs = nobjs;
diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c
@@ -139,6 +139,18 @@ typedef struct LdOptions {
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 */
@@ -812,6 +824,140 @@ static int ld_record_symbols_format(LdOptions* o, const char* val, size_t n) {
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;
+}
+
+/* --section-start: store a NAME -> ADDR override (NAME may be ".data"/".bss"
+ * synthesized from -Tdata/-Tbss). */
+static int ld_record_section_addr(LdOptions* o, const char* secname,
+ const char* addr) {
+ uint64_t v;
+ const char* name_owned;
+ KitLinkSectionStart* ss;
+ if (!secname || !*secname || !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;
+ }
+ if (ld_span_to_owned_cstr(o, secname, driver_strlen(secname), &name_owned) !=
+ 0)
+ return 1;
+ 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;
+}
+
+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;
+ }
+ 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_record_section_addr(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;
@@ -821,6 +967,10 @@ static int ld_parse_wl(LdOptions* o, const char* arg) {
int expect_emulation = 0;
int expect_map = 0;
int expect_symbols = 0;
+ int expect_defsym = 0;
+ int expect_section_start = 0;
+ int expect_cref = 0;
+ int expect_orphan = 0;
while (*p) {
const char* tok = p;
size_t n = 0;
@@ -828,8 +978,23 @@ static int ld_parse_wl(LdOptions* o, const char* arg) {
p = tok + n + (tok[n] == ',' ? 1u : 0u);
if (expect_rpath || expect_soname || expect_interp || expect_z ||
- expect_emulation || expect_map || expect_symbols) {
- if (expect_rpath) {
+ expect_emulation || expect_map || expect_symbols || expect_defsym ||
+ expect_section_start || expect_cref || expect_orphan) {
+ if (expect_defsym) {
+ if (ld_record_defsym(o, tok, n) != 0) return 1;
+ } else if (expect_section_start) {
+ if (ld_record_section_start(o, tok, n) != 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;
+ } else if (expect_orphan) {
+ if (ld_record_orphan_handling(o, 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;
@@ -865,7 +1030,8 @@ static int ld_parse_wl(LdOptions* o, const char* arg) {
o->symbols_path = owned;
}
expect_rpath = expect_soname = expect_interp = expect_z =
- expect_emulation = expect_map = expect_symbols = 0;
+ expect_emulation = expect_map = expect_symbols = expect_defsym =
+ expect_section_start = expect_cref = expect_orphan = 0;
continue;
}
@@ -1032,12 +1198,87 @@ static int ld_parse_wl(LdOptions* o, const char* arg) {
o->interp_path = owned;
continue;
}
+ /* -Tdata / -Tbss section base overrides. */
+ if (ld_tok_eq(tok, n, "-Tdata") || ld_tok_eq(tok, n, "-Tbss")) {
+ const char* secname = ld_tok_eq(tok, n, "-Tdata") ? ".data" : ".bss";
+ const char* atok = p;
+ size_t an = 0;
+ const char* addr_owned;
+ if (!*atok) {
+ driver_errf(LD_TOOL, "%.*s requires an address", (int)n, tok);
+ return 1;
+ }
+ 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;
+ 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;
+ 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;
+ continue;
+ }
+ if (ld_tok_prefix(tok, n, "--defsym=")) {
+ if (ld_record_defsym(o, tok + 9, n - 9u) != 0) return 1;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--defsym")) {
+ expect_defsym = 1;
+ continue;
+ }
+ if (ld_tok_prefix(tok, n, "--section-start=")) {
+ if (ld_record_section_start(o, tok + 16, n - 16u) != 0) return 1;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--section-start")) {
+ expect_section_start = 1;
+ 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;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--cref")) {
+ expect_cref = 1;
+ continue;
+ }
+ if (ld_tok_prefix(tok, n, "--orphan-handling=")) {
+ if (ld_record_orphan_handling(o, tok + 18, n - 18u) != 0) return 1;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--orphan-handling")) {
+ expect_orphan = 1;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--fatal-warnings")) {
+ o->fatal_warnings = 1;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--no-fatal-warnings")) {
+ o->fatal_warnings = 0;
+ continue;
+ }
+ if (ld_tok_eq(tok, n, "--print-memory-usage")) {
+ o->print_memory_usage = 1;
+ continue;
+ }
driver_errf(LD_TOOL, "unsupported -Wl, token: %.*s", (int)n, tok);
return 1;
}
if (expect_rpath || expect_soname || expect_interp || expect_z ||
- expect_emulation || expect_map || expect_symbols) {
+ expect_emulation || expect_map || expect_symbols || expect_defsym ||
+ expect_section_start || expect_cref || expect_orphan) {
driver_errf(LD_TOOL, "-Wl option requires another comma argument");
return 1;
}
@@ -2108,6 +2349,99 @@ static int ld_parse(int argc, char** argv, LdOptions* o) {
continue;
}
}
+ /* -Tdata / -Tbss ADDR (or =ADDR): per-section address override. Folded
+ * into the section_starts table as `.data` / `.bss`. */
+ {
+ const char* secname = NULL;
+ const char* addr = NULL;
+ if (driver_streq(a, "-Tdata") || driver_streq(a, "-Tbss")) {
+ secname = driver_streq(a, "-Tdata") ? ".data" : ".bss";
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "%s requires an address", a);
+ return 1;
+ }
+ addr = argv[i];
+ } else if ((addr = arg_eq_value(a, "-Tdata")) != NULL) {
+ secname = ".data";
+ } else if ((addr = arg_eq_value(a, "-Tbss")) != NULL) {
+ secname = ".bss";
+ }
+ if (secname) {
+ if (ld_record_section_addr(o, secname, addr) != 0) return 1;
+ continue;
+ }
+ }
+ if (driver_streq(a, "--defsym")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--defsym requires NAME=EXPR");
+ return 1;
+ }
+ if (ld_record_defsym(o, 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;
+ continue;
+ }
+ if (driver_streq(a, "--section-start")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR");
+ return 1;
+ }
+ if (ld_record_section_start(o, 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;
+ continue;
+ }
+ if (driver_streq(a, "--orphan-handling")) {
+ if (++i >= argc) {
+ driver_errf(LD_TOOL, "--orphan-handling requires a mode");
+ return 1;
+ }
+ if (ld_record_orphan_handling(o, 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;
+ continue;
+ }
+ if (driver_streq(a, "--fatal-warnings")) {
+ o->fatal_warnings = 1;
+ continue;
+ }
+ if (driver_streq(a, "--no-fatal-warnings")) {
+ o->fatal_warnings = 0;
+ continue;
+ }
+ if (driver_streq(a, "--print-memory-usage")) {
+ o->print_memory_usage = 1;
+ continue;
+ }
+ if (driver_streq(a, "--cref")) {
+ if (++i >= argc) {
+ 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;
+ }
+ 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)
+ return 1;
+ o->cref_path = owned;
+ continue;
+ }
/* -nostdlib / --no-default-libs: do not auto-resolve and link kit's
* compiler runtime. A freestanding image (e.g. riscv32-none-elf) supplies
* its own libkit_rt.a on the command line, and a pure-linker invocation
@@ -2654,6 +2988,11 @@ static void ld_options_release(LdOptions* o) {
bound * 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 ---------- */
@@ -2712,6 +3051,43 @@ static KitStatus ld_write_link_report(const LdOptions* o, const KitFileIO* io,
return st;
}
+/* --cref FILE: cross-reference table side file. */
+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) {
+ driver_errf(LD_TOOL, "failed to open cref output: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(o->cref_path)));
+ return KIT_IO;
+ }
+ st = kit_link_session_write_cref(link, w);
+ if (st == KIT_OK) st = kit_writer_status(w);
+ 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)));
+ return st;
+}
+
+/* --print-memory-usage: GNU-ld-style per-region summary to stdout. */
+static KitStatus ld_print_memory_usage(LdOptions* o, KitLinkSession* link) {
+ KitWriter* w;
+ KitStatus st;
+ if (!o->print_memory_usage) return KIT_OK;
+ w = driver_stdout_writer(o->env);
+ if (!w) {
+ driver_errf(LD_TOOL, "failed to print memory usage");
+ return KIT_IO;
+ }
+ st = kit_link_session_write_memory_usage(link, w);
+ if (st == KIT_OK) st = kit_writer_status(w);
+ kit_writer_close(w);
+ if (st != KIT_OK) driver_errf(LD_TOOL, "failed to print memory usage");
+ return st;
+}
+
static int ld_append_hosted_input(LdOptions* o, const DriverHostedInput* in,
uint32_t insert_pos, int insert) {
switch ((DriverHostedInputKind)in->kind) {
@@ -2895,6 +3271,27 @@ static int ld_run_link(LdOptions* o) {
goto out;
}
+ /* Strict freestanding policy: every input object must agree with the link
+ * target's arch and object format. A `*-none-*` link silently mis-linking a
+ * foreign-arch object (the default detection trusts obj[0]'s arch) is a hard
+ * error here. */
+ if (!o->relocatable && o->target.os == KIT_OS_FREESTANDING) {
+ uint32_t oi;
+ for (oi = 0; oi < initial_nobject_files; ++oi) {
+ KitTargetSpec t;
+ if (kit_detect_target(obj_lf[oi].data.data, obj_lf[oi].data.size, &t) !=
+ KIT_OK)
+ continue;
+ if (t.arch != o->target.arch || t.obj != o->target.obj) {
+ driver_errf(LD_TOOL,
+ "freestanding link: input '%s' arch/format does not match "
+ "the link target",
+ o->object_files[oi]);
+ goto out;
+ }
+ }
+ }
+
if (ld_apply_hosted_before_after(o) != 0) goto out;
/* Hosted dynamic link: default to PIE, matching what `kit cc` does.
@@ -3061,6 +3458,12 @@ static int ld_run_link(LdOptions* o) {
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;
@@ -3097,6 +3500,14 @@ static int ld_run_link(LdOptions* o) {
/* 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. */
+ 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);
@@ -3134,6 +3545,8 @@ static int ld_run_link(LdOptions* o) {
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->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;
}
diff --git a/driver/lib/link_flags.c b/driver/lib/link_flags.c
@@ -241,6 +241,209 @@ int driver_link_flags_record_text_base(DriverLinkFlags* lf, const char* val) {
return 0;
}
+/* Is this span a numeric (hex/dec) literal, or a symbol name? Used to decide
+ * the --defsym RHS form. A literal starts with a digit (0x.. or decimal). */
+static int lf_span_is_number(const char* s, size_t n) {
+ if (n == 0) return 0;
+ return s[0] >= '0' && s[0] <= '9';
+}
+
+/* Parse a possibly-NUL-unterminated numeric span as u64. */
+static int lf_parse_u64_span(DriverLinkFlags* lf, const char* s, size_t n,
+ uint64_t* out) {
+ char* buf = lf_dup_owned(lf, s, n);
+ if (!buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ return driver_parse_u64(buf, out);
+}
+
+static int lf_grow_defsyms(DriverLinkFlags* lf) {
+ uint32_t old_cap = lf->cap_defsyms;
+ uint32_t new_cap = old_cap ? old_cap * 2u : 4u;
+ KitLinkDefsym* nd;
+ if (new_cap <= old_cap) return 1;
+ nd = (KitLinkDefsym*)driver_alloc_zeroed(lf->env,
+ (size_t)new_cap * sizeof(*nd));
+ if (!nd) return 1;
+ if (lf->defsyms) {
+ driver_memcpy(nd, lf->defsyms, (size_t)lf->ndefsyms * sizeof(*lf->defsyms));
+ driver_free(lf->env, lf->defsyms, (size_t)old_cap * sizeof(*lf->defsyms));
+ }
+ lf->defsyms = nd;
+ lf->cap_defsyms = new_cap;
+ return 0;
+}
+
+static int lf_grow_section_starts(DriverLinkFlags* lf) {
+ uint32_t old_cap = lf->cap_section_starts;
+ uint32_t new_cap = old_cap ? old_cap * 2u : 4u;
+ KitLinkSectionStart* ns;
+ if (new_cap <= old_cap) return 1;
+ ns = (KitLinkSectionStart*)driver_alloc_zeroed(
+ lf->env, (size_t)new_cap * sizeof(*ns));
+ if (!ns) return 1;
+ if (lf->section_starts) {
+ driver_memcpy(ns, lf->section_starts,
+ (size_t)lf->nsection_starts * sizeof(*lf->section_starts));
+ driver_free(lf->env, lf->section_starts,
+ (size_t)old_cap * sizeof(*lf->section_starts));
+ }
+ lf->section_starts = ns;
+ lf->cap_section_starts = new_cap;
+ return 0;
+}
+
+int driver_link_flags_record_defsym(DriverLinkFlags* lf, const char* val,
+ size_t n) {
+ size_t eq = 0;
+ const char* name;
+ size_t name_len;
+ const char* rhs;
+ size_t rhs_len;
+ KitLinkDefsym* d;
+ char* name_buf;
+ while (eq < n && val[eq] != '=') ++eq;
+ if (eq == 0 || eq >= n) {
+ driver_errf(lf->tool, "--defsym requires NAME=EXPR: %.*s", (int)n, val);
+ return 1;
+ }
+ name = val;
+ name_len = eq;
+ rhs = val + eq + 1u;
+ rhs_len = n - eq - 1u;
+ if (rhs_len == 0) {
+ driver_errf(lf->tool, "--defsym requires a value: %.*s", (int)n, val);
+ return 1;
+ }
+ if (lf->ndefsyms >= lf->cap_defsyms && lf_grow_defsyms(lf) != 0) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ name_buf = lf_dup_owned(lf, name, name_len);
+ if (!name_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ d = &lf->defsyms[lf->ndefsyms];
+ memset(d, 0, sizeof(*d));
+ d->name = kit_slice_cstr(name_buf);
+ if (lf_span_is_number(rhs, rhs_len)) {
+ uint64_t v;
+ if (lf_parse_u64_span(lf, rhs, rhs_len, &v) != 0) {
+ driver_errf(lf->tool, "--defsym: invalid value: %.*s", (int)rhs_len, rhs);
+ return 1;
+ }
+ d->value = v;
+ } else {
+ char* alias_buf = lf_dup_owned(lf, rhs, rhs_len);
+ if (!alias_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ d->alias = kit_slice_cstr(alias_buf);
+ d->value = 0;
+ }
+ lf->ndefsyms++;
+ return 0;
+}
+
+int driver_link_flags_record_section_addr(DriverLinkFlags* lf,
+ const char* secname,
+ const char* addr) {
+ uint64_t v;
+ KitLinkSectionStart* ss;
+ char* name_buf;
+ if (!secname || !*secname || !addr || !*addr) {
+ driver_errf(lf->tool, "--section-start requires .NAME=ADDR");
+ return 1;
+ }
+ if (driver_parse_u64(addr, &v) != 0) {
+ driver_errf(lf->tool, "--section-start: invalid address: %s", addr);
+ return 1;
+ }
+ if (lf->nsection_starts >= lf->cap_section_starts &&
+ lf_grow_section_starts(lf) != 0) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ name_buf = lf_dup_owned(lf, secname, driver_strlen(secname));
+ if (!name_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ ss = &lf->section_starts[lf->nsection_starts];
+ memset(ss, 0, sizeof(*ss));
+ ss->name = kit_slice_cstr(name_buf);
+ ss->addr = v;
+ lf->nsection_starts++;
+ return 0;
+}
+
+int driver_link_flags_record_section_start(DriverLinkFlags* lf, const char* val,
+ size_t n) {
+ /* --section-start=.NAME=ADDR — split on the LAST '='. */
+ size_t eq = n;
+ size_t i;
+ char* name_buf;
+ char* addr_buf;
+ int rc;
+ for (i = 0; i < n; ++i)
+ if (val[i] == '=') eq = i;
+ if (eq == n || eq == 0) {
+ driver_errf(lf->tool, "--section-start requires .NAME=ADDR: %.*s", (int)n,
+ val);
+ return 1;
+ }
+ name_buf = lf_dup_owned(lf, val, eq);
+ addr_buf = lf_dup_owned(lf, val + eq + 1u, n - eq - 1u);
+ if (!name_buf || !addr_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ rc = driver_link_flags_record_section_addr(lf, name_buf, addr_buf);
+ return rc;
+}
+
+int driver_link_flags_record_orphan_handling(DriverLinkFlags* lf,
+ const char* val, size_t n) {
+ if (lf_tok_eq(val, n, "place")) {
+ lf->orphan_handling = KIT_LINK_ORPHAN_PLACE;
+ return 0;
+ }
+ if (lf_tok_eq(val, n, "warn")) {
+ lf->orphan_handling = KIT_LINK_ORPHAN_WARN;
+ return 0;
+ }
+ if (lf_tok_eq(val, n, "error")) {
+ lf->orphan_handling = KIT_LINK_ORPHAN_ERROR;
+ return 0;
+ }
+ if (lf_tok_eq(val, n, "discard")) {
+ lf->orphan_handling = KIT_LINK_ORPHAN_DISCARD;
+ return 0;
+ }
+ driver_errf(lf->tool, "unsupported --orphan-handling: %.*s", (int)n, val);
+ return 1;
+}
+
+int driver_link_flags_record_cref(DriverLinkFlags* lf, const char* val,
+ size_t n) {
+ char* buf;
+ if (!val || n == 0) {
+ driver_errf(lf->tool, "--cref requires a non-empty path");
+ return 1;
+ }
+ buf = lf_dup_owned(lf, val, n);
+ if (!buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ lf->cref_path = buf;
+ return 0;
+}
+
int driver_link_flags_record_z(DriverLinkFlags* lf, const char* val, size_t n) {
if (lf_tok_eq(val, n, "defs")) {
lf->allow_undefined = 0;
@@ -313,6 +516,12 @@ void driver_link_flags_fini(DriverLinkFlags* lf) {
driver_free(
lf->env, lf->owned_string_sizes,
(size_t)lf->cap_owned_strings * sizeof(*lf->owned_string_sizes));
+ if (lf->defsyms)
+ driver_free(lf->env, lf->defsyms,
+ (size_t)lf->cap_defsyms * sizeof(*lf->defsyms));
+ if (lf->section_starts)
+ driver_free(lf->env, lf->section_starts,
+ (size_t)lf->cap_section_starts * sizeof(*lf->section_starts));
memset(lf, 0, sizeof(*lf));
}
@@ -344,6 +553,10 @@ int driver_link_flags_record_wl(DriverLinkFlags* lf, const char* arg) {
int expect_map = 0;
int expect_symbols = 0;
int expect_ttext = 0;
+ int expect_defsym = 0;
+ int expect_section_start = 0;
+ int expect_cref = 0;
+ int expect_orphan = 0;
while (*p) {
const char* tok = p;
size_t n = 0;
@@ -351,7 +564,8 @@ int driver_link_flags_record_wl(DriverLinkFlags* lf, const char* arg) {
p = tok + n + (tok[n] == ',' ? 1u : 0u);
if (expect_rpath || expect_soname || expect_interp || expect_subsystem ||
- expect_z || expect_map || expect_symbols || expect_ttext) {
+ expect_z || expect_map || expect_symbols || expect_ttext ||
+ expect_defsym || expect_section_start || expect_cref || expect_orphan) {
int rc = 0;
if (expect_rpath)
rc = lf_record_rpath(lf, tok, n);
@@ -367,6 +581,14 @@ int driver_link_flags_record_wl(DriverLinkFlags* lf, const char* arg) {
rc = driver_link_flags_record_map(lf, tok, n);
else if (expect_symbols)
rc = driver_link_flags_record_symbols(lf, tok, n);
+ else if (expect_defsym)
+ rc = driver_link_flags_record_defsym(lf, tok, n);
+ else if (expect_section_start)
+ rc = driver_link_flags_record_section_start(lf, tok, n);
+ else if (expect_cref)
+ rc = driver_link_flags_record_cref(lf, tok, n);
+ else if (expect_orphan)
+ rc = driver_link_flags_record_orphan_handling(lf, tok, n);
else if (expect_ttext) {
char* buf = lf_dup_owned(lf, tok, n);
if (!buf) {
@@ -377,7 +599,9 @@ int driver_link_flags_record_wl(DriverLinkFlags* lf, const char* arg) {
}
if (rc != 0) return 1;
expect_rpath = expect_soname = expect_interp = expect_subsystem =
- expect_z = expect_map = expect_symbols = expect_ttext = 0;
+ expect_z = expect_map = expect_symbols = expect_ttext =
+ expect_defsym = expect_section_start = expect_cref =
+ expect_orphan = 0;
continue;
}
@@ -504,12 +728,108 @@ int driver_link_flags_record_wl(DriverLinkFlags* lf, const char* arg) {
return 1;
continue;
}
+ /* -Tdata / -Tbss: section base overrides, folded into section_starts. */
+ if (lf_tok_eq(tok, n, "-Tdata") || lf_tok_eq(tok, n, "-Tbss")) {
+ /* The next comma token is the address. Reuse the section-start consume
+ * via a synthesized ".data=ADDR" / ".bss=ADDR" on the following token;
+ * simplest is a dedicated tiny state, but section_start expects NAME=ADDR.
+ * Stash the chosen section name and read the address as a fresh token. */
+ const char* secname = lf_tok_eq(tok, n, "-Tdata") ? ".data" : ".bss";
+ const char* atok = p;
+ size_t an = 0;
+ char* addr_buf;
+ if (!*atok) {
+ driver_errf(lf->tool, "%.*s requires an address", (int)n, tok);
+ return 1;
+ }
+ while (atok[an] && atok[an] != ',') ++an;
+ p = atok + an + (atok[an] == ',' ? 1u : 0u);
+ addr_buf = lf_dup_owned(lf, atok, an);
+ if (!addr_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ if (driver_link_flags_record_section_addr(lf, secname, addr_buf) != 0)
+ return 1;
+ continue;
+ }
+ if (lf_tok_prefix(tok, n, "-Tdata=")) {
+ char* addr_buf = lf_dup_owned(lf, tok + 7, n - 7u);
+ if (!addr_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ if (driver_link_flags_record_section_addr(lf, ".data", addr_buf) != 0)
+ return 1;
+ continue;
+ }
+ if (lf_tok_prefix(tok, n, "-Tbss=")) {
+ char* addr_buf = lf_dup_owned(lf, tok + 6, n - 6u);
+ if (!addr_buf) {
+ driver_errf(lf->tool, "out of memory");
+ return 1;
+ }
+ if (driver_link_flags_record_section_addr(lf, ".bss", addr_buf) != 0)
+ return 1;
+ continue;
+ }
+ /* --defsym NAME=EXPR */
+ if (lf_tok_prefix(tok, n, "--defsym=")) {
+ if (driver_link_flags_record_defsym(lf, tok + 9, n - 9u) != 0) return 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--defsym")) {
+ expect_defsym = 1;
+ continue;
+ }
+ /* --section-start=.NAME=ADDR */
+ if (lf_tok_prefix(tok, n, "--section-start=")) {
+ if (driver_link_flags_record_section_start(lf, tok + 16, n - 16u) != 0)
+ return 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--section-start")) {
+ expect_section_start = 1;
+ continue;
+ }
+ /* --cref [FILE] */
+ if (lf_tok_prefix(tok, n, "--cref=")) {
+ if (driver_link_flags_record_cref(lf, tok + 7, n - 7u) != 0) return 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--cref")) {
+ expect_cref = 1;
+ continue;
+ }
+ /* --orphan-handling=MODE */
+ if (lf_tok_prefix(tok, n, "--orphan-handling=")) {
+ if (driver_link_flags_record_orphan_handling(lf, tok + 18, n - 18u) != 0)
+ return 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--orphan-handling")) {
+ expect_orphan = 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--fatal-warnings")) {
+ lf->fatal_warnings = 1;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--no-fatal-warnings")) {
+ lf->fatal_warnings = 0;
+ continue;
+ }
+ if (lf_tok_eq(tok, n, "--print-memory-usage")) {
+ lf->print_memory_usage = 1;
+ continue;
+ }
driver_errf(lf->tool, "unsupported -Wl, token: %.*s", (int)n, tok);
return 1;
}
if (expect_rpath || expect_soname || expect_interp || expect_subsystem ||
- expect_z || expect_map || expect_symbols || expect_ttext) {
+ expect_z || expect_map || expect_symbols || expect_ttext ||
+ expect_defsym || expect_section_start || expect_cref || expect_orphan) {
driver_errf(lf->tool, "-Wl option requires another comma argument");
return 1;
}
@@ -547,6 +867,12 @@ int driver_link_flags_fill_options(const DriverLinkFlags* lf,
lopts->strip_debug = lf->strip_debug != 0;
lopts->text_base_set = lf->text_base_set != 0;
lopts->text_base = lf->text_base;
+ lopts->defsyms = lf->defsyms;
+ lopts->ndefsyms = lf->ndefsyms;
+ lopts->section_starts = lf->section_starts;
+ lopts->nsection_starts = lf->nsection_starts;
+ lopts->orphan_handling = lf->orphan_handling;
+ lopts->fatal_warnings = lf->fatal_warnings != 0;
lopts->pie = driver_link_pie(target, explicit_pie, shared, relocatable) != 0;
lopts->pe_subsystem = lf->pe_subsystem;
lopts->interp_path = kit_slice_cstr(lf->interp_path);
diff --git a/driver/lib/link_flags.h b/driver/lib/link_flags.h
@@ -28,6 +28,19 @@ typedef struct DriverLinkFlags {
const char* map_path;
const char* symbols_path;
uint8_t symbols_format; /* KitLinkSymbolsFormat */
+ const char* cref_path; /* --cref FILE: cross-reference table */
+ int print_memory_usage; /* --print-memory-usage */
+ uint8_t orphan_handling; /* KitLinkOrphanHandling */
+ int fatal_warnings; /* --fatal-warnings */
+
+ /* --defsym NAME=EXPR. owned name/alias strings live in owned_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;
uint8_t build_id_mode; /* KitBuildIdMode */
uint8_t* build_id_bytes;
@@ -56,6 +69,19 @@ int driver_link_flags_record_symbols(DriverLinkFlags* lf, const char* val,
int driver_link_flags_record_symbols_format(DriverLinkFlags* lf,
const char* val, size_t n);
int driver_link_flags_record_text_base(DriverLinkFlags* lf, const char* val);
+/* --defsym NAME=EXPR (literal hex/dec or alias-symbol form). */
+int driver_link_flags_record_defsym(DriverLinkFlags* lf, const char* val,
+ size_t n);
+/* --section-start=.name=ADDR; also -Tdata/-Tbss via secname=".data"/".bss". */
+int driver_link_flags_record_section_start(DriverLinkFlags* lf, const char* val,
+ size_t n);
+int driver_link_flags_record_section_addr(DriverLinkFlags* lf,
+ const char* secname,
+ const char* addr);
+int driver_link_flags_record_orphan_handling(DriverLinkFlags* lf,
+ const char* val, size_t n);
+int driver_link_flags_record_cref(DriverLinkFlags* lf, const char* val,
+ size_t n);
int driver_link_flags_fill_options(const DriverLinkFlags* lf,
KitTargetSpec target, int explicit_pie,