kit

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

link_script.c (52291B)


      1 /* Linker-script parser: a minimal GNU-ld-subset front end that produces
      2  * the structured KitLinkScript form documented in <kit/link.h>. The
      3  * applicator (link_layout.c) consumes the structured form; this file
      4  * never speaks ELF or layout.
      5  *
      6  * Subset (driven by the kernel.lds at the head of doc/DESIGN.md §13):
      7  *   ENTRY(symbol)
      8  *   SECTIONS { ... }
      9  *     . = expr
     10  *     name = expr
     11  *     name : [ALIGN(N)] { body }
     12  *     /DISCARD/ : { body }
     13  *   body items: *(p1 p2 ...), name = expr, . = expr
     14  *   exprs: int literal (dec / 0x), `.`, ident, parens,
     15  *          + - * / & | ^ << >>,
     16  *          ALIGN(align)        (1-arg: aligns `.`, GNU form)
     17  *          ALIGN(val, align)   (2-arg: aligns an explicit expr)
     18  *   slash-star comments; whitespace insensitive.
     19  *
     20  * Anything else (MEMORY, PROVIDE, KEEP, AT>, > REGION, OVERLAY, INSERT,
     21  * OUTPUT_FORMAT, INPUT, GROUP, MAX, MIN, line comments, quoted strings,
     22  * file patterns other than the implicit `*` of `*(...)`) is a parse
     23  * error: emits a diagnostic and returns 1, leaving *out unchanged.
     24  *
     25  * Encoding contracts the applicator relies on:
     26  *   - /DISCARD/ is encoded as a KitLinkOutputSection with name
     27  *     "/DISCARD/" (a literal sentinel, not a parsed identifier).
     28  *   - An output section's `: ALIGN(N)` header is encoded as the first
     29  *     entry in its asns[]: a dot-assignment whose expr is ALIGN(., N).
     30  *   - `*(p1 p2 ...)` produces one KitLinkInputMatch per pattern with
     31  *     file_pattern = empty slice (implicit `*`) and section_pattern set.
     32  *     COMMON is parsed as a literal pattern "COMMON".
     33  *
     34  * Allocation: every node and string is owned by the compiler's tu arena.
     35  * kit_link_script_free is therefore a no-op — the arena outlives the
     36  * script and is collectively freed with the compiler. During parsing we
     37  * grow temporary arrays on the host heap, then arena-copy at finish.
     38  *
     39  * Diagnostics: SourceManager registration of a script buffer is a future
     40  * cleanup; for now diagnostics carry file_id = 0 and pack the byte
     41  * offset into the SrcLoc.line field (col is computed inline). */
     42 
     43 #include <kit/core.h>
     44 #include <kit/link.h>
     45 #include <stdarg.h>
     46 #include <stddef.h>
     47 #include <string.h>
     48 
     49 #include "core/arena.h"
     50 #include "core/core.h"
     51 #include "core/diag.h"
     52 #include "core/heap.h"
     53 #include "core/slice.h"
     54 #include "core/util.h"
     55 
     56 /* The public KitLinkScript has no place to carry its backing-arena
     57  * pointer, so we allocate a fixed-shape owner block via heap and arena-
     58  * init it inline. kit_link_script_free recovers the owner by stepping
     59  * back from the script field to the wrapping struct. The arena's first
     60  * member must match struct Arena (defined in core/arena.h). */
     61 typedef struct ScriptOwner {
     62   Arena arena;
     63   KitLinkScript script;
     64 } ScriptOwner;
     65 
     66 typedef struct LSP {
     67   Arena* arena;
     68   Heap* heap;
     69   KitDiagSink* diag;
     70   const char* src;
     71   size_t len;
     72   size_t pos;
     73   /* one-bit error sticky: any diagnostic flips this and the parser
     74    * unwinds without producing partial output. */
     75   int err;
     76   /* expression-parse recursion depth (parens / unary / binop RHS); guards the
     77    * descent from overflowing the C stack on pathological input. */
     78   int expr_depth;
     79 } LSP;
     80 
     81 /* Maximum expression nesting depth for both parse and eval. Past this we emit
     82  * a clean diagnostic instead of recursing into a stack overflow. */
     83 #define LSP_MAX_EXPR_DEPTH 256
     84 
     85 /* ---- diagnostics ---- */
     86 
     87 static SrcLoc lsp_loc(const LSP* p, size_t off) {
     88   /* TODO: register the script buffer with SourceManager so diagnostics
     89    * carry a real file_id; until then encode the byte offset as `line`
     90    * and recompute a 1-based line/col on demand. */
     91   SrcLoc l;
     92   size_t i, line = 1, col = 1;
     93   l.file_id = 0;
     94   for (i = 0; i < off && i < p->len; ++i) {
     95     if (p->src[i] == '\n') {
     96       ++line;
     97       col = 1;
     98     } else {
     99       ++col;
    100     }
    101   }
    102   l.line = (u32)line;
    103   l.col = (u32)col;
    104   return l;
    105 }
    106 
    107 static void lsp_errf(LSP* p, size_t off, const char* fmt, ...) {
    108   va_list ap;
    109   if (!p->diag) {
    110     p->err = 1;
    111     return;
    112   }
    113   va_start(ap, fmt);
    114   diag_emitv(p->diag, DIAG_ERROR, lsp_loc(p, off), fmt, ap);
    115   va_end(ap);
    116   p->err = 1;
    117 }
    118 
    119 /* ---- arena helpers ---- */
    120 
    121 static char* lsp_strdup(LSP* p, const char* s, size_t n) {
    122   return arena_strdup(p->arena, s, n);
    123 }
    124 
    125 /* Arena-copy a span of the script text and return a KitSlice over the
    126  * copy. The copy is NUL-terminated (arena_strdup), so consumers that hit
    127  * a host boundary can use .s directly. */
    128 static KitSlice lsp_slice(LSP* p, const char* s, size_t n) {
    129   KitSlice out;
    130   out.s = lsp_strdup(p, s, n);
    131   out.len = out.s ? n : 0;
    132   return out;
    133 }
    134 
    135 static KitLinkExpr* lsp_new_expr(LSP* p) {
    136   return arena_znew(p->arena, KitLinkExpr);
    137 }
    138 
    139 /* ---- heap-backed temp vectors (copied to the arena at finish) ---- */
    140 
    141 typedef struct VecAsn {
    142   KitLinkAssignment* p;
    143   u32 n, cap;
    144 } VecAsn;
    145 typedef struct VecMatch {
    146   KitLinkInputMatch* p;
    147   u32 n, cap;
    148 } VecMatch;
    149 typedef struct VecSec {
    150   KitLinkOutputSection* p;
    151   u32 n, cap;
    152 } VecSec;
    153 typedef struct VecRegion {
    154   KitLinkRegion* p;
    155   u32 n, cap;
    156 } VecRegion;
    157 typedef struct VecPhdr {
    158   KitLinkPhdr* p;
    159   u32 n, cap;
    160 } VecPhdr;
    161 typedef struct VecAssert {
    162   KitLinkAssertion* p;
    163   u32 n, cap;
    164 } VecAssert;
    165 typedef struct VecSlice {
    166   KitSlice* p;
    167   u32 n, cap;
    168 } VecSlice;
    169 
    170 static int vec_reserve_(LSP* p, void** ptr, u32* cap, u32 want, size_t es) {
    171   u32 nc;
    172   void* nb;
    173   if (*cap >= want) return 0;
    174   nc = *cap ? *cap * 2 : 8;
    175   while (nc < want) nc *= 2;
    176   nb = p->heap->realloc(p->heap, *ptr, (size_t)*cap * es, (size_t)nc * es,
    177                         sizeof(void*));
    178   if (!nb) return 1;
    179   *ptr = nb;
    180   *cap = nc;
    181   return 0;
    182 }
    183 
    184 #define VEC_PUSH(p, v, val)                                               \
    185   (vec_reserve_((p), (void**)&(v).p, &(v).cap, (v).n + 1, sizeof(*(v).p)) \
    186        ? 1                                                                \
    187        : ((v).p[(v).n++] = (val), 0))
    188 
    189 static void vec_free_(LSP* p, void* ptr, u32 cap, size_t es) {
    190   if (ptr) p->heap->free(p->heap, ptr, (size_t)cap * es);
    191 }
    192 
    193 /* ---- lex primitives ---- */
    194 
    195 static int is_id_start(int c) {
    196   return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ||
    197          c == '.';
    198 }
    199 static int is_id_cont(int c) {
    200   return is_id_start(c) || (c >= '0' && c <= '9') || c == '-';
    201 }
    202 
    203 static void skip_ws(LSP* p) {
    204   while (p->pos < p->len) {
    205     char ch = p->src[p->pos];
    206     if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
    207       ++p->pos;
    208       continue;
    209     }
    210     if (ch == '/' && p->pos + 1 < p->len && p->src[p->pos + 1] == '*') {
    211       size_t start = p->pos;
    212       p->pos += 2;
    213       while (p->pos + 1 < p->len &&
    214              !(p->src[p->pos] == '*' && p->src[p->pos + 1] == '/')) {
    215         ++p->pos;
    216       }
    217       if (p->pos + 1 >= p->len) {
    218         lsp_errf(p, start, "unterminated /* comment");
    219         return;
    220       }
    221       p->pos += 2;
    222       continue;
    223     }
    224     if (ch == '/' && p->pos + 1 < p->len && p->src[p->pos + 1] == '/') {
    225       lsp_errf(p, p->pos, "// line comments not supported");
    226       return;
    227     }
    228     break;
    229   }
    230 }
    231 
    232 static int peek_ch(LSP* p) {
    233   skip_ws(p);
    234   if (p->err) return -1;
    235   if (p->pos >= p->len) return -1;
    236   return (unsigned char)p->src[p->pos];
    237 }
    238 
    239 static int match_ch(LSP* p, char ch) {
    240   skip_ws(p);
    241   if (p->err) return 0;
    242   if (p->pos < p->len && p->src[p->pos] == ch) {
    243     ++p->pos;
    244     return 1;
    245   }
    246   return 0;
    247 }
    248 
    249 static int expect_ch(LSP* p, char ch) {
    250   if (match_ch(p, ch)) return 0;
    251   lsp_errf(p, p->pos, "expected '%c'", ch);
    252   return 1;
    253 }
    254 
    255 /* Lex an identifier-or-section-name token in place: returns a pointer
    256  * into p->src and length via *out_len. Section names like .text.* and
    257  * /DISCARD/ are handled by the section-name-aware variant below. */
    258 static int lex_ident(LSP* p, const char** out, size_t* out_len) {
    259   size_t start;
    260   skip_ws(p);
    261   if (p->err) return 1;
    262   if (p->pos >= p->len || !is_id_start((unsigned char)p->src[p->pos])) {
    263     lsp_errf(p, p->pos, "expected identifier");
    264     return 1;
    265   }
    266   start = p->pos;
    267   while (p->pos < p->len && is_id_cont((unsigned char)p->src[p->pos])) ++p->pos;
    268   *out = p->src + start;
    269   *out_len = p->pos - start;
    270   return 0;
    271 }
    272 
    273 static int lex_string(LSP* p, KitSlice* out) {
    274   size_t start;
    275   skip_ws(p);
    276   if (p->err) return 1;
    277   if (p->pos >= p->len || p->src[p->pos] != '"') {
    278     lsp_errf(p, p->pos, "expected string literal");
    279     return 1;
    280   }
    281   ++p->pos;
    282   start = p->pos;
    283   while (p->pos < p->len && p->src[p->pos] != '"') {
    284     if (p->src[p->pos] == '\\') {
    285       lsp_errf(p, p->pos, "escapes in linker-script strings not supported");
    286       return 1;
    287     }
    288     ++p->pos;
    289   }
    290   if (p->pos >= p->len) {
    291     lsp_errf(p, start, "unterminated string literal");
    292     return 1;
    293   }
    294   *out = lsp_slice(p, p->src + start, p->pos - start);
    295   ++p->pos;
    296   return out->s ? 0 : 1;
    297 }
    298 
    299 static int lex_ident_or_string(LSP* p, KitSlice* out) {
    300   int ch;
    301   skip_ws(p);
    302   if (p->err) return 1;
    303   ch = peek_ch(p);
    304   if (ch == '"') return lex_string(p, out);
    305   {
    306     const char* s;
    307     size_t n;
    308     if (lex_ident(p, &s, &n)) return 1;
    309     *out = lsp_slice(p, s, n);
    310     return out->s ? 0 : 1;
    311   }
    312 }
    313 
    314 /* Match a literal keyword. Caller must have already peeked. */
    315 static int match_kw(LSP* p, const char* kw) {
    316   size_t klen = slice_from_cstr(kw).len;
    317   size_t save;
    318   skip_ws(p);
    319   if (p->err) return 0;
    320   save = p->pos;
    321   if (p->pos + klen > p->len) return 0;
    322   if (memcmp(p->src + p->pos, kw, klen) != 0) return 0;
    323   /* must not glue to a following id-cont character */
    324   if (p->pos + klen < p->len &&
    325       is_id_cont((unsigned char)p->src[p->pos + klen]))
    326     return 0;
    327   p->pos += klen;
    328   (void)save;
    329   return 1;
    330 }
    331 
    332 /* ---- expression parser (precedence climbing) ----
    333  *
    334  * Levels (low -> high):
    335  *   0: |
    336  *   1: ^
    337  *   2: &
    338  *   3: << >>
    339  *   4: + -
    340  *   5: * /
    341  *   6: unary (none beyond parenthesized atoms here)
    342  *   atom: int | . | ALIGN(e,a) | ident | (expr)
    343  */
    344 
    345 static KitLinkExpr* parse_expr(LSP* p);
    346 static KitLinkExpr* parse_atom(LSP* p);
    347 
    348 static KitLinkExpr* parse_int(LSP* p) {
    349   KitLinkExpr* e;
    350   size_t start = p->pos;
    351   /* Accumulate unsigned: linker-script addresses routinely set the top bit
    352    * (e.g. 0xFFFFFFFF80000000 high-half kernels); a signed shift/multiply would
    353    * be UB there. The value is a bit pattern, reinterpreted into int_val below. */
    354   u64 v = 0;
    355   if (p->pos + 1 < p->len && p->src[p->pos] == '0' &&
    356       (p->src[p->pos + 1] == 'x' || p->src[p->pos + 1] == 'X')) {
    357     p->pos += 2;
    358     if (p->pos >= p->len) {
    359       lsp_errf(p, start, "malformed hex literal");
    360       return NULL;
    361     }
    362     while (p->pos < p->len) {
    363       char ch = p->src[p->pos];
    364       int d;
    365       if (ch >= '0' && ch <= '9')
    366         d = ch - '0';
    367       else if (ch >= 'a' && ch <= 'f')
    368         d = 10 + (ch - 'a');
    369       else if (ch >= 'A' && ch <= 'F')
    370         d = 10 + (ch - 'A');
    371       else
    372         break;
    373       v = (v << 4) | d;
    374       ++p->pos;
    375     }
    376     if (p->pos == start + 2) {
    377       lsp_errf(p, start, "empty hex literal");
    378       return NULL;
    379     }
    380   } else {
    381     while (p->pos < p->len && p->src[p->pos] >= '0' && p->src[p->pos] <= '9') {
    382       v = v * 10 + (p->src[p->pos] - '0');
    383       ++p->pos;
    384     }
    385     if (p->pos == start) {
    386       lsp_errf(p, start, "expected integer");
    387       return NULL;
    388     }
    389   }
    390   if (p->pos < p->len) {
    391     char suffix = p->src[p->pos];
    392     if (suffix == 'K' || suffix == 'k') {
    393       v *= 1024ull;
    394       ++p->pos;
    395     } else if (suffix == 'M' || suffix == 'm') {
    396       v *= 1024ull * 1024ull;
    397       ++p->pos;
    398     } else if (suffix == 'G' || suffix == 'g') {
    399       v *= 1024ull * 1024ull * 1024ull;
    400       ++p->pos;
    401     }
    402   }
    403   e = lsp_new_expr(p);
    404   if (!e) return NULL;
    405   e->kind = KIT_LE_INT;
    406   e->v.int_val = (i64)v;
    407   return e;
    408 }
    409 
    410 static KitLinkExpr* parse_atom_inner(LSP* p) {
    411   int ch;
    412   skip_ws(p);
    413   if (p->err) return NULL;
    414   ch = peek_ch(p);
    415   if (ch < 0) {
    416     lsp_errf(p, p->pos, "unexpected end of expression");
    417     return NULL;
    418   }
    419   if (ch == '-' || ch == '+') {
    420     int neg = (ch == '-');
    421     KitLinkExpr* val;
    422     KitLinkExpr* e;
    423     ++p->pos;
    424     val = parse_atom(p);
    425     if (!val) return NULL;
    426     if (!neg) return val;
    427     e = lsp_new_expr(p);
    428     if (!e) return NULL;
    429     e->kind = KIT_LE_NEG;
    430     e->v.align.val = val;
    431     e->v.align.align = NULL;
    432     return e;
    433   }
    434   if (ch == '(') {
    435     KitLinkExpr* e;
    436     ++p->pos;
    437     e = parse_expr(p);
    438     if (!e) return NULL;
    439     if (expect_ch(p, ')')) return NULL;
    440     return e;
    441   }
    442   if (ch == '.') {
    443     /* `.` only — bare dot, not a dotted ident. We disambiguate by
    444      * looking at the next char: a digit/letter/underscore/dot here is a
    445      * lex error in this subset (no .text in expression position). */
    446     size_t off = p->pos;
    447     ++p->pos;
    448     if (p->pos < p->len && is_id_cont((unsigned char)p->src[p->pos])) {
    449       lsp_errf(p, off, "dotted identifiers not allowed in expressions");
    450       return NULL;
    451     }
    452     {
    453       KitLinkExpr* e = lsp_new_expr(p);
    454       if (!e) return NULL;
    455       e->kind = KIT_LE_DOT;
    456       return e;
    457     }
    458   }
    459   if (ch >= '0' && ch <= '9') return parse_int(p);
    460   if (is_id_start(ch)) {
    461     /* either a built-in helper or a symbol reference */
    462     if (match_kw(p, "ALIGN") || match_kw(p, "BLOCK")) {
    463       /* Two forms, matching GNU ld:
    464        *   ALIGN(align)        — align the current location `.` (val defaults
    465        *                         to dot); the common `. = ALIGN(N)` idiom.
    466        *   ALIGN(val, align)   — align an explicit expression. */
    467       KitLinkExpr *val, *aln, *e;
    468       KitLinkExprKind kind =
    469           (memcmp(p->src + p->pos - 5, "BLOCK", 5) == 0) ? KIT_LE_BLOCK
    470                                                           : KIT_LE_ALIGN;
    471       if (expect_ch(p, '(')) return NULL;
    472       val = parse_expr(p);
    473       if (!val) return NULL;
    474       skip_ws(p);
    475       if (p->pos < p->len && p->src[p->pos] == ',') {
    476         ++p->pos;
    477         aln = parse_expr(p);
    478         if (!aln) return NULL;
    479       } else {
    480         /* 1-arg form: the parsed expr is the alignment; val is `.`. */
    481         aln = val;
    482         val = lsp_new_expr(p);
    483         if (!val) return NULL;
    484         val->kind = KIT_LE_DOT;
    485       }
    486       if (expect_ch(p, ')')) return NULL;
    487       e = lsp_new_expr(p);
    488       if (!e) return NULL;
    489       e->kind = (uint8_t)kind;
    490       e->v.align.val = val;
    491       e->v.align.align = aln;
    492       return e;
    493     }
    494     if (match_kw(p, "MAX") || match_kw(p, "MIN")) {
    495       KitLinkExprKind kind =
    496           (memcmp(p->src + p->pos - 3, "MIN", 3) == 0) ? KIT_LE_MIN
    497                                                         : KIT_LE_MAX;
    498       KitLinkExpr *lhs, *rhs, *e;
    499       if (expect_ch(p, '(')) return NULL;
    500       lhs = parse_expr(p);
    501       if (!lhs) return NULL;
    502       if (expect_ch(p, ',')) return NULL;
    503       rhs = parse_expr(p);
    504       if (!rhs) return NULL;
    505       if (expect_ch(p, ')')) return NULL;
    506       e = lsp_new_expr(p);
    507       if (!e) return NULL;
    508       e->kind = (uint8_t)kind;
    509       e->v.bin.lhs = lhs;
    510       e->v.bin.rhs = rhs;
    511       return e;
    512     }
    513     if (match_kw(p, "ORIGIN") || match_kw(p, "LENGTH") ||
    514         match_kw(p, "ADDR") || match_kw(p, "LOADADDR") ||
    515         match_kw(p, "SIZEOF") || match_kw(p, "DEFINED")) {
    516       size_t end = p->pos;
    517       size_t start = end;
    518       KitLinkExprKind kind;
    519       const char* s;
    520       size_t n;
    521       KitLinkExpr* e;
    522       while (start > 0 && is_id_cont((unsigned char)p->src[start - 1]))
    523         --start;
    524       if (end - start == 6 && memcmp(p->src + start, "ORIGIN", 6) == 0)
    525         kind = KIT_LE_REGION_ORIGIN;
    526       else if (end - start == 6 && memcmp(p->src + start, "LENGTH", 6) == 0)
    527         kind = KIT_LE_REGION_LENGTH;
    528       else if (end - start == 4 && memcmp(p->src + start, "ADDR", 4) == 0)
    529         kind = KIT_LE_ADDR;
    530       else if (end - start == 8 && memcmp(p->src + start, "LOADADDR", 8) == 0)
    531         kind = KIT_LE_LOADADDR;
    532       else if (end - start == 6 && memcmp(p->src + start, "SIZEOF", 6) == 0)
    533         kind = KIT_LE_SIZEOF;
    534       else
    535         kind = KIT_LE_DEFINED;
    536       if (expect_ch(p, '(')) return NULL;
    537       if (lex_ident(p, &s, &n)) return NULL;
    538       if (expect_ch(p, ')')) return NULL;
    539       e = lsp_new_expr(p);
    540       if (!e) return NULL;
    541       e->kind = (uint8_t)kind;
    542       e->v.name = lsp_slice(p, s, n);
    543       return e;
    544     }
    545     if (match_kw(p, "SIZEOF_HEADERS")) {
    546       KitLinkExpr* e;
    547       if (match_ch(p, '(')) {
    548         if (expect_ch(p, ')')) return NULL;
    549       }
    550       e = lsp_new_expr(p);
    551       if (!e) return NULL;
    552       e->kind = KIT_LE_SIZEOF_HEADERS;
    553       return e;
    554     }
    555     if (match_kw(p, "ABSOLUTE")) {
    556       KitLinkExpr *val, *e;
    557       if (expect_ch(p, '(')) return NULL;
    558       val = parse_expr(p);
    559       if (!val) return NULL;
    560       if (expect_ch(p, ')')) return NULL;
    561       e = lsp_new_expr(p);
    562       if (!e) return NULL;
    563       e->kind = KIT_LE_ABSOLUTE;
    564       e->v.align.val = val;
    565       e->v.align.align = NULL;
    566       return e;
    567     }
    568     {
    569       const char* s;
    570       size_t n;
    571       KitLinkExpr* e;
    572       if (lex_ident(p, &s, &n)) return NULL;
    573       e = lsp_new_expr(p);
    574       if (!e) return NULL;
    575       e->kind = KIT_LE_SYM;
    576       e->v.name = lsp_slice(p, s, n);
    577       return e;
    578     }
    579   }
    580   lsp_errf(p, p->pos, "unexpected '%c' in expression", (char)ch);
    581   return NULL;
    582 }
    583 
    584 /* Depth-guarded entry to atom parsing. Every nesting level (parens, unary,
    585  * binop RHS, helper-call argument) descends through parse_atom, so bumping the
    586  * counter here bounds total expression-parse recursion. */
    587 static KitLinkExpr* parse_atom(LSP* p) {
    588   KitLinkExpr* e;
    589   if (p->err) return NULL;
    590   if (++p->expr_depth > LSP_MAX_EXPR_DEPTH) {
    591     lsp_errf(p, p->pos, "linker-script expression nested too deeply (>%d)",
    592              LSP_MAX_EXPR_DEPTH);
    593     --p->expr_depth;
    594     return NULL;
    595   }
    596   e = parse_atom_inner(p);
    597   --p->expr_depth;
    598   return e;
    599 }
    600 
    601 /* Returns >=0 binding power for a binary operator at p->pos and
    602  * advances past it; -1 if no binary operator at the lookahead. */
    603 static int try_take_binop(LSP* p, KitLinkExprKind* out_kind) {
    604   int ch;
    605   skip_ws(p);
    606   if (p->err) return -1;
    607   if (p->pos >= p->len) return -1;
    608   ch = (unsigned char)p->src[p->pos];
    609   switch (ch) {
    610     case '|':
    611       ++p->pos;
    612       *out_kind = KIT_LE_OR;
    613       return 0;
    614     case '^':
    615       ++p->pos;
    616       *out_kind = KIT_LE_XOR;
    617       return 1;
    618     case '&':
    619       ++p->pos;
    620       *out_kind = KIT_LE_AND;
    621       return 2;
    622     case '<':
    623       if (p->pos + 1 < p->len && p->src[p->pos + 1] == '<') {
    624         p->pos += 2;
    625         *out_kind = KIT_LE_SHL;
    626         return 3;
    627       }
    628       return -1;
    629     case '>':
    630       if (p->pos + 1 < p->len && p->src[p->pos + 1] == '>') {
    631         p->pos += 2;
    632         *out_kind = KIT_LE_SHR;
    633         return 3;
    634       }
    635       return -1;
    636     case '+':
    637       ++p->pos;
    638       *out_kind = KIT_LE_ADD;
    639       return 4;
    640     case '-':
    641       ++p->pos;
    642       *out_kind = KIT_LE_SUB;
    643       return 4;
    644     case '*':
    645       ++p->pos;
    646       *out_kind = KIT_LE_MUL;
    647       return 5;
    648     case '/':
    649       /* Division. Block-comment and /DISCARD/ openers are filtered
    650        * elsewhere: skip_ws eats slash-star comments, and /DISCARD/ is
    651        * recognized by the SECTIONS-body loop before expression
    652        * context. */
    653       ++p->pos;
    654       *out_kind = KIT_LE_DIV;
    655       return 5;
    656     default:
    657       return -1;
    658   }
    659 }
    660 
    661 static KitLinkExpr* parse_binop_rhs(LSP* p, int min_bp, KitLinkExpr* lhs) {
    662   while (!p->err) {
    663     size_t save;
    664     KitLinkExprKind k;
    665     int bp;
    666     skip_ws(p);
    667     if (p->err) return NULL;
    668     save = p->pos;
    669     bp = try_take_binop(p, &k);
    670     if (bp < 0) return lhs;
    671     if (bp < min_bp) {
    672       p->pos = save;
    673       return lhs;
    674     }
    675     {
    676       KitLinkExpr* rhs = parse_atom(p);
    677       KitLinkExpr* node;
    678       if (!rhs) return NULL;
    679       rhs = parse_binop_rhs(p, bp + 1, rhs);
    680       if (!rhs) return NULL;
    681       node = lsp_new_expr(p);
    682       if (!node) return NULL;
    683       node->kind = (uint8_t)k;
    684       node->v.bin.lhs = lhs;
    685       node->v.bin.rhs = rhs;
    686       lhs = node;
    687     }
    688   }
    689   return NULL;
    690 }
    691 
    692 static KitLinkExpr* parse_expr(LSP* p) {
    693   KitLinkExpr* lhs = parse_atom(p);
    694   if (!lhs) return NULL;
    695   return parse_binop_rhs(p, 0, lhs);
    696 }
    697 
    698 /* ---- assignment helpers ---- */
    699 
    700 static int push_dot_align(LSP* p, VecAsn* asns, KitLinkExpr* align_n) {
    701   KitLinkExpr* dot;
    702   KitLinkExpr* aln;
    703   KitLinkAssignment a;
    704   dot = lsp_new_expr(p);
    705   if (!dot) return 1;
    706   dot->kind = KIT_LE_DOT;
    707   aln = lsp_new_expr(p);
    708   if (!aln) return 1;
    709   aln->kind = KIT_LE_ALIGN;
    710   aln->v.align.val = dot;
    711   aln->v.align.align = align_n;
    712   memset(&a, 0, sizeof(a));
    713   a.kind = KIT_LAS_DOT;
    714   a.sym = KIT_SLICE_NULL;
    715   a.expr = aln;
    716   return VEC_PUSH(p, *asns, a);
    717 }
    718 
    719 /* ---- output section body ---- */
    720 
    721 static int lex_pattern_token(LSP* p, const char** out, size_t* out_len) {
    722   size_t start;
    723   skip_ws(p);
    724   if (p->err) return 1;
    725   start = p->pos;
    726   while (p->pos < p->len) {
    727     char c = p->src[p->pos];
    728     if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '(' ||
    729         c == ')' || c == '{' || c == '}' || c == ';' || c == ':' ||
    730         c == ',')
    731       break;
    732     ++p->pos;
    733   }
    734   if (p->pos == start) {
    735     lsp_errf(p, p->pos, "expected pattern");
    736     return 1;
    737   }
    738   *out = p->src + start;
    739   *out_len = p->pos - start;
    740   return 0;
    741 }
    742 
    743 static const KitSlice* copy_slice_vec(LSP* p, const VecSlice* v) {
    744   KitSlice* arr;
    745   if (!v || !v->n) return NULL;
    746   arr = arena_array(p->arena, KitSlice, v->n);
    747   if (!arr) return NULL;
    748   memcpy(arr, v->p, sizeof(*arr) * v->n);
    749   return arr;
    750 }
    751 
    752 static int parse_file_excludes(LSP* p, VecSlice* excludes) {
    753   if (expect_ch(p, '(')) return 1;
    754   for (;;) {
    755     int ch;
    756     const char* s;
    757     size_t n;
    758     KitSlice sl;
    759     skip_ws(p);
    760     if (p->err) return 1;
    761     ch = peek_ch(p);
    762     if (ch == ')') {
    763       ++p->pos;
    764       return 0;
    765     }
    766     if (ch < 0) {
    767       lsp_errf(p, p->pos, "unterminated EXCLUDE_FILE");
    768       return 1;
    769     }
    770     if (lex_pattern_token(p, &s, &n)) return 1;
    771     sl = lsp_slice(p, s, n);
    772     if (!sl.s) return 1;
    773     if (VEC_PUSH(p, *excludes, sl)) return 1;
    774   }
    775 }
    776 
    777 static int parse_input_matchers_for_file(LSP* p, VecMatch* out,
    778                                          KitSlice file_pattern, int keep) {
    779   /* expect `(p1 p2 ...)` */
    780   VecSlice excludes = {0};
    781   if (expect_ch(p, '(')) return 1;
    782   for (;;) {
    783     int ch;
    784     skip_ws(p);
    785     if (p->err) return 1;
    786     ch = peek_ch(p);
    787     if (ch == ')') {
    788       ++p->pos;
    789       vec_free_(p, excludes.p, excludes.cap, sizeof(*excludes.p));
    790       return 0;
    791     }
    792     if (ch < 0) {
    793       lsp_errf(p, p->pos, "unterminated `*(...)`");
    794       goto fail;
    795     }
    796     if (match_kw(p, "EXCLUDE_FILE")) {
    797       if (parse_file_excludes(p, &excludes)) goto fail;
    798       continue;
    799     }
    800     {
    801       const char* s;
    802       size_t n;
    803       KitLinkInputMatch m;
    804       if (lex_pattern_token(p, &s, &n)) goto fail;
    805       if (n == 0) {
    806         lsp_errf(p, p->pos, "expected section pattern");
    807         goto fail;
    808       }
    809       memset(&m, 0, sizeof(m));
    810       m.file_pattern = file_pattern;
    811       m.section_pattern = lsp_slice(p, s, n);
    812       m.exclude_file_patterns = copy_slice_vec(p, &excludes);
    813       m.nexclude_file_patterns = excludes.n;
    814       m.keep = keep;
    815       if (excludes.n && !m.exclude_file_patterns) goto fail;
    816       if (VEC_PUSH(p, *out, m)) goto fail;
    817     }
    818   }
    819 fail:
    820   vec_free_(p, excludes.p, excludes.cap, sizeof(*excludes.p));
    821   return 1;
    822 }
    823 
    824 static int parse_input_matchers(LSP* p, VecMatch* out, int keep) {
    825   /* opening `*` already consumed by caller. */
    826   return parse_input_matchers_for_file(p, out, KIT_SLICE_NULL, keep);
    827 }
    828 
    829 static int parse_assignment_call(LSP* p, KitLinkAsnKind kind, VecAsn* asns) {
    830   const char* s;
    831   size_t n;
    832   KitLinkExpr* e;
    833   KitLinkAssignment a;
    834   if (expect_ch(p, '(')) return 1;
    835   if (lex_ident(p, &s, &n)) return 1;
    836   if (expect_ch(p, '=')) return 1;
    837   e = parse_expr(p);
    838   if (!e) return 1;
    839   if (expect_ch(p, ')')) return 1;
    840   (void)match_ch(p, ';');
    841   memset(&a, 0, sizeof(a));
    842   a.kind = (uint8_t)kind;
    843   a.sym = lsp_slice(p, s, n);
    844   a.expr = e;
    845   return VEC_PUSH(p, *asns, a);
    846 }
    847 
    848 static int parse_assertion(LSP* p, VecAssert* asserts) {
    849   KitLinkAssertion a;
    850   memset(&a, 0, sizeof(a));
    851   if (expect_ch(p, '(')) return 1;
    852   a.expr = parse_expr(p);
    853   if (!a.expr) return 1;
    854   if (expect_ch(p, ',')) return 1;
    855   if (lex_string(p, &a.message)) return 1;
    856   if (expect_ch(p, ')')) return 1;
    857   (void)match_ch(p, ';');
    858   return VEC_PUSH(p, *asserts, a);
    859 }
    860 
    861 static int parse_file_qualified_matchers(LSP* p, VecMatch* inputs,
    862                                          const char* file, size_t file_len,
    863                                          int keep) {
    864   KitSlice fp = lsp_slice(p, file, file_len);
    865   if (!fp.s) return 1;
    866   return parse_input_matchers_for_file(p, inputs, fp, keep);
    867 }
    868 
    869 static int parse_section_body(LSP* p, VecMatch* inputs, VecAsn* asns,
    870                               VecAssert* asserts, KitLinkExpr** fill_out,
    871                               u32* body_seq) {
    872   u32 prev_in = inputs->n;
    873   u32 prev_as = asns->n;
    874   if (expect_ch(p, '{')) return 1;
    875   for (;;) {
    876     int ch;
    877     /* Stamp the source-order body position onto every input match and
    878      * assignment pushed by the previous iteration, then bump for the next
    879      * command. One body command (a `*(...)`, a `sym = expr`, a `. = expr`)
    880      * may push several input matches (`*(.a .b .c)`) but they all belong to
    881      * the same command and share its body_seq. The applicator merge-walks
    882      * inputs+asns by body_seq to execute them in true source order. */
    883     while (prev_in < inputs->n) inputs->p[prev_in++].body_seq = *body_seq;
    884     while (prev_as < asns->n) asns->p[prev_as++].body_seq = *body_seq;
    885     ++*body_seq;
    886     skip_ws(p);
    887     if (p->err) return 1;
    888     ch = peek_ch(p);
    889     if (ch == '}') {
    890       ++p->pos;
    891       return 0;
    892     }
    893     if (ch < 0) {
    894       lsp_errf(p, p->pos, "unterminated section body");
    895       return 1;
    896     }
    897     if (ch == '*') {
    898       ++p->pos;
    899       if (parse_input_matchers(p, inputs, 0)) return 1;
    900       continue;
    901     }
    902     if (ch == '.') {
    903       /* `. = expr;` */
    904       size_t off = p->pos;
    905       ++p->pos;
    906       skip_ws(p);
    907       if (p->err) return 1;
    908       if (!match_ch(p, '=')) {
    909         lsp_errf(p, off, "expected `. = expr` in section body");
    910         return 1;
    911       }
    912       {
    913         KitLinkExpr* e = parse_expr(p);
    914         KitLinkAssignment a;
    915         if (!e) return 1;
    916         if (!match_ch(p, ';')) { /* ; is optional but encouraged */
    917         }
    918         memset(&a, 0, sizeof(a));
    919         a.kind = KIT_LAS_DOT;
    920         a.sym = KIT_SLICE_NULL;
    921         a.expr = e;
    922         if (VEC_PUSH(p, *asns, a)) return 1;
    923       }
    924       continue;
    925     }
    926     if (is_id_start(ch)) {
    927       if (match_kw(p, "KEEP")) {
    928         const char* fs;
    929         size_t fn;
    930         if (expect_ch(p, '(')) return 1;
    931         skip_ws(p);
    932         if (p->err) return 1;
    933         if (peek_ch(p) == '*') {
    934           ++p->pos;
    935           if (parse_input_matchers(p, inputs, 1)) return 1;
    936         } else {
    937           if (lex_pattern_token(p, &fs, &fn)) return 1;
    938           if (parse_file_qualified_matchers(p, inputs, fs, fn, 1)) return 1;
    939         }
    940         if (expect_ch(p, ')')) return 1;
    941         continue;
    942       }
    943       if (match_kw(p, "FILL")) {
    944         if (expect_ch(p, '(')) return 1;
    945         *fill_out = parse_expr(p);
    946         if (!*fill_out) return 1;
    947         if (expect_ch(p, ')')) return 1;
    948         (void)match_ch(p, ';');
    949         continue;
    950       }
    951       if (match_kw(p, "ASSERT")) {
    952         if (parse_assertion(p, asserts)) return 1;
    953         continue;
    954       }
    955       if (match_kw(p, "PROVIDE_HIDDEN")) {
    956         if (parse_assignment_call(p, KIT_LAS_PROVIDE_HIDDEN, asns)) return 1;
    957         continue;
    958       }
    959       if (match_kw(p, "PROVIDE")) {
    960         if (parse_assignment_call(p, KIT_LAS_PROVIDE, asns)) return 1;
    961         continue;
    962       }
    963       if (match_kw(p, "HIDDEN")) {
    964         if (parse_assignment_call(p, KIT_LAS_HIDDEN, asns)) return 1;
    965         continue;
    966       }
    967       /* sym = expr; or file(section-patterns) */
    968       const char* s;
    969       size_t n;
    970       KitLinkExpr* e;
    971       KitLinkAssignment a;
    972       if (lex_ident(p, &s, &n)) return 1;
    973       skip_ws(p);
    974       if (p->err) return 1;
    975       if (p->pos < p->len && p->src[p->pos] == '(') {
    976         if (parse_file_qualified_matchers(p, inputs, s, n, 0)) return 1;
    977         continue;
    978       }
    979       if (!match_ch(p, '=')) {
    980         lsp_errf(p, p->pos, "expected `=` after `%.*s`", (int)n, s);
    981         return 1;
    982       }
    983       e = parse_expr(p);
    984       if (!e) return 1;
    985       (void)match_ch(p, ';');
    986       memset(&a, 0, sizeof(a));
    987       a.kind = KIT_LAS_SYM;
    988       a.sym = lsp_slice(p, s, n);
    989       a.expr = e;
    990       if (VEC_PUSH(p, *asns, a)) return 1;
    991       continue;
    992     }
    993     lsp_errf(p, p->pos, "unexpected '%c' in section body", (char)ch);
    994     return 1;
    995   }
    996 }
    997 
    998 /* ---- output section header ---- */
    999 
   1000 static int parse_output_section(LSP* p, const char* name_buf, size_t name_len,
   1001                                 KitLinkExpr* vma, bool noload,
   1002                                 VecSec* sections, VecAssert* asserts) {
   1003   /* The `:` is the next non-ws char on entry. Header may carry
   1004    * `: ALIGN(N)` then `{ body }`. */
   1005   KitLinkOutputSection sec;
   1006   VecMatch inputs = {0};
   1007   VecAsn asns = {0};
   1008   VecSlice phdrs = {0};
   1009   KitLinkExpr* align_n = NULL;
   1010   KitLinkExpr* subalign = NULL;
   1011   KitLinkExpr* fill = NULL;
   1012   KitLinkExpr* lma = NULL;
   1013   KitLinkExpr* flags = NULL;
   1014   KitSlice region = KIT_SLICE_NULL;
   1015   KitSlice load_region = KIT_SLICE_NULL;
   1016 
   1017   if (expect_ch(p, ':')) return 1;
   1018   for (;;) {
   1019     skip_ws(p);
   1020     if (p->err) return 1;
   1021     if (match_kw(p, "ALIGN")) {
   1022       if (expect_ch(p, '(')) return 1;
   1023       align_n = parse_expr(p);
   1024       if (!align_n) return 1;
   1025       if (expect_ch(p, ')')) return 1;
   1026       continue;
   1027     }
   1028     if (match_kw(p, "SUBALIGN")) {
   1029       if (expect_ch(p, '(')) return 1;
   1030       subalign = parse_expr(p);
   1031       if (!subalign) return 1;
   1032       if (expect_ch(p, ')')) return 1;
   1033       continue;
   1034     }
   1035     if (match_kw(p, "AT")) {
   1036       if (expect_ch(p, '(')) return 1;
   1037       lma = parse_expr(p);
   1038       if (!lma) return 1;
   1039       if (expect_ch(p, ')')) return 1;
   1040       continue;
   1041     }
   1042     if (match_kw(p, "FLAGS")) {
   1043       if (expect_ch(p, '(')) return 1;
   1044       flags = parse_expr(p);
   1045       if (!flags) return 1;
   1046       if (expect_ch(p, ')')) return 1;
   1047       continue;
   1048     }
   1049     break;
   1050   }
   1051 
   1052   /* Section header alignment is encoded as the first asn — applicator
   1053    * pulls it before processing inputs. It runs at body_seq 0 (the section
   1054    * base); body commands start at body_seq 1. */
   1055   if (align_n) {
   1056     if (push_dot_align(p, &asns, align_n)) goto fail;
   1057     asns.p[asns.n - 1].body_seq = 0;
   1058   }
   1059 
   1060   {
   1061     u32 body_seq = 1;
   1062     if (parse_section_body(p, &inputs, &asns, asserts, &fill, &body_seq))
   1063       goto fail;
   1064   }
   1065 
   1066   for (;;) {
   1067     int ch;
   1068     skip_ws(p);
   1069     if (p->err) goto fail;
   1070     ch = peek_ch(p);
   1071     if (ch == ';') {
   1072       ++p->pos;
   1073       break;
   1074     }
   1075     if (ch == '>') {
   1076       const char* s;
   1077       size_t n;
   1078       ++p->pos;
   1079       if (lex_ident(p, &s, &n)) goto fail;
   1080       region = lsp_slice(p, s, n);
   1081       if (!region.s) goto fail;
   1082       continue;
   1083     }
   1084     if (ch == ':') {
   1085       const char* s;
   1086       size_t n;
   1087       KitSlice sl;
   1088       ++p->pos;
   1089       if (lex_ident(p, &s, &n)) goto fail;
   1090       sl = lsp_slice(p, s, n);
   1091       if (!sl.s) goto fail;
   1092       if (VEC_PUSH(p, phdrs, sl)) goto fail;
   1093       continue;
   1094     }
   1095     if (ch == '=') {
   1096       ++p->pos;
   1097       fill = parse_expr(p);
   1098       if (!fill) goto fail;
   1099       continue;
   1100     }
   1101     if (match_kw(p, "AT")) {
   1102       const char* s;
   1103       size_t n;
   1104       if (match_ch(p, '>')) {
   1105         if (lex_ident(p, &s, &n)) goto fail;
   1106         load_region = lsp_slice(p, s, n);
   1107         if (!load_region.s) goto fail;
   1108         continue;
   1109       }
   1110       if (expect_ch(p, '(')) goto fail;
   1111       lma = parse_expr(p);
   1112       if (!lma) goto fail;
   1113       if (expect_ch(p, ')')) goto fail;
   1114       continue;
   1115     }
   1116     if (match_kw(p, "FILL")) {
   1117       if (expect_ch(p, '(')) goto fail;
   1118       fill = parse_expr(p);
   1119       if (!fill) goto fail;
   1120       if (expect_ch(p, ')')) goto fail;
   1121       continue;
   1122     }
   1123     break;
   1124   }
   1125 
   1126   /* Materialize. */
   1127   {
   1128     KitLinkInputMatch* arr_in = NULL;
   1129     KitLinkAssignment* arr_as = NULL;
   1130     KitSlice* arr_ph = NULL;
   1131     if (inputs.n) {
   1132       arr_in = arena_array(p->arena, KitLinkInputMatch, inputs.n);
   1133       if (!arr_in) goto fail;
   1134       memcpy(arr_in, inputs.p, sizeof(*arr_in) * inputs.n);
   1135     }
   1136     if (asns.n) {
   1137       arr_as = arena_array(p->arena, KitLinkAssignment, asns.n);
   1138       if (!arr_as) goto fail;
   1139       memcpy(arr_as, asns.p, sizeof(*arr_as) * asns.n);
   1140     }
   1141     if (phdrs.n) {
   1142       arr_ph = arena_array(p->arena, KitSlice, phdrs.n);
   1143       if (!arr_ph) goto fail;
   1144       memcpy(arr_ph, phdrs.p, sizeof(*arr_ph) * phdrs.n);
   1145     }
   1146     memset(&sec, 0, sizeof(sec));
   1147     sec.name = lsp_slice(p, name_buf, name_len);
   1148     sec.vma = vma;
   1149     sec.lma = lma;
   1150     sec.inputs = arr_in;
   1151     sec.ninputs = inputs.n;
   1152     sec.region = region;
   1153     sec.load_region = load_region;
   1154     sec.phdrs = arr_ph;
   1155     sec.nphdrs = phdrs.n;
   1156     sec.asns = arr_as;
   1157     sec.nasns = asns.n;
   1158     sec.subalign = subalign;
   1159     sec.fill = fill;
   1160     sec.flags = flags;
   1161     sec.noload = noload;
   1162   }
   1163 
   1164   vec_free_(p, inputs.p, inputs.cap, sizeof(*inputs.p));
   1165   vec_free_(p, asns.p, asns.cap, sizeof(*asns.p));
   1166   vec_free_(p, phdrs.p, phdrs.cap, sizeof(*phdrs.p));
   1167 
   1168   return VEC_PUSH(p, *sections, sec);
   1169 
   1170 fail:
   1171   vec_free_(p, inputs.p, inputs.cap, sizeof(*inputs.p));
   1172   vec_free_(p, asns.p, asns.cap, sizeof(*asns.p));
   1173   vec_free_(p, phdrs.p, phdrs.cap, sizeof(*phdrs.p));
   1174   return 1;
   1175 }
   1176 
   1177 /* ---- SECTIONS{...} ---- */
   1178 
   1179 /* Stamp a monotonic textual-order sequence onto any top_asns / sections
   1180  * pushed since the previous call. Top-level assignments and output sections
   1181  * share one counter (*seq) so layout can interleave them in source order;
   1182  * see KitLinkAssignment.seq / KitLinkOutputSection.seq. */
   1183 static void stamp_seq(VecAsn* top_asns, u32* prev_as, VecSec* sections,
   1184                       u32* prev_sec, u32* seq) {
   1185   while (*prev_as < top_asns->n) top_asns->p[(*prev_as)++].seq = (*seq)++;
   1186   while (*prev_sec < sections->n) sections->p[(*prev_sec)++].seq = (*seq)++;
   1187 }
   1188 
   1189 static int parse_sections_block(LSP* p, VecAsn* top_asns, VecSec* sections,
   1190                                 VecAssert* asserts) {
   1191   u32 seq = 0;
   1192   u32 prev_as = 0;
   1193   u32 prev_sec = 0;
   1194   if (expect_ch(p, '{')) return 1;
   1195   for (;;) {
   1196     int ch;
   1197     /* Assign source-order seq to whatever the previous iteration pushed.
   1198      * (A single iteration appends at most one section but may append a
   1199      * burst of assignments via parse_assignment_call.) */
   1200     stamp_seq(top_asns, &prev_as, sections, &prev_sec, &seq);
   1201     skip_ws(p);
   1202     if (p->err) return 1;
   1203     ch = peek_ch(p);
   1204     if (ch == '}') {
   1205       ++p->pos;
   1206       return 0;
   1207     }
   1208     if (ch < 0) {
   1209       lsp_errf(p, p->pos, "unterminated SECTIONS block");
   1210       return 1;
   1211     }
   1212     /* /DISCARD/ : { body } */
   1213     if (ch == '/') {
   1214       static const char kDiscard[] = "/DISCARD/";
   1215       size_t klen = sizeof(kDiscard) - 1;
   1216       if (p->pos + klen <= p->len &&
   1217           memcmp(p->src + p->pos, kDiscard, klen) == 0) {
   1218         p->pos += klen;
   1219         if (parse_output_section(p, kDiscard, klen, NULL, false, sections,
   1220                                  asserts))
   1221           return 1;
   1222         continue;
   1223       }
   1224       lsp_errf(p, p->pos, "expected /DISCARD/ or section header");
   1225       return 1;
   1226     }
   1227     /* `. = expr;` at SECTIONS top level */
   1228     if (ch == '.') {
   1229       size_t off = p->pos;
   1230       /* Distinguish bare-dot (`. =`) from `.text :` head. Bare dot has
   1231        * no id-cont following. */
   1232       if (p->pos + 1 < p->len &&
   1233           is_id_cont((unsigned char)p->src[p->pos + 1])) {
   1234         /* falls through to identifier path */
   1235       } else {
   1236         ++p->pos;
   1237         skip_ws(p);
   1238         if (p->err) return 1;
   1239         if (!match_ch(p, '=')) {
   1240           lsp_errf(p, off, "expected `. = expr`");
   1241           return 1;
   1242         }
   1243         {
   1244           KitLinkExpr* e = parse_expr(p);
   1245           KitLinkAssignment a;
   1246           if (!e) return 1;
   1247           (void)match_ch(p, ';');
   1248           memset(&a, 0, sizeof(a));
   1249           a.kind = KIT_LAS_DOT;
   1250           a.sym = KIT_SLICE_NULL;
   1251           a.expr = e;
   1252           if (VEC_PUSH(p, *top_asns, a)) return 1;
   1253         }
   1254         continue;
   1255       }
   1256     }
   1257     if (is_id_start(ch)) {
   1258       /* either `name :` (output section) or `sym = expr;` */
   1259       const char* s;
   1260       size_t n;
   1261       size_t name_off;
   1262       KitLinkExpr* vma = NULL;
   1263       bool noload = false;
   1264       if (match_kw(p, "ASSERT")) {
   1265         if (parse_assertion(p, asserts)) return 1;
   1266         continue;
   1267       }
   1268       if (match_kw(p, "PROVIDE_HIDDEN")) {
   1269         if (parse_assignment_call(p, KIT_LAS_PROVIDE_HIDDEN, top_asns))
   1270           return 1;
   1271         continue;
   1272       }
   1273       if (match_kw(p, "PROVIDE")) {
   1274         if (parse_assignment_call(p, KIT_LAS_PROVIDE, top_asns)) return 1;
   1275         continue;
   1276       }
   1277       if (match_kw(p, "HIDDEN")) {
   1278         if (parse_assignment_call(p, KIT_LAS_HIDDEN, top_asns)) return 1;
   1279         continue;
   1280       }
   1281       name_off = p->pos;
   1282       if (lex_ident(p, &s, &n)) return 1;
   1283       skip_ws(p);
   1284       if (p->err) return 1;
   1285       if (match_ch(p, '(')) {
   1286         const char* ts;
   1287         size_t tn;
   1288         if (lex_ident(p, &ts, &tn)) return 1;
   1289         if (tn == 6 && memcmp(ts, "NOLOAD", 6) == 0)
   1290           noload = true;
   1291         else {
   1292           lsp_errf(p, name_off, "unsupported output-section type `%.*s`",
   1293                    (int)tn, ts);
   1294           return 1;
   1295         }
   1296         if (expect_ch(p, ')')) return 1;
   1297         skip_ws(p);
   1298         if (p->err) return 1;
   1299       }
   1300       if (p->pos < p->len && p->src[p->pos] != ':' &&
   1301           p->src[p->pos] != '=') {
   1302         vma = parse_expr(p);
   1303         if (!vma) return 1;
   1304         skip_ws(p);
   1305         if (p->err) return 1;
   1306         if (match_ch(p, '(')) {
   1307           const char* ts;
   1308           size_t tn;
   1309           if (lex_ident(p, &ts, &tn)) return 1;
   1310           if (tn == 6 && memcmp(ts, "NOLOAD", 6) == 0)
   1311             noload = true;
   1312           else {
   1313             lsp_errf(p, name_off, "unsupported output-section type `%.*s`",
   1314                      (int)tn, ts);
   1315             return 1;
   1316           }
   1317           if (expect_ch(p, ')')) return 1;
   1318           skip_ws(p);
   1319           if (p->err) return 1;
   1320         }
   1321       }
   1322       if (p->pos < p->len && p->src[p->pos] == ':') {
   1323         char* nm = lsp_strdup(p, s, n);
   1324         if (!nm) return 1;
   1325         if (parse_output_section(p, nm, n, vma, noload, sections, asserts))
   1326           return 1;
   1327         continue;
   1328       }
   1329       if (match_ch(p, '=')) {
   1330         KitLinkExpr* e = parse_expr(p);
   1331         KitLinkAssignment a;
   1332         if (!e) return 1;
   1333         (void)match_ch(p, ';');
   1334         memset(&a, 0, sizeof(a));
   1335         a.kind = KIT_LAS_SYM;
   1336         a.sym = lsp_slice(p, s, n);
   1337         a.expr = e;
   1338         if (VEC_PUSH(p, *top_asns, a)) return 1;
   1339         continue;
   1340       }
   1341       lsp_errf(p, name_off,
   1342                "expected `:` (output section) or `=` (assignment) after "
   1343                "`%.*s`",
   1344                (int)n, s);
   1345       return 1;
   1346     }
   1347     lsp_errf(p, p->pos, "unexpected '%c' in SECTIONS body", (char)ch);
   1348     return 1;
   1349   }
   1350 }
   1351 
   1352 static int eval_const_expr(LSP* p, const KitLinkExpr* e, u64* out) {
   1353   u64 a, b;
   1354   if (!e) return 1;
   1355   switch ((KitLinkExprKind)e->kind) {
   1356     case KIT_LE_INT:
   1357       *out = (u64)e->v.int_val;
   1358       return 0;
   1359     case KIT_LE_NEG:
   1360       if (eval_const_expr(p, e->v.align.val, &a)) return 1;
   1361       *out = (u64)(-(i64)a);
   1362       return 0;
   1363     case KIT_LE_ADD:
   1364     case KIT_LE_SUB:
   1365     case KIT_LE_MUL:
   1366     case KIT_LE_DIV:
   1367     case KIT_LE_AND:
   1368     case KIT_LE_OR:
   1369     case KIT_LE_XOR:
   1370     case KIT_LE_SHL:
   1371     case KIT_LE_SHR:
   1372     case KIT_LE_MAX:
   1373     case KIT_LE_MIN:
   1374       if (eval_const_expr(p, e->v.bin.lhs, &a) ||
   1375           eval_const_expr(p, e->v.bin.rhs, &b))
   1376         return 1;
   1377       switch ((KitLinkExprKind)e->kind) {
   1378         case KIT_LE_ADD:
   1379           *out = a + b;
   1380           return 0;
   1381         case KIT_LE_SUB:
   1382           *out = a - b;
   1383           return 0;
   1384         case KIT_LE_MUL:
   1385           *out = a * b;
   1386           return 0;
   1387         case KIT_LE_DIV:
   1388           if (b == 0) {
   1389             lsp_errf(p, p->pos, "division by zero in constant expression");
   1390             return 1;
   1391           }
   1392           *out = a / b;
   1393           return 0;
   1394         case KIT_LE_AND:
   1395           *out = a & b;
   1396           return 0;
   1397         case KIT_LE_OR:
   1398           *out = a | b;
   1399           return 0;
   1400         case KIT_LE_XOR:
   1401           *out = a ^ b;
   1402           return 0;
   1403         case KIT_LE_SHL:
   1404           *out = a << b;
   1405           return 0;
   1406         case KIT_LE_SHR:
   1407           *out = a >> b;
   1408           return 0;
   1409         case KIT_LE_MAX:
   1410           *out = a > b ? a : b;
   1411           return 0;
   1412         case KIT_LE_MIN:
   1413           *out = a < b ? a : b;
   1414           return 0;
   1415         default:
   1416           break;
   1417       }
   1418       break;
   1419     case KIT_LE_ALIGN:
   1420     case KIT_LE_BLOCK:
   1421       if (eval_const_expr(p, e->v.align.val, &a) ||
   1422           eval_const_expr(p, e->v.align.align, &b))
   1423         return 1;
   1424       *out = b ? ALIGN_UP(a, b) : a;
   1425       return 0;
   1426     case KIT_LE_ABSOLUTE:
   1427       return eval_const_expr(p, e->v.align.val, out);
   1428     default:
   1429       break;
   1430   }
   1431   lsp_errf(p, p->pos, "non-constant expression in MEMORY region");
   1432   return 1;
   1433 }
   1434 
   1435 static int parse_memory_block(LSP* p, VecRegion* regions) {
   1436   if (expect_ch(p, '{')) return 1;
   1437   for (;;) {
   1438     const char* ns;
   1439     size_t nn;
   1440     KitLinkRegion r;
   1441     int have_origin = 0, have_length = 0;
   1442     skip_ws(p);
   1443     if (p->err) return 1;
   1444     if (peek_ch(p) == '}') {
   1445       ++p->pos;
   1446       return 0;
   1447     }
   1448     if (lex_ident(p, &ns, &nn)) return 1;
   1449     memset(&r, 0, sizeof(r));
   1450     r.name = lsp_slice(p, ns, nn);
   1451     if (!r.name.s) return 1;
   1452     skip_ws(p);
   1453     if (match_ch(p, '(')) {
   1454       while (!p->err && p->pos < p->len && p->src[p->pos] != ')') {
   1455         char c = p->src[p->pos++];
   1456         if (c == 'r' || c == 'R') r.flags |= KIT_LRF_R;
   1457         if (c == 'w' || c == 'W') r.flags |= KIT_LRF_W;
   1458         if (c == 'x' || c == 'X') r.flags |= KIT_LRF_X;
   1459       }
   1460       if (expect_ch(p, ')')) return 1;
   1461     }
   1462     if (expect_ch(p, ':')) return 1;
   1463     while (!p->err) {
   1464       const char* ks;
   1465       size_t kn;
   1466       KitLinkExpr* e;
   1467       u64 v;
   1468       skip_ws(p);
   1469       if (p->err) return 1;
   1470       if (peek_ch(p) == '}') break;
   1471       if (lex_ident(p, &ks, &kn)) return 1;
   1472       if (expect_ch(p, '=')) return 1;
   1473       e = parse_expr(p);
   1474       if (!e) return 1;
   1475       if (eval_const_expr(p, e, &v)) return 1;
   1476       if ((kn == 6 && memcmp(ks, "ORIGIN", 6) == 0) ||
   1477           (kn == 3 && memcmp(ks, "org", 3) == 0) ||
   1478           (kn == 1 && (ks[0] == 'o' || ks[0] == 'O'))) {
   1479         r.origin = v;
   1480         have_origin = 1;
   1481       } else if ((kn == 6 && memcmp(ks, "LENGTH", 6) == 0) ||
   1482                  (kn == 3 && memcmp(ks, "len", 3) == 0) ||
   1483                  (kn == 1 && (ks[0] == 'l' || ks[0] == 'L'))) {
   1484         r.length = v;
   1485         have_length = 1;
   1486       } else {
   1487         lsp_errf(p, p->pos, "unknown MEMORY field `%.*s`", (int)kn, ks);
   1488         return 1;
   1489       }
   1490       skip_ws(p);
   1491       (void)match_ch(p, ',');
   1492       if (have_origin && have_length) break;
   1493     }
   1494     if (!have_origin || !have_length) {
   1495       lsp_errf(p, p->pos, "MEMORY region `%.*s` needs ORIGIN and LENGTH",
   1496                (int)nn, ns);
   1497       return 1;
   1498     }
   1499     if (VEC_PUSH(p, *regions, r)) return 1;
   1500   }
   1501 }
   1502 
   1503 static uint32_t phdr_type_from_name(const char* s, size_t n) {
   1504   if (n == 7 && memcmp(s, "PT_NULL", 7) == 0) return 0u;
   1505   if (n == 7 && memcmp(s, "PT_LOAD", 7) == 0) return 1u;
   1506   if (n == 10 && memcmp(s, "PT_DYNAMIC", 10) == 0) return 2u;
   1507   if (n == 9 && memcmp(s, "PT_INTERP", 9) == 0) return 3u;
   1508   if (n == 7 && memcmp(s, "PT_NOTE", 7) == 0) return 4u;
   1509   if (n == 8 && memcmp(s, "PT_SHLIB", 8) == 0) return 5u;
   1510   if (n == 7 && memcmp(s, "PT_PHDR", 7) == 0) return 6u;
   1511   if (n == 6 && memcmp(s, "PT_TLS", 6) == 0) return 7u;
   1512   if (n == 12 && memcmp(s, "PT_GNU_STACK", 12) == 0) return 0x6474e551u;
   1513   if (n == 12 && memcmp(s, "PT_GNU_RELRO", 12) == 0) return 0x6474e552u;
   1514   return 1u;
   1515 }
   1516 
   1517 static int parse_phdrs_block(LSP* p, VecPhdr* phdrs) {
   1518   if (expect_ch(p, '{')) return 1;
   1519   for (;;) {
   1520     const char *ns, *ts;
   1521     size_t nn, tn;
   1522     KitLinkPhdr ph;
   1523     skip_ws(p);
   1524     if (p->err) return 1;
   1525     if (peek_ch(p) == '}') {
   1526       ++p->pos;
   1527       return 0;
   1528     }
   1529     if (lex_ident(p, &ns, &nn)) return 1;
   1530     if (lex_ident(p, &ts, &tn)) return 1;
   1531     memset(&ph, 0, sizeof(ph));
   1532     ph.name = lsp_slice(p, ns, nn);
   1533     ph.type = phdr_type_from_name(ts, tn);
   1534     if (!ph.name.s) return 1;
   1535     for (;;) {
   1536       skip_ws(p);
   1537       if (p->err) return 1;
   1538       if (match_ch(p, ';')) break;
   1539       if (match_kw(p, "FILEHDR")) {
   1540         ph.filehdr = true;
   1541         continue;
   1542       }
   1543       if (match_kw(p, "PHDRS")) {
   1544         ph.phdrs = true;
   1545         continue;
   1546       }
   1547       if (match_kw(p, "FLAGS")) {
   1548         if (expect_ch(p, '(')) return 1;
   1549         ph.flags = parse_expr(p);
   1550         if (!ph.flags) return 1;
   1551         if (expect_ch(p, ')')) return 1;
   1552         continue;
   1553       }
   1554       lsp_errf(p, p->pos, "expected PHDRS attribute or `;`");
   1555       return 1;
   1556     }
   1557     if (VEC_PUSH(p, *phdrs, ph)) return 1;
   1558   }
   1559 }
   1560 
   1561 static int parse_extern_directive(LSP* p, VecSlice* externs) {
   1562   if (expect_ch(p, '(')) return 1;
   1563   for (;;) {
   1564     int ch;
   1565     const char* s;
   1566     size_t n;
   1567     KitSlice sl;
   1568     skip_ws(p);
   1569     if (p->err) return 1;
   1570     ch = peek_ch(p);
   1571     if (ch == ')') {
   1572       ++p->pos;
   1573       (void)match_ch(p, ';');
   1574       return 0;
   1575     }
   1576     if (lex_ident(p, &s, &n)) return 1;
   1577     sl = lsp_slice(p, s, n);
   1578     if (!sl.s) return 1;
   1579     if (VEC_PUSH(p, *externs, sl)) return 1;
   1580   }
   1581 }
   1582 
   1583 static int parse_output_name_directive(LSP* p, KitSlice* out) {
   1584   if (expect_ch(p, '(')) return 1;
   1585   if (lex_ident_or_string(p, out)) return 1;
   1586   for (;;) {
   1587     KitSlice ignored;
   1588     skip_ws(p);
   1589     if (p->err) return 1;
   1590     if (match_ch(p, ')')) break;
   1591     if (match_ch(p, ',')) {
   1592       if (lex_ident_or_string(p, &ignored)) return 1;
   1593       continue;
   1594     }
   1595     lsp_errf(p, p->pos, "expected `,` or `)`");
   1596     return 1;
   1597   }
   1598   (void)match_ch(p, ';');
   1599   return 0;
   1600 }
   1601 
   1602 /* ---- top level ---- */
   1603 
   1604 static int parse_top(LSP* p, KitLinkScript* out) {
   1605   VecAsn top_asns = {0};
   1606   VecSec sections = {0};
   1607   VecRegion regions = {0};
   1608   VecPhdr phdrs = {0};
   1609   VecAssert asserts = {0};
   1610   VecSlice externs = {0};
   1611   KitSlice entry_name = KIT_SLICE_NULL;
   1612   KitSlice output_arch = KIT_SLICE_NULL;
   1613   KitSlice output_format = KIT_SLICE_NULL;
   1614   int saw_sections = 0;
   1615   int rc = 1;
   1616 
   1617   for (;;) {
   1618     int ch;
   1619     skip_ws(p);
   1620     if (p->err) goto done;
   1621     if (p->pos >= p->len) break;
   1622     ch = (unsigned char)p->src[p->pos];
   1623 
   1624     if (is_id_start(ch)) {
   1625       if (match_kw(p, "ENTRY")) {
   1626         const char* s;
   1627         size_t n;
   1628         if (expect_ch(p, '(')) goto done;
   1629         if (lex_ident(p, &s, &n)) goto done;
   1630         if (expect_ch(p, ')')) goto done;
   1631         (void)match_ch(p, ';');
   1632         entry_name = lsp_slice(p, s, n);
   1633         if (!entry_name.s) goto done;
   1634         continue;
   1635       }
   1636       if (match_kw(p, "SECTIONS")) {
   1637         if (saw_sections) {
   1638           lsp_errf(p, p->pos, "duplicate SECTIONS block");
   1639           goto done;
   1640         }
   1641         if (parse_sections_block(p, &top_asns, &sections, &asserts))
   1642           goto done;
   1643         saw_sections = 1;
   1644         continue;
   1645       }
   1646       if (match_kw(p, "MEMORY")) {
   1647         if (parse_memory_block(p, &regions)) goto done;
   1648         continue;
   1649       }
   1650       if (match_kw(p, "PHDRS")) {
   1651         if (parse_phdrs_block(p, &phdrs)) goto done;
   1652         continue;
   1653       }
   1654       if (match_kw(p, "EXTERN")) {
   1655         if (parse_extern_directive(p, &externs)) goto done;
   1656         continue;
   1657       }
   1658       if (match_kw(p, "ASSERT")) {
   1659         if (parse_assertion(p, &asserts)) goto done;
   1660         continue;
   1661       }
   1662       if (match_kw(p, "PROVIDE_HIDDEN")) {
   1663         if (parse_assignment_call(p, KIT_LAS_PROVIDE_HIDDEN, &top_asns))
   1664           goto done;
   1665         continue;
   1666       }
   1667       if (match_kw(p, "PROVIDE")) {
   1668         if (parse_assignment_call(p, KIT_LAS_PROVIDE, &top_asns)) goto done;
   1669         continue;
   1670       }
   1671       if (match_kw(p, "HIDDEN")) {
   1672         if (parse_assignment_call(p, KIT_LAS_HIDDEN, &top_asns)) goto done;
   1673         continue;
   1674       }
   1675       if (match_kw(p, "OUTPUT_FORMAT")) {
   1676         if (parse_output_name_directive(p, &output_format)) goto done;
   1677         continue;
   1678       }
   1679       if (match_kw(p, "OUTPUT_ARCH")) {
   1680         if (parse_output_name_directive(p, &output_arch)) goto done;
   1681         continue;
   1682       }
   1683       if (match_kw(p, "OVERLAY") || match_kw(p, "INSERT") ||
   1684           match_kw(p, "INPUT") || match_kw(p, "GROUP") ||
   1685           match_kw(p, "VERSION") || match_kw(p, "STARTUP") ||
   1686           match_kw(p, "SEARCH_DIR") || match_kw(p, "TARGET")) {
   1687         lsp_errf(p, p->pos,
   1688                  "directive not supported in this linker-script subset");
   1689         goto done;
   1690       }
   1691       lsp_errf(p, p->pos, "unknown top-level directive");
   1692       goto done;
   1693     }
   1694     lsp_errf(p, p->pos, "unexpected '%c' at top level", (char)ch);
   1695     goto done;
   1696   }
   1697 
   1698   /* Materialize. */
   1699   out->entry = entry_name;
   1700   out->output_arch = output_arch;
   1701   out->output_format = output_format;
   1702   out->regions = NULL;
   1703   out->nregions = 0;
   1704   out->phdrs = NULL;
   1705   out->nphdrs = 0;
   1706   out->top_asns = NULL;
   1707   out->ntop_asns = 0;
   1708   out->sections = NULL;
   1709   out->nsections = 0;
   1710   out->asserts = NULL;
   1711   out->nasserts = 0;
   1712   out->externs = NULL;
   1713   out->nexterns = 0;
   1714 
   1715   if (regions.n) {
   1716     KitLinkRegion* r = arena_array(p->arena, KitLinkRegion, regions.n);
   1717     if (!r) goto done;
   1718     memcpy(r, regions.p, sizeof(*r) * regions.n);
   1719     out->regions = r;
   1720     out->nregions = regions.n;
   1721   }
   1722   if (phdrs.n) {
   1723     KitLinkPhdr* ph = arena_array(p->arena, KitLinkPhdr, phdrs.n);
   1724     if (!ph) goto done;
   1725     memcpy(ph, phdrs.p, sizeof(*ph) * phdrs.n);
   1726     out->phdrs = ph;
   1727     out->nphdrs = phdrs.n;
   1728   }
   1729   if (top_asns.n) {
   1730     KitLinkAssignment* a = arena_array(p->arena, KitLinkAssignment, top_asns.n);
   1731     if (!a) goto done;
   1732     memcpy(a, top_asns.p, sizeof(*a) * top_asns.n);
   1733     out->top_asns = a;
   1734     out->ntop_asns = top_asns.n;
   1735   }
   1736   if (sections.n) {
   1737     KitLinkOutputSection* s =
   1738         arena_array(p->arena, KitLinkOutputSection, sections.n);
   1739     if (!s) goto done;
   1740     memcpy(s, sections.p, sizeof(*s) * sections.n);
   1741     out->sections = s;
   1742     out->nsections = sections.n;
   1743   }
   1744   if (asserts.n) {
   1745     KitLinkAssertion* a = arena_array(p->arena, KitLinkAssertion, asserts.n);
   1746     if (!a) goto done;
   1747     memcpy(a, asserts.p, sizeof(*a) * asserts.n);
   1748     out->asserts = a;
   1749     out->nasserts = asserts.n;
   1750   }
   1751   if (externs.n) {
   1752     KitSlice* e = arena_array(p->arena, KitSlice, externs.n);
   1753     if (!e) goto done;
   1754     memcpy(e, externs.p, sizeof(*e) * externs.n);
   1755     out->externs = e;
   1756     out->nexterns = externs.n;
   1757   }
   1758   rc = 0;
   1759 
   1760 done:
   1761   vec_free_(p, top_asns.p, top_asns.cap, sizeof(*top_asns.p));
   1762   vec_free_(p, sections.p, sections.cap, sizeof(*sections.p));
   1763   vec_free_(p, regions.p, regions.cap, sizeof(*regions.p));
   1764   vec_free_(p, phdrs.p, phdrs.cap, sizeof(*phdrs.p));
   1765   vec_free_(p, asserts.p, asserts.cap, sizeof(*asserts.p));
   1766   vec_free_(p, externs.p, externs.cap, sizeof(*externs.p));
   1767   return rc;
   1768 }
   1769 
   1770 /* ---- public API ---- */
   1771 
   1772 KitStatus kit_link_script_parse(const KitContext* ctx, KitSlice text,
   1773                                 KitLinkScript** out) {
   1774   ScriptOwner* owner;
   1775   LSP p;
   1776   int rc;
   1777   Heap* h;
   1778 
   1779   if (!out) return KIT_INVALID;
   1780   *out = NULL;
   1781   if (!ctx || !ctx->heap || !text.s) return KIT_INVALID;
   1782 
   1783   h = ctx->heap;
   1784   owner = (ScriptOwner*)h->alloc(h, sizeof(*owner), _Alignof(ScriptOwner));
   1785   if (!owner) return KIT_NOMEM;
   1786   memset(owner, 0, sizeof(*owner));
   1787   /* 16 KiB blocks: matches the linker's tu arena defaults and is plenty
   1788    * for the script subset we support. */
   1789   arena_init(&owner->arena, h, 16u * 1024u);
   1790 
   1791   memset(&p, 0, sizeof(p));
   1792   p.arena = &owner->arena;
   1793   p.heap = h;
   1794   p.diag = ctx->diag;
   1795   p.src = text.s;
   1796   p.len = text.len;
   1797 
   1798   rc = parse_top(&p, &owner->script);
   1799   if (rc != 0 || p.err) {
   1800     arena_fini(&owner->arena);
   1801     h->free(h, owner, sizeof(*owner));
   1802     return KIT_ERR;
   1803   }
   1804   *out = &owner->script;
   1805   return KIT_OK;
   1806 }
   1807 
   1808 void kit_link_script_free(const KitContext* ctx, KitLinkScript* s) {
   1809   ScriptOwner* owner;
   1810   Heap* h;
   1811   if (!ctx || !ctx->heap || !s) return;
   1812   owner = (ScriptOwner*)((char*)s - offsetof(ScriptOwner, script));
   1813   h = ctx->heap;
   1814   arena_fini(&owner->arena);
   1815   h->free(h, owner, sizeof(*owner));
   1816 }