diff --git a/selfhost/cmd/w6l/main.combined.ww b/selfhost/cmd/w6l/main.combined.ww index 7ba25de5..10dbffa5 100644 --- a/selfhost/cmd/w6l/main.combined.ww +++ b/selfhost/cmd/w6l/main.combined.ww @@ -812,115 +812,6 @@ package rt; // a future task (task #39). ref/hare/rt/malloc.ha:27. @symbol("rt_malloc") export fn malloc(n: u64) *void; -// selfhost/cmd/w6l/sym.ww — port of cmd/w6l/sym.c. -// -// Linker symbol table. Singly-linked list, usually a few hundred -// entries; hashing isn't worth it yet. - -package w6l; - -type lsym = struct { - name: str, - val: u64, // offset within combined .text (or .data when - // indata=1) once linked - defined: i32, // 1 if some lobj defines this symbol - indata: i32, // 1 if defined in .data (writable globals) - owner: *lobj, - idxinowner: i32, - // Dynamic-linking fields. Set by resolve when an undefined sym - // is provided by some loaded lso. pltidx and dynsymidx default - // to -1 (set explicitly by resolve; alloc-zeroing gives 0, not -1). - isdyn: i32, - dynlib: *lso, - dynversion: str, // matched export's version; len 0 if none - pltidx: i32, - dynsymidx: i32, - snext: *lsym, -}; - -type lrel = struct { - off: u64, // offset within the relocation's section - section: i32, // 0 = .text, 1 = .data - kind: i32, // R_X86_64_* - sym: *lsym, - addend: i64, - rnext: *lrel, -}; - -type lobj = struct { - path: str, - buf: *u8, // object bytes - len: u64, - textoff: u64, // offset of .text in combined output - textsize: u64, - dataoff: u64, // offset of .data in combined output - datasize: u64, // bytes contributed to combined .data (0 if none) - onext: *lobj, -}; - -// lexport — one entry per GLOBAL/WEAK symbol exported by a loaded .so. -// Stored as a chain in the order the .so's dynsym presents them, so -// soprovides_v's first-match semantics agree with the C version. -type lexport = struct { - name: str, - version: str, // len 0 for unversioned globals - enext: *lexport, -}; - -type lso = struct { - path: str, // full filesystem path used to load - soname: str, // DT_SONAME, or basename if missing - exports: *lexport, // dynsym-order chain of exported names - sonext: *lso, -}; - -type lnk = struct { - objs: *lobj, - sos: *lso, - syms: *lsym, - rels: *lrel, - text: *u8, // combined .text - textcap: u64, - textlen: u64, - // Combined .data (writable). Empty unless any input .o has a - // .data PROGBITS section. - data: *u8, - datacap: u64, - datalen: u64, - errs: i32, - dynn: i32, // number of syms routed through PLT -}; - -fn streq(a: str, b: str) bool = { - if (a.len != b.len) { return false; }; - let i: i32 = 0; - for (i < a.len) { - if (a[i] != b[i]) { return false; }; - i += 1; - }; - return true; -}; - -export fn intern(l: *lnk, name: str) *lsym = { - let s: *lsym = l.syms; - for (s != nil) { - if (streq(s.name, name)) { return s; }; - s = s.snext; - }; - let n: *lsym = alloc(lsym { name = name, snext = l.syms })!; - l.syms = n; - return n; -}; - -export fn lookup(l: *lnk, name: str) *lsym = { - let s: *lsym = l.syms; - for (s != nil) { - if (streq(s.name, name)) { return s; }; - s = s.snext; - }; - return nil; -}; - // types — integer limits. Mirrors Hare's types::limits (I8_MAX, …) // platform-fixed for amd64. Numeric helpers live in lib/math, matching // Hare's split between types::limits and math::. @@ -2941,6 +2832,115 @@ export fn rpad(s: str, p: rune, maxlen: i32) str = { return frombytes(buf); }; +// selfhost/cmd/w6l/sym.ww — port of cmd/w6l/sym.c. +// +// Linker symbol table. Singly-linked list, usually a few hundred +// entries; hashing isn't worth it yet. + +package w6l; + +type lsym = struct { + name: str, + val: u64, // offset within combined .text (or .data when + // indata=1) once linked + defined: i32, // 1 if some lobj defines this symbol + indata: i32, // 1 if defined in .data (writable globals) + owner: *lobj, + idxinowner: i32, + // Dynamic-linking fields. Set by resolve when an undefined sym + // is provided by some loaded lso. pltidx and dynsymidx default + // to -1 (set explicitly by resolve; alloc-zeroing gives 0, not -1). + isdyn: i32, + dynlib: *lso, + dynversion: str, // matched export's version; len 0 if none + pltidx: i32, + dynsymidx: i32, + snext: *lsym, +}; + +type lrel = struct { + off: u64, // offset within the relocation's section + section: i32, // 0 = .text, 1 = .data + kind: i32, // R_X86_64_* + sym: *lsym, + addend: i64, + rnext: *lrel, +}; + +type lobj = struct { + path: str, + buf: *u8, // object bytes + len: u64, + textoff: u64, // offset of .text in combined output + textsize: u64, + dataoff: u64, // offset of .data in combined output + datasize: u64, // bytes contributed to combined .data (0 if none) + onext: *lobj, +}; + +// lexport — one entry per GLOBAL/WEAK symbol exported by a loaded .so. +// Stored as a chain in the order the .so's dynsym presents them, so +// soprovides_v's first-match semantics agree with the C version. +type lexport = struct { + name: str, + version: str, // len 0 for unversioned globals + enext: *lexport, +}; + +type lso = struct { + path: str, // full filesystem path used to load + soname: str, // DT_SONAME, or basename if missing + exports: *lexport, // dynsym-order chain of exported names + sonext: *lso, +}; + +type lnk = struct { + objs: *lobj, + sos: *lso, + syms: *lsym, + rels: *lrel, + text: *u8, // combined .text + textcap: u64, + textlen: u64, + // Combined .data (writable). Empty unless any input .o has a + // .data PROGBITS section. + data: *u8, + datacap: u64, + datalen: u64, + errs: i32, + dynn: i32, // number of syms routed through PLT +}; + +fn streq(a: str, b: str) bool = { + if (a.len != b.len) { return false; }; + let i: i32 = 0; + for (i < a.len) { + if (a[i] != b[i]) { return false; }; + i += 1; + }; + return true; +}; + +export fn intern(l: *lnk, name: str) *lsym = { + let s: *lsym = l.syms; + for (s != nil) { + if (streq(s.name, name)) { return s; }; + s = s.snext; + }; + let n: *lsym = alloc(lsym { name = name, snext = l.syms })!; + l.syms = n; + return n; +}; + +export fn lookup(l: *lnk, name: str) *lsym = { + let s: *lsym = l.syms; + for (s != nil) { + if (streq(s.name, name)) { return s; }; + s = s.snext; + }; + return nil; +}; + // selfhost/cmd/w6l/obj.ww — port of cmd/w6l/obj.c. // // Loads relocatable ELF64 .o files emitted by w6a, appends .text to @@ -5050,6 +5050,7 @@ package main; import os; import rt; +import strings; import sym; import obj; import dyn; @@ -5146,23 +5147,12 @@ fn islinkable(path: *u8) bool = { let n: i64 = os.read(fd, &mp[0], 8u64); os.close(fd); if (n < 4i64) { return false; }; - // archive: "!\n" - if (n >= 8i64) { - if (mp[0u64] == '!') { if (mp[1u64] == '<') { - if (mp[2u64] == 'a') { if (mp[3u64] == 'r') { - if (mp[4u64] == 'c') { if (mp[5u64] == 'h') { - if (mp[6u64] == '>') { if (mp[7u64] == '\n') { - return true; - }; }; }; }; }; }; }; }; - }; - // ELF: "\x7fELF" - if (mp[0u64] == 127u8) { - if (mp[1u64] == 'E') { - if (mp[2u64] == 'L') { - if (mp[3u64] == 'F') { return true; }; - }; - }; - }; + // hasprefix length-bails when the header is shorter than the magic, + // matching the old n>=8 archive guard (str len < 8 => no match). + let hn: i32 = n: i32; + let hdr: str = strings.frombytes(mp[0:hn]); + if (strings.hasprefix(hdr, "!\n")) { return true; }; + if (strings.hasprefix(hdr, "\x7fELF")) { return true; }; return false; }; @@ -5216,10 +5206,8 @@ fn isso(path: *u8) i32 = { let n: i64 = os.read(fd, &mp[0], 20u64); os.close(fd); if (n < 20i64) { return 0; }; - if (mp[0u64] != 127u8) { return 0; }; - if (mp[1u64] != 'E') { return 0; }; - if (mp[2u64] != 'L') { return 0; }; - if (mp[3u64] != 'F') { return 0; }; + let hdr: str = strings.frombytes(mp[0:4]); + if (!strings.hasprefix(hdr, "\x7fELF")) { return 0; }; // e_type at offset 16, u16 little-endian let t: u16 = (mp[16u64]: u16) | ((mp[17u64]: u16) << 8u16); if (t == 3u16) { return 1; }; diff --git a/selfhost/cmd/w6l/main.ww b/selfhost/cmd/w6l/main.ww index f74d021a..00320423 100644 --- a/selfhost/cmd/w6l/main.ww +++ b/selfhost/cmd/w6l/main.ww @@ -10,6 +10,7 @@ package main; import os; import rt; +import strings; import sym; import obj; import dyn; @@ -106,23 +107,12 @@ fn islinkable(path: *u8) bool = { let n: i64 = os.read(fd, &mp[0], 8u64); os.close(fd); if (n < 4i64) { return false; }; - // archive: "!\n" - if (n >= 8i64) { - if (mp[0u64] == '!') { if (mp[1u64] == '<') { - if (mp[2u64] == 'a') { if (mp[3u64] == 'r') { - if (mp[4u64] == 'c') { if (mp[5u64] == 'h') { - if (mp[6u64] == '>') { if (mp[7u64] == '\n') { - return true; - }; }; }; }; }; }; }; }; - }; - // ELF: "\x7fELF" - if (mp[0u64] == 127u8) { - if (mp[1u64] == 'E') { - if (mp[2u64] == 'L') { - if (mp[3u64] == 'F') { return true; }; - }; - }; - }; + // hasprefix length-bails when the header is shorter than the magic, + // matching the old n>=8 archive guard (str len < 8 => no match). + let hn: i32 = n: i32; + let hdr: str = strings.frombytes(mp[0:hn]); + if (strings.hasprefix(hdr, "!\n")) { return true; }; + if (strings.hasprefix(hdr, "\x7fELF")) { return true; }; return false; }; @@ -176,10 +166,8 @@ fn isso(path: *u8) i32 = { let n: i64 = os.read(fd, &mp[0], 20u64); os.close(fd); if (n < 20i64) { return 0; }; - if (mp[0u64] != 127u8) { return 0; }; - if (mp[1u64] != 'E') { return 0; }; - if (mp[2u64] != 'L') { return 0; }; - if (mp[3u64] != 'F') { return 0; }; + let hdr: str = strings.frombytes(mp[0:4]); + if (!strings.hasprefix(hdr, "\x7fELF")) { return 0; }; // e_type at offset 16, u16 little-endian let t: u16 = (mp[16u64]: u16) | ((mp[17u64]: u16) << 8u16); if (t == 3u16) { return 1; };