kit

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

commit cb5b48ffb690877237353e7f96d468cd5a847ce1
parent 6b470e4919a9d24103069c8beeb741ac0bf9dc9d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 16 Jun 2026 14:21:31 -0700

link: honor inter-section dot assignments at textual position

Top-level assignments and output sections are stored in parallel arrays;
nothing recovered their textual order, so all '. = ...' assignments were
applied up-front rather than at their source position between two
sections. Add a shared monotonic seq stamped during SECTIONS{} parsing
(KitLinkAssignment.seq / KitLinkOutputSection.seq) and walk DOT
assignments in seq order via a forward cursor, applying those that
precede each output section just before it is laid out (trailing ones
after the last section). Non-DOT (symbol/PROVIDE) assignments keep their
post-layout pass.

Adds check_intersection_dot_align (red->green).

Diffstat:
Minclude/kit/link.h | 9+++++++++
Msrc/link/link_layout.c | 38+++++++++++++++++++++++++++++++++-----
Msrc/link/link_script.c | 17+++++++++++++++++
3 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/include/kit/link.h b/include/kit/link.h @@ -101,6 +101,11 @@ typedef struct KitLinkAssignment { uint8_t kind; /* KitLinkAsnKind */ KitSlice sym; const KitLinkExpr* expr; + /* Textual position within the enclosing SECTIONS{} body, shared with the + * KitLinkOutputSection.seq sequence so layout can interleave top-level + * assignments (e.g. `. = ALIGN(N)` between two sections) with output + * sections in source order. Only meaningful for top_asns; 0 otherwise. */ + uint32_t seq; } KitLinkAssignment; typedef struct KitLinkOutputSection { @@ -119,6 +124,10 @@ typedef struct KitLinkOutputSection { const KitLinkExpr* fill; const KitLinkExpr* flags; bool noload; + /* Textual position within the enclosing SECTIONS{} body (shared with + * KitLinkAssignment.seq); lets layout interleave inter-section dot + * assignments at their source position. */ + uint32_t seq; } KitLinkOutputSection; typedef struct KitLinkPhdr { diff --git a/src/link/link_layout.c b/src/link/link_layout.c @@ -957,6 +957,24 @@ static void apply_asn(Linker* l, LinkImage* img, u64* dot, } } +/* Apply every KIT_LAS_DOT top-level assignment whose textual `seq` is below + * `seq_limit` and that has not yet been applied (tracked by *cursor), so the + * location counter advances at the assignment's source position. top_asns is + * already in textual order, so a single forward cursor suffices. Non-DOT + * top-level assignments (symbol defs / PROVIDE) are applied in a later pass + * once all section addresses are known. */ +static void apply_dot_asns_before(Linker* l, LinkImage* img, u64* dot, + const ScriptOutInfo* outs, u32 nouts, + const KitLinkScript* script, u32* cursor, + u32 seq_limit) { + while (*cursor < script->ntop_asns && + script->top_asns[*cursor].seq < seq_limit) { + const KitLinkAssignment* a = &script->top_asns[*cursor]; + if (a->kind == KIT_LAS_DOT) apply_asn(l, img, dot, outs, nouts, a); + ++*cursor; + } +} + static const char* link_basename(const char* s) { const char* base = s; if (!s) return NULL; @@ -1096,11 +1114,13 @@ static void link_layout_sections_scripted(Linker* l, LinkImage* img, } } - for (k = 0; k < script->ntop_asns; ++k) { - const KitLinkAssignment* a = &script->top_asns[k]; - if (a->kind == KIT_LAS_DOT) - apply_asn(l, img, &dot, outs, script->nsections, a); - } + /* Inter-section dot assignments (`. = ALIGN(N)` / `. = expr` written + * between two output sections) must advance the location counter at their + * textual position. top_asns and sections each carry a shared `seq`; walk + * the DOT assignments in seq order, applying those that precede the next + * section right before that section is laid out. dot_cursor tracks the + * next unapplied top_asn. */ + u32 dot_cursor = 0; u32 nseg_max = 0; for (si = 0; si < script->nsections; ++si) @@ -1130,6 +1150,10 @@ static void link_layout_sections_scripted(Linker* l, LinkImage* img, for (si = 0; si < script->nsections; ++si) { const KitLinkOutputSection* os = &script->sections[si]; int is_discard = slice_eq_cstr(os->name, "/DISCARD/"); + /* Apply any inter-section dot assignments that textually precede this + * output section before placing it. */ + apply_dot_asns_before(l, img, &dot, outs, script->nsections, script, + &dot_cursor, os->seq); if (outs) outs[si].name = os->name; if (is_discard) { @@ -1431,6 +1455,10 @@ static void link_layout_sections_scripted(Linker* l, LinkImage* img, img->nsegments++; } + /* Apply any trailing dot assignments after the last output section. */ + apply_dot_asns_before(l, img, &dot, outs, script->nsections, script, + &dot_cursor, UINT32_MAX); + for (k = 0; k < script->ntop_asns; ++k) { const KitLinkAssignment* a = &script->top_asns[k]; if (a->kind != KIT_LAS_DOT) diff --git a/src/link/link_script.c b/src/link/link_script.c @@ -1126,11 +1126,28 @@ fail: /* ---- SECTIONS{...} ---- */ +/* Stamp a monotonic textual-order sequence onto any top_asns / sections + * pushed since the previous call. Top-level assignments and output sections + * share one counter (*seq) so layout can interleave them in source order; + * see KitLinkAssignment.seq / KitLinkOutputSection.seq. */ +static void stamp_seq(VecAsn* top_asns, u32* prev_as, VecSec* sections, + u32* prev_sec, u32* seq) { + while (*prev_as < top_asns->n) top_asns->p[(*prev_as)++].seq = (*seq)++; + while (*prev_sec < sections->n) sections->p[(*prev_sec)++].seq = (*seq)++; +} + static int parse_sections_block(LSP* p, VecAsn* top_asns, VecSec* sections, VecAssert* asserts) { + u32 seq = 0; + u32 prev_as = 0; + u32 prev_sec = 0; if (expect_ch(p, '{')) return 1; for (;;) { int ch; + /* Assign source-order seq to whatever the previous iteration pushed. + * (A single iteration appends at most one section but may append a + * burst of assignments via parse_assignment_call.) */ + stamp_seq(top_asns, &prev_as, sections, &prev_sec, &seq); skip_ws(p); if (p->err) return 1; ch = peek_ch(p);