selfhost/cmd/w6l: drop l_* prefix from exports + snake fields/helpers
This commit is contained in:
@@ -123,8 +123,8 @@ fn d_basename(p: *u8) *u8 = {
|
||||
|
||||
// ---- file slurp --------------------------------------------------------
|
||||
|
||||
fn read_all_so(path_cs: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path_cs, os.O_RDONLY, 0i32);
|
||||
fn readallso(path: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
|
||||
if (fd < 0) { return nil, 0u64; };
|
||||
let n: i64 = os.filesize(fd);
|
||||
if (n < 0i64) { os.close(fd); return nil, 0u64; };
|
||||
@@ -137,11 +137,11 @@ fn read_all_so(path_cs: *u8) (*u8, u64) = {
|
||||
|
||||
// ---- verdef helpers ----------------------------------------------------
|
||||
|
||||
// vd_name_at — walk verdef records and return the name (as *u8 into
|
||||
// vdnameat — walk verdef records and return the name (as *u8 into
|
||||
// the .so's verstr buffer) for the entry whose vd_ndx == ndx. The name
|
||||
// is the first Verdaux's vda_name (subsequent auxes are predecessor
|
||||
// names). Returns nil if no entry matches.
|
||||
fn vd_name_at(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
fn vdnameat(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
verstr: *u8, ndx: u16) *u8 = {
|
||||
let off: u64 = 0u64;
|
||||
for (off < verdef_size) {
|
||||
@@ -162,10 +162,10 @@ fn vd_name_at(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
|
||||
// ---- entry points ------------------------------------------------------
|
||||
|
||||
export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
export fn loadso(l: *lnk, path: *u8) i32 = {
|
||||
let buf: *u8;
|
||||
let blen: u64;
|
||||
buf, blen = read_all_so(path_cs);
|
||||
buf, blen = readallso(path);
|
||||
if (buf == nil) {
|
||||
os.write(2, "w6l: cannot read .so\n".ptr, 20u64);
|
||||
return -1;
|
||||
@@ -174,22 +174,22 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
os.write(2, "w6l: short ELF\n".ptr, 14u64);
|
||||
return -1;
|
||||
};
|
||||
if (buf[0u64] != 127u8) { return so_err("not ELF"); };
|
||||
if (buf[1u64] != 69u8) { return so_err("not ELF"); };
|
||||
if (buf[2u64] != 76u8) { return so_err("not ELF"); };
|
||||
if (buf[3u64] != 70u8) { return so_err("not ELF"); };
|
||||
if (buf[4u64] != 2u8) { return so_err("not ELFCLASS64"); };
|
||||
if (buf[0u64] != 127u8) { return soerr("not ELF"); };
|
||||
if (buf[1u64] != 69u8) { return soerr("not ELF"); };
|
||||
if (buf[2u64] != 76u8) { return soerr("not ELF"); };
|
||||
if (buf[3u64] != 70u8) { return soerr("not ELF"); };
|
||||
if (buf[4u64] != 2u8) { return soerr("not ELFCLASS64"); };
|
||||
if (d_u16(buf, EH_EMACHINE) != EM_X86_64_SO) {
|
||||
return so_err("not amd64");
|
||||
return soerr("not amd64");
|
||||
};
|
||||
if (d_u16(buf, EH_ETYPE) != ET_DYN_SO) {
|
||||
return so_err("not ET_DYN");
|
||||
return soerr("not ET_DYN");
|
||||
};
|
||||
|
||||
let shoff: u64 = d_u64(buf, EH_SHOFF);
|
||||
let shnum: u32 = d_u16(buf, EH_SHNUM): u32;
|
||||
if (shoff == 0u64) { return so_err("stripped .so unsupported"); };
|
||||
if (shnum == 0u32) { return so_err("stripped .so unsupported"); };
|
||||
if (shoff == 0u64) { return soerr("stripped .so unsupported"); };
|
||||
if (shnum == 0u32) { return soerr("stripped .so unsupported"); };
|
||||
|
||||
// Locate the four sections we care about.
|
||||
let idx_dynsym: i32 = -1;
|
||||
@@ -207,7 +207,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
i += 1u32;
|
||||
};
|
||||
if (idx_dynsym < 0) {
|
||||
return so_err("no .dynsym");
|
||||
return soerr("no .dynsym");
|
||||
};
|
||||
|
||||
let dynsym_sh: u64 = shoff + (idx_dynsym: u64) * SH_SIZE;
|
||||
@@ -250,7 +250,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
};
|
||||
};
|
||||
if (soname_cs == nil) {
|
||||
soname_cs = d_basename(path_cs);
|
||||
soname_cs = d_basename(path);
|
||||
};
|
||||
|
||||
// Versym is one u16 per dynsym entry.
|
||||
@@ -277,9 +277,9 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
};
|
||||
|
||||
// Build the lso. Exports are appended in dynsym order so
|
||||
// l_so_provides_v's first-match semantics match the C version.
|
||||
// soprovides_v's first-match semantics match the C version.
|
||||
let so: *lso = amalloc(l.a, 64u64): *lso;
|
||||
so.path = d_cstr_to_str(l.a, path_cs);
|
||||
so.path = d_cstr_to_str(l.a, path);
|
||||
so.soname = d_cstr_to_str(l.a, soname_cs);
|
||||
so.exports = nil;
|
||||
let tail: *lexport = nil;
|
||||
@@ -325,7 +325,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
vername_cs = nil;
|
||||
} else {
|
||||
if (verstr != nil) {
|
||||
let nm: *u8 = vd_name_at(buf, verdef_off, verdef_size, verstr, vidx);
|
||||
let nm: *u8 = vdnameat(buf, verdef_off, verdef_size, verstr, vidx);
|
||||
vername_cs = nm;
|
||||
};
|
||||
}; }; };
|
||||
@@ -358,34 +358,34 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn so_err(msg: str) i32 = {
|
||||
fn soerr(msg: str) i32 = {
|
||||
os.write(2, "w6l: ".ptr, 4u64);
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
return -1;
|
||||
};
|
||||
|
||||
// l_so_provides — 1 if so exports name, 0 otherwise.
|
||||
export fn l_so_provides(so: *lso, name: str) i32 = {
|
||||
// soprovides — 1 if so exports name, 0 otherwise.
|
||||
export fn soprovides(so: *lso, name: str) i32 = {
|
||||
if (so == nil) { return 0; };
|
||||
let e: *lexport = so.exports;
|
||||
for (e != nil) {
|
||||
if (s_eq(e.name, name)) { return 1; };
|
||||
if (streq(e.name, name)) { return 1; };
|
||||
e = e.enext;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// l_so_version — the version of so's export named `name`, or an empty
|
||||
// soversion — the version of so's export named `name`, or an empty
|
||||
// str (ptr=nil, len=0) if the export is unversioned or not present.
|
||||
export fn l_so_version(so: *lso, name: str) str = {
|
||||
export fn soversion(so: *lso, name: str) str = {
|
||||
let result: str;
|
||||
result.ptr = nil;
|
||||
result.len = 0i32;
|
||||
if (so == nil) { return result; };
|
||||
let e: *lexport = so.exports;
|
||||
for (e != nil) {
|
||||
if (s_eq(e.name, name)) {
|
||||
if (streq(e.name, name)) {
|
||||
result.ptr = e.version.ptr;
|
||||
result.len = e.version.len;
|
||||
return result;
|
||||
@@ -395,12 +395,4 @@ export fn l_so_version(so: *lso, name: str) str = {
|
||||
return result;
|
||||
};
|
||||
|
||||
fn s_eq(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;
|
||||
};
|
||||
// `streq` lives in sym.ww — same bundle, single definition.
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
// [dynamic_off] .dynamic (writable; covered by PT_DYNAMIC)
|
||||
//
|
||||
// cgen workaround notes:
|
||||
// - Two-level field assignments through pointers (e.g. r.sym.is_dyn = 1)
|
||||
// - Two-level field assignments through pointers (e.g. r.sym.isdyn = 1)
|
||||
// silently fail in the current wwstage cgen. We bind the inner
|
||||
// pointer to a local first: `let sym = r.sym; sym.is_dyn = 1;`
|
||||
// - Tuple returns of (i32, str) drop the str payload. l_so_version
|
||||
// pointer to a local first: `let sym = r.sym; sym.isdyn = 1;`
|
||||
// - Tuple returns of (i32, str) drop the str payload. soversion
|
||||
// returns a plain str instead.
|
||||
// - `def NAME: str = "...";` produces a bogus str (len ends up adding
|
||||
// to an offset accumulator). We use a fn returning the literal.
|
||||
@@ -175,19 +175,19 @@ fn str_eq_d(a: str, b: str) bool = {
|
||||
|
||||
// ---- main entry --------------------------------------------------------
|
||||
|
||||
export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let a: *arena = l.a;
|
||||
let n: i32 = l.dyn_n;
|
||||
let n: i32 = l.dynn;
|
||||
let nu: u64 = n: u64;
|
||||
|
||||
// ---- collect dyn syms into a plt_idx-indexed array ----
|
||||
let dynsyms: **lsym = amalloc(a, nu * 8u64): **lsym;
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (s.is_dyn != 0) {
|
||||
if (s.plt_idx >= 0) {
|
||||
if (s.plt_idx < n) {
|
||||
dynsyms[s.plt_idx] = s;
|
||||
if (s.isdyn != 0) {
|
||||
if (s.pltidx >= 0) {
|
||||
if (s.pltidx < n) {
|
||||
dynsyms[s.pltidx] = s;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -214,7 +214,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let j: i32 = 0;
|
||||
for (j < n) {
|
||||
let dsm: *lsym = dynsyms[j];
|
||||
let dl: *lso = dsm.dyn_lib;
|
||||
let dl: *lso = dsm.dynlib;
|
||||
if (dl == so) { used = 1; j = n; }
|
||||
else { j += 1; };
|
||||
};
|
||||
@@ -254,10 +254,10 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let j: i32 = 0;
|
||||
for (j < n) {
|
||||
let dsm: *lsym = dynsyms[j];
|
||||
let dl: *lso = dsm.dyn_lib;
|
||||
let dl: *lso = dsm.dynlib;
|
||||
if (dl == cur_so) {
|
||||
let nm0: str = dsm.name;
|
||||
let dv: str = l_so_version(cur_so, nm0);
|
||||
let dv: str = soversion(cur_so, nm0);
|
||||
if (dv.len > 0) {
|
||||
has = 1; j = n;
|
||||
} else { j += 1; };
|
||||
@@ -270,10 +270,10 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let jj: i32 = 0;
|
||||
for (jj < n) {
|
||||
let dsm2: *lsym = dynsyms[jj];
|
||||
let dl2: *lso = dsm2.dyn_lib;
|
||||
let dl2: *lso = dsm2.dynlib;
|
||||
if (dl2 == cur_so) {
|
||||
let nm2: str = dsm2.name;
|
||||
let vname: str = l_so_version(cur_so, nm2);
|
||||
let vname: str = soversion(cur_so, nm2);
|
||||
if (vname.len > 0) {
|
||||
let seen: i32 = 0;
|
||||
let k: i32 = 0;
|
||||
@@ -387,9 +387,9 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
pi = 0;
|
||||
for (pi < n) {
|
||||
let dsm3: *lsym = dynsyms[pi];
|
||||
let dl3: *lso = dsm3.dyn_lib;
|
||||
let dl3: *lso = dsm3.dynlib;
|
||||
let nm3: str = dsm3.name;
|
||||
let vname: str = l_so_version(dl3, nm3);
|
||||
let vname: str = soversion(dl3, nm3);
|
||||
if (vname.len == 0) {
|
||||
d_wr16(versym_for, (pi: u64) * 2u64, VER_NDX_GLOBAL_D);
|
||||
} else {
|
||||
@@ -630,7 +630,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
for (r != nil) {
|
||||
if (r.sym != nil) {
|
||||
let rsym: *lsym = r.sym;
|
||||
if (rsym.is_dyn != 0) {
|
||||
if (rsym.isdyn != 0) {
|
||||
if (r.kind != R_X86_64_PC32_D) {
|
||||
if (r.kind != R_X86_64_PLT32_D) {
|
||||
os.write(2, "w6l: dynamic reloc kind unsupported\n".ptr, 35u64);
|
||||
@@ -638,7 +638,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
};
|
||||
};
|
||||
let site: u64 = text_va + r.off;
|
||||
let stub: u64 = plt_va + (rsym.plt_idx: u64) * PLT_STUB_BYTES_D;
|
||||
let stub: u64 = plt_va + (rsym.pltidx: u64) * PLT_STUB_BYTES_D;
|
||||
let disp: i64 = (stub: i64) - (site: i64) + r.addend;
|
||||
d_wr32(l.text, r.off, (disp: i32): u32);
|
||||
};
|
||||
|
||||
@@ -326,15 +326,15 @@ type lsym = struct {
|
||||
val: u64, // offset within combined .text once linked
|
||||
defined: i32, // 1 if some lobj defines this symbol
|
||||
owner: *lobj,
|
||||
idx_in_owner: i32,
|
||||
// Dynamic-linking fields. Set by l_resolve when an undefined sym
|
||||
// is provided by some loaded lso. plt_idx and dynsym_idx default
|
||||
// to -1 (set explicitly by l_resolve, not by amalloc-zeroing).
|
||||
is_dyn: i32,
|
||||
dyn_lib: *lso,
|
||||
dyn_version: str, // matched export's version; len 0 if none
|
||||
plt_idx: i32,
|
||||
dynsym_idx: i32,
|
||||
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, not by amalloc-zeroing).
|
||||
isdyn: i32,
|
||||
dynlib: *lso,
|
||||
dynversion: str, // matched export's version; len 0 if none
|
||||
pltidx: i32,
|
||||
dynsymidx: i32,
|
||||
snext: *lsym,
|
||||
};
|
||||
|
||||
@@ -350,14 +350,14 @@ type lobj = struct {
|
||||
path: str,
|
||||
buf: *u8, // object bytes
|
||||
len: u64,
|
||||
text_off: u64, // offset of .text in combined output
|
||||
text_size: u64,
|
||||
textoff: u64, // offset of .text in combined output
|
||||
textsize: u64,
|
||||
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
|
||||
// l_so_provides_v's first-match semantics agree with the C version.
|
||||
// soprovides_v's first-match semantics agree with the C version.
|
||||
type lexport = struct {
|
||||
name: str,
|
||||
version: str, // len 0 for unversioned globals
|
||||
@@ -381,7 +381,7 @@ type lnk = struct {
|
||||
textcap: u64,
|
||||
textlen: u64,
|
||||
errs: i32,
|
||||
dyn_n: i32, // number of syms routed through PLT
|
||||
dynn: i32, // number of syms routed through PLT
|
||||
};
|
||||
|
||||
fn streq(a: str, b: str) bool = {
|
||||
@@ -394,7 +394,7 @@ fn streq(a: str, b: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn l_intern(l: *lnk, name: str) *lsym = {
|
||||
export fn intern(l: *lnk, name: str) *lsym = {
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (streq(s.name, name)) { return s; };
|
||||
@@ -407,7 +407,7 @@ export fn l_intern(l: *lnk, name: str) *lsym = {
|
||||
return n;
|
||||
};
|
||||
|
||||
export fn l_lookup(l: *lnk, name: str) *lsym = {
|
||||
export fn lookup(l: *lnk, name: str) *lsym = {
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (streq(s.name, name)) { return s; };
|
||||
@@ -493,8 +493,8 @@ def RELA_ADDEND: u64 = 16u64;
|
||||
|
||||
// ---- file slurp --------------------------------------------------------
|
||||
|
||||
fn read_all(path_cs: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path_cs, os.O_RDONLY, 0i32);
|
||||
fn readall(path: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
|
||||
if (fd < 0) { return nil, 0u64; };
|
||||
let n: i64 = os.filesize(fd);
|
||||
if (n < 0i64) { os.close(fd); return nil, 0u64; };
|
||||
@@ -507,7 +507,7 @@ fn read_all(path_cs: *u8) (*u8, u64) = {
|
||||
|
||||
// ---- text buffer growth ------------------------------------------------
|
||||
|
||||
fn emit_text(l: *lnk, src: *u8, n: u64) void = {
|
||||
fn emittext(l: *lnk, src: *u8, n: u64) void = {
|
||||
if (l.textlen + n > l.textcap) {
|
||||
let nc: u64 = l.textcap;
|
||||
if (nc == 0u64) { nc = 4096u64; };
|
||||
@@ -540,7 +540,7 @@ fn cstrlen(p: *u8) u64 = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn cstr_eq(p: *u8, lit: str) bool = {
|
||||
fn cstreq(p: *u8, lit: str) bool = {
|
||||
let n: u64 = lit.len: u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
@@ -552,8 +552,8 @@ fn cstr_eq(p: *u8, lit: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Build a ww str from a NUL-terminated *u8 (for passing to l_intern).
|
||||
fn cstr_to_str(a: *arena, p: *u8) str = {
|
||||
// Build a ww str from a NUL-terminated *u8 (for passing to intern).
|
||||
fn cstrtostr(a: *arena, p: *u8) str = {
|
||||
let n: u64 = cstrlen(p);
|
||||
return astrndup(a, p, n);
|
||||
};
|
||||
@@ -577,7 +577,7 @@ type armember = struct {
|
||||
mnext: *armember,
|
||||
};
|
||||
|
||||
fn is_archive(p: *u8, len: u64) bool = {
|
||||
fn isarchive(p: *u8, len: u64) bool = {
|
||||
if (len < 8u64) { return false; };
|
||||
if (p[0u64] != 33u8) { return false; }; // '!'
|
||||
if (p[1u64] != 60u8) { return false; }; // '<'
|
||||
@@ -590,8 +590,8 @@ fn is_archive(p: *u8, len: u64) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// ar_field — parse a space-padded decimal integer of width n.
|
||||
fn ar_field(p: *u8, n: u64) u64 = {
|
||||
// arfield — parse a space-padded decimal integer of width n.
|
||||
fn arfield(p: *u8, n: u64) u64 = {
|
||||
let v: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
@@ -604,10 +604,10 @@ fn ar_field(p: *u8, n: u64) u64 = {
|
||||
return v;
|
||||
};
|
||||
|
||||
// elf_globals — return a linked list of names of globally-defined
|
||||
// elfglobals — return a linked list of names of globally-defined
|
||||
// (STB_GLOBAL) symbols whose section is `.text`. Names are arena
|
||||
// copies, so the source ELF buffer can be freed afterward.
|
||||
fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
fn elfglobals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
if (len < EHDR_SIZE) { return nil; };
|
||||
if (buf[0u64] != 127u8) { return nil; };
|
||||
if (buf[1u64] != 69u8) { return nil; };
|
||||
@@ -630,7 +630,7 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
let sh_name: u32 = rd_u32(buf, sh_off + SHDR_NAME);
|
||||
let nm: *u8 = shstr + (sh_name: u64);
|
||||
if (sh_type == SHT_PROGBITS: u32) {
|
||||
if (cstr_eq(nm, ".text")) { idx_text = i: i32; };
|
||||
if (cstreq(nm, ".text")) { idx_text = i: i32; };
|
||||
};
|
||||
if (sh_type == SHT_SYMTAB: u32) { idx_symtab = i: i32; };
|
||||
i += 1u32;
|
||||
@@ -662,7 +662,7 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
if ((st_shndx: i32) == idx_text) {
|
||||
let nm_p: *u8 = strtab + (st_name: u64);
|
||||
if (nm_p[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(a, nm_p);
|
||||
let nm: str = cstrtostr(a, nm_p);
|
||||
let de: *defent = amalloc(a, 32u64): *defent;
|
||||
de.name = nm;
|
||||
de.dnext = head;
|
||||
@@ -676,13 +676,13 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
return head;
|
||||
};
|
||||
|
||||
// member_defines_undef — true if any of m's defined globals matches a
|
||||
// memberdefinesundef — true if any of m's defined globals matches a
|
||||
// currently-undefined symbol in the linker's symbol table. Names not
|
||||
// already interned are uninteresting (the link doesn't need them yet).
|
||||
fn member_defines_undef(l: *lnk, m: *armember) bool = {
|
||||
fn memberdefinesundef(l: *lnk, m: *armember) bool = {
|
||||
let de: *defent = m.defs;
|
||||
for (de != nil) {
|
||||
let s: *lsym = l_lookup(l, de.name);
|
||||
let s: *lsym = lookup(l, de.name);
|
||||
if (s != nil) {
|
||||
if (s.defined == 0) { return true; };
|
||||
};
|
||||
@@ -691,17 +691,17 @@ fn member_defines_undef(l: *lnk, m: *armember) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// load_archive — port of cmd/w6l/obj.c:load_archive.
|
||||
// loadarchive — port of cmd/w6l/obj.c:load_archive.
|
||||
//
|
||||
// Pass 1 indexes every regular member. Pass 2 iteratively pulls in any
|
||||
// member that supplies a currently-undefined symbol; each pull may
|
||||
// introduce fresh undefs, so we loop until quiescent.
|
||||
fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
fn loadarchive(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
|
||||
let head: *armember = nil;
|
||||
let tail: *armember = nil;
|
||||
let pos: u64 = 8u64; // past "!<arch>\n"
|
||||
for (pos + 60u64 <= len) {
|
||||
let hdr_size: u64 = ar_field(buf + pos + 48u64, 10u64);
|
||||
let hdr_size: u64 = arfield(buf + pos + 48u64, 10u64);
|
||||
let hdr_end: u64 = pos + 60u64;
|
||||
if (hdr_end + hdr_size > len) { break; };
|
||||
let first: u8 = buf[pos];
|
||||
@@ -717,7 +717,7 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
i += 1u64;
|
||||
};
|
||||
m.data = mb;
|
||||
m.defs = elf_globals(l.a, mb, hdr_size);
|
||||
m.defs = elfglobals(l.a, mb, hdr_size);
|
||||
m.loaded = 0;
|
||||
m.mnext = nil;
|
||||
if (head == nil) { head = m; }
|
||||
@@ -734,8 +734,8 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let m: *armember = head;
|
||||
for (m != nil) {
|
||||
if (m.loaded == 0) {
|
||||
if (member_defines_undef(l, m)) {
|
||||
if (load_image(l, path_cs, m.data, m.size) == 0) {
|
||||
if (memberdefinesundef(l, m)) {
|
||||
if (loadimage(l, path, m.data, m.size) == 0) {
|
||||
m.loaded = 1;
|
||||
changed = 1;
|
||||
};
|
||||
@@ -749,21 +749,21 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
|
||||
// ---- main loader -------------------------------------------------------
|
||||
|
||||
export fn l_load(l: *lnk, path_cs: *u8) i32 = {
|
||||
export fn load(l: *lnk, path: *u8) i32 = {
|
||||
let bufp: *u8;
|
||||
let buflen: u64;
|
||||
bufp, buflen = read_all(path_cs);
|
||||
bufp, buflen = readall(path);
|
||||
if (bufp == nil) {
|
||||
os.write(2, "w6l: cannot read object\n".ptr, 23u64);
|
||||
return -1;
|
||||
};
|
||||
if (is_archive(bufp, buflen)) {
|
||||
return load_archive(l, path_cs, bufp, buflen);
|
||||
if (isarchive(bufp, buflen)) {
|
||||
return loadarchive(l, path, bufp, buflen);
|
||||
};
|
||||
return load_image(l, path_cs, bufp, buflen);
|
||||
return loadimage(l, path, bufp, buflen);
|
||||
};
|
||||
|
||||
fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
fn loadimage(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
|
||||
if (len < EHDR_SIZE) { return -1; };
|
||||
// magic: 0x7f, 'E', 'L', 'F'
|
||||
if (buf[0u64] != 127u8) { return -1; };
|
||||
@@ -792,11 +792,11 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let sh_name: u32 = rd_u32(buf, sh_off + SHDR_NAME);
|
||||
let nm: *u8 = shstr + (sh_name: u64);
|
||||
if (sh_type == SHT_PROGBITS: u32) {
|
||||
if (cstr_eq(nm, ".text")) { idx_text = i: i32; };
|
||||
if (cstreq(nm, ".text")) { idx_text = i: i32; };
|
||||
};
|
||||
if (sh_type == SHT_SYMTAB: u32) { idx_symtab = i: i32; };
|
||||
if (sh_type == SHT_RELA: u32) {
|
||||
if (cstr_eq(nm, ".rela.text")) { idx_rela = i: i32; };
|
||||
if (cstreq(nm, ".rela.text")) { idx_rela = i: i32; };
|
||||
};
|
||||
i += 1u32;
|
||||
};
|
||||
@@ -825,16 +825,16 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
|
||||
// Track this object.
|
||||
let ob: *lobj = amalloc(l.a, 64u64): *lobj;
|
||||
ob.path = cstr_to_str(l.a, path_cs);
|
||||
ob.path = cstrtostr(l.a, path);
|
||||
ob.buf = buf;
|
||||
ob.len = len;
|
||||
ob.text_off = l.textlen;
|
||||
ob.text_size = text_size;
|
||||
ob.textoff = l.textlen;
|
||||
ob.textsize = text_size;
|
||||
ob.onext = l.objs;
|
||||
l.objs = ob;
|
||||
|
||||
// Append .text bytes to the combined image.
|
||||
emit_text(l, buf + text_off, text_size);
|
||||
emittext(l, buf + text_off, text_size);
|
||||
|
||||
// Walk symbols. We don't keep a per-object map[] of *lsym. Instead
|
||||
// the reloc loop re-walks symtab and re-interns by name. Simpler
|
||||
@@ -847,8 +847,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let st_value: u64 = rd_u64(buf, sym_p + SYM_VALUE);
|
||||
let nm_p: *u8 = strtab + (st_name: u64);
|
||||
if (nm_p[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(l.a, nm_p);
|
||||
let gs: *lsym = l_intern(l, nm);
|
||||
let nm: str = cstrtostr(l.a, nm_p);
|
||||
let gs: *lsym = intern(l, nm);
|
||||
if (st_shndx != 0u16) {
|
||||
if ((st_shndx: i32) == idx_text) {
|
||||
if (gs.defined != 0) {
|
||||
@@ -857,8 +857,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
} else {
|
||||
gs.defined = 1;
|
||||
gs.owner = ob;
|
||||
gs.idx_in_owner = si: i32;
|
||||
gs.val = ob.text_off + st_value;
|
||||
gs.idxinowner = si: i32;
|
||||
gs.val = ob.textoff + st_value;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -881,7 +881,7 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let r_sym_idx: u32 = (r_info >> 32u64): u32;
|
||||
let r_kind: i32 = ((r_info & 4294967295u64): u32): i32;
|
||||
let nr: *lrel = amalloc(l.a, 48u64): *lrel;
|
||||
nr.off = ob.text_off + r_off;
|
||||
nr.off = ob.textoff + r_off;
|
||||
nr.kind = r_kind;
|
||||
nr.addend = r_addend: i64;
|
||||
// Look up the referenced sym by name (re-walk symtab).
|
||||
@@ -890,8 +890,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let s_name: u32 = rd_u32(buf, s_p + SYM_NAME);
|
||||
let s_nm: *u8 = strtab + (s_name: u64);
|
||||
if (s_nm[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(l.a, s_nm);
|
||||
nr.sym = l_intern(l, nm);
|
||||
let nm: str = cstrtostr(l.a, s_nm);
|
||||
nr.sym = intern(l, nm);
|
||||
};
|
||||
};
|
||||
nr.rnext = l.rels;
|
||||
@@ -1028,8 +1028,8 @@ fn d_basename(p: *u8) *u8 = {
|
||||
|
||||
// ---- file slurp --------------------------------------------------------
|
||||
|
||||
fn read_all_so(path_cs: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path_cs, os.O_RDONLY, 0i32);
|
||||
fn readallso(path: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
|
||||
if (fd < 0) { return nil, 0u64; };
|
||||
let n: i64 = os.filesize(fd);
|
||||
if (n < 0i64) { os.close(fd); return nil, 0u64; };
|
||||
@@ -1042,11 +1042,11 @@ fn read_all_so(path_cs: *u8) (*u8, u64) = {
|
||||
|
||||
// ---- verdef helpers ----------------------------------------------------
|
||||
|
||||
// vd_name_at — walk verdef records and return the name (as *u8 into
|
||||
// vdnameat — walk verdef records and return the name (as *u8 into
|
||||
// the .so's verstr buffer) for the entry whose vd_ndx == ndx. The name
|
||||
// is the first Verdaux's vda_name (subsequent auxes are predecessor
|
||||
// names). Returns nil if no entry matches.
|
||||
fn vd_name_at(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
fn vdnameat(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
verstr: *u8, ndx: u16) *u8 = {
|
||||
let off: u64 = 0u64;
|
||||
for (off < verdef_size) {
|
||||
@@ -1067,10 +1067,10 @@ fn vd_name_at(buf: *u8, verdef_off: u64, verdef_size: u64,
|
||||
|
||||
// ---- entry points ------------------------------------------------------
|
||||
|
||||
export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
export fn loadso(l: *lnk, path: *u8) i32 = {
|
||||
let buf: *u8;
|
||||
let blen: u64;
|
||||
buf, blen = read_all_so(path_cs);
|
||||
buf, blen = readallso(path);
|
||||
if (buf == nil) {
|
||||
os.write(2, "w6l: cannot read .so\n".ptr, 20u64);
|
||||
return -1;
|
||||
@@ -1079,22 +1079,22 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
os.write(2, "w6l: short ELF\n".ptr, 14u64);
|
||||
return -1;
|
||||
};
|
||||
if (buf[0u64] != 127u8) { return so_err("not ELF"); };
|
||||
if (buf[1u64] != 69u8) { return so_err("not ELF"); };
|
||||
if (buf[2u64] != 76u8) { return so_err("not ELF"); };
|
||||
if (buf[3u64] != 70u8) { return so_err("not ELF"); };
|
||||
if (buf[4u64] != 2u8) { return so_err("not ELFCLASS64"); };
|
||||
if (buf[0u64] != 127u8) { return soerr("not ELF"); };
|
||||
if (buf[1u64] != 69u8) { return soerr("not ELF"); };
|
||||
if (buf[2u64] != 76u8) { return soerr("not ELF"); };
|
||||
if (buf[3u64] != 70u8) { return soerr("not ELF"); };
|
||||
if (buf[4u64] != 2u8) { return soerr("not ELFCLASS64"); };
|
||||
if (d_u16(buf, EH_EMACHINE) != EM_X86_64_SO) {
|
||||
return so_err("not amd64");
|
||||
return soerr("not amd64");
|
||||
};
|
||||
if (d_u16(buf, EH_ETYPE) != ET_DYN_SO) {
|
||||
return so_err("not ET_DYN");
|
||||
return soerr("not ET_DYN");
|
||||
};
|
||||
|
||||
let shoff: u64 = d_u64(buf, EH_SHOFF);
|
||||
let shnum: u32 = d_u16(buf, EH_SHNUM): u32;
|
||||
if (shoff == 0u64) { return so_err("stripped .so unsupported"); };
|
||||
if (shnum == 0u32) { return so_err("stripped .so unsupported"); };
|
||||
if (shoff == 0u64) { return soerr("stripped .so unsupported"); };
|
||||
if (shnum == 0u32) { return soerr("stripped .so unsupported"); };
|
||||
|
||||
// Locate the four sections we care about.
|
||||
let idx_dynsym: i32 = -1;
|
||||
@@ -1112,7 +1112,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
i += 1u32;
|
||||
};
|
||||
if (idx_dynsym < 0) {
|
||||
return so_err("no .dynsym");
|
||||
return soerr("no .dynsym");
|
||||
};
|
||||
|
||||
let dynsym_sh: u64 = shoff + (idx_dynsym: u64) * SH_SIZE;
|
||||
@@ -1155,7 +1155,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
};
|
||||
};
|
||||
if (soname_cs == nil) {
|
||||
soname_cs = d_basename(path_cs);
|
||||
soname_cs = d_basename(path);
|
||||
};
|
||||
|
||||
// Versym is one u16 per dynsym entry.
|
||||
@@ -1182,9 +1182,9 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
};
|
||||
|
||||
// Build the lso. Exports are appended in dynsym order so
|
||||
// l_so_provides_v's first-match semantics match the C version.
|
||||
// soprovides_v's first-match semantics match the C version.
|
||||
let so: *lso = amalloc(l.a, 64u64): *lso;
|
||||
so.path = d_cstr_to_str(l.a, path_cs);
|
||||
so.path = d_cstr_to_str(l.a, path);
|
||||
so.soname = d_cstr_to_str(l.a, soname_cs);
|
||||
so.exports = nil;
|
||||
let tail: *lexport = nil;
|
||||
@@ -1230,7 +1230,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
vername_cs = nil;
|
||||
} else {
|
||||
if (verstr != nil) {
|
||||
let nm: *u8 = vd_name_at(buf, verdef_off, verdef_size, verstr, vidx);
|
||||
let nm: *u8 = vdnameat(buf, verdef_off, verdef_size, verstr, vidx);
|
||||
vername_cs = nm;
|
||||
};
|
||||
}; }; };
|
||||
@@ -1263,34 +1263,34 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn so_err(msg: str) i32 = {
|
||||
fn soerr(msg: str) i32 = {
|
||||
os.write(2, "w6l: ".ptr, 4u64);
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.write(2, "\n".ptr, 1u64);
|
||||
return -1;
|
||||
};
|
||||
|
||||
// l_so_provides — 1 if so exports name, 0 otherwise.
|
||||
export fn l_so_provides(so: *lso, name: str) i32 = {
|
||||
// soprovides — 1 if so exports name, 0 otherwise.
|
||||
export fn soprovides(so: *lso, name: str) i32 = {
|
||||
if (so == nil) { return 0; };
|
||||
let e: *lexport = so.exports;
|
||||
for (e != nil) {
|
||||
if (s_eq(e.name, name)) { return 1; };
|
||||
if (streq(e.name, name)) { return 1; };
|
||||
e = e.enext;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// l_so_version — the version of so's export named `name`, or an empty
|
||||
// soversion — the version of so's export named `name`, or an empty
|
||||
// str (ptr=nil, len=0) if the export is unversioned or not present.
|
||||
export fn l_so_version(so: *lso, name: str) str = {
|
||||
export fn soversion(so: *lso, name: str) str = {
|
||||
let result: str;
|
||||
result.ptr = nil;
|
||||
result.len = 0i32;
|
||||
if (so == nil) { return result; };
|
||||
let e: *lexport = so.exports;
|
||||
for (e != nil) {
|
||||
if (s_eq(e.name, name)) {
|
||||
if (streq(e.name, name)) {
|
||||
result.ptr = e.version.ptr;
|
||||
result.len = e.version.len;
|
||||
return result;
|
||||
@@ -1300,15 +1300,7 @@ export fn l_so_version(so: *lso, name: str) str = {
|
||||
return result;
|
||||
};
|
||||
|
||||
fn s_eq(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;
|
||||
};
|
||||
// `streq` lives in sym.ww — same bundle, single definition.
|
||||
|
||||
// selfhost/cmd/w6l/pass.ww — port of cmd/w6l/pass.c.
|
||||
//
|
||||
@@ -1329,14 +1321,14 @@ use dyn;
|
||||
def R_X86_64_PC32: i32 = 2;
|
||||
def R_X86_64_PLT32: i32 = 4;
|
||||
|
||||
export fn l_resolve(l: *lnk) i32 = {
|
||||
export fn resolve(l: *lnk) i32 = {
|
||||
// Initialise dynamic-linking sentinels. amalloc zeroes, so
|
||||
// is_dyn/dyn_lib start clean — but plt_idx and dynsym_idx
|
||||
// isdyn/dynlib start clean — but pltidx and dynsymidx
|
||||
// must be -1, not 0.
|
||||
let si: *lsym = l.syms;
|
||||
for (si != nil) {
|
||||
si.plt_idx = -1;
|
||||
si.dynsym_idx = -1;
|
||||
si.pltidx = -1;
|
||||
si.dynsymidx = -1;
|
||||
si = si.snext;
|
||||
};
|
||||
|
||||
@@ -1348,14 +1340,14 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
if (r.sym != nil) {
|
||||
if (r.sym.defined == 0) {
|
||||
let sym: *lsym = r.sym;
|
||||
if (sym.is_dyn == 0) {
|
||||
if (sym.isdyn == 0) {
|
||||
let so: *lso = l.sos;
|
||||
for (so != nil) {
|
||||
if (l_so_provides(so, sym.name) != 0) {
|
||||
sym.is_dyn = 1;
|
||||
sym.dyn_lib = so;
|
||||
sym.plt_idx = l.dyn_n;
|
||||
l.dyn_n += 1;
|
||||
if (soprovides(so, sym.name) != 0) {
|
||||
sym.isdyn = 1;
|
||||
sym.dynlib = so;
|
||||
sym.pltidx = l.dynn;
|
||||
l.dynn += 1;
|
||||
so = nil; // break
|
||||
} else {
|
||||
so = so.sonext;
|
||||
@@ -1372,7 +1364,7 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
for (r2 != nil) {
|
||||
if (r2.sym != nil) {
|
||||
if (r2.sym.defined == 0) {
|
||||
if (r2.sym.is_dyn == 0) {
|
||||
if (r2.sym.isdyn == 0) {
|
||||
os.write(2, "w6l: undefined reference to '".ptr, 28u64);
|
||||
let nm: str = r2.sym.name;
|
||||
os.write(2, nm.ptr, nm.len: u64);
|
||||
@@ -1386,20 +1378,20 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
return l.errs;
|
||||
};
|
||||
|
||||
fn patch_u32(p: *u8, v: u32) void = {
|
||||
fn patchu32(p: *u8, v: u32) void = {
|
||||
p[0] = (v & 255u32): u8;
|
||||
p[1] = ((v >> 8u32) & 255u32): u8;
|
||||
p[2] = ((v >> 16u32) & 255u32): u8;
|
||||
p[3] = ((v >> 24u32) & 255u32): u8;
|
||||
};
|
||||
|
||||
export fn l_relocate(l: *lnk, base: u64) i32 = {
|
||||
export fn relocate(l: *lnk, base: u64) i32 = {
|
||||
let r: *lrel = l.rels;
|
||||
for (r != nil) {
|
||||
if (r.sym != nil) {
|
||||
// Dynamic refs are patched later in dynout once the
|
||||
// PLT vaddr is known.
|
||||
if (r.sym.is_dyn != 0) {
|
||||
if (r.sym.isdyn != 0) {
|
||||
r = r.rnext;
|
||||
continue;
|
||||
};
|
||||
@@ -1409,12 +1401,12 @@ export fn l_relocate(l: *lnk, base: u64) i32 = {
|
||||
let site: u64 = base + r.off;
|
||||
let target: i64 = (base + r.sym.val): i64;
|
||||
let rel: i64 = (target - site: i64) + r.addend;
|
||||
patch_u32(l.text + r.off, rel: u32);
|
||||
patchu32(l.text + r.off, rel: u32);
|
||||
} else { if (k == R_X86_64_PLT32) {
|
||||
let site: u64 = base + r.off;
|
||||
let target: i64 = (base + r.sym.val): i64;
|
||||
let rel: i64 = (target - site: i64) + r.addend;
|
||||
patch_u32(l.text + r.off, rel: u32);
|
||||
patchu32(l.text + r.off, rel: u32);
|
||||
} else {
|
||||
os.write(2, "w6l: unsupported reloc kind\n".ptr, 27u64);
|
||||
l.errs += 1;
|
||||
@@ -1451,10 +1443,10 @@ export fn l_relocate(l: *lnk, base: u64) i32 = {
|
||||
// [dynamic_off] .dynamic (writable; covered by PT_DYNAMIC)
|
||||
//
|
||||
// cgen workaround notes:
|
||||
// - Two-level field assignments through pointers (e.g. r.sym.is_dyn = 1)
|
||||
// - Two-level field assignments through pointers (e.g. r.sym.isdyn = 1)
|
||||
// silently fail in the current wwstage cgen. We bind the inner
|
||||
// pointer to a local first: `let sym = r.sym; sym.is_dyn = 1;`
|
||||
// - Tuple returns of (i32, str) drop the str payload. l_so_version
|
||||
// pointer to a local first: `let sym = r.sym; sym.isdyn = 1;`
|
||||
// - Tuple returns of (i32, str) drop the str payload. soversion
|
||||
// returns a plain str instead.
|
||||
// - `def NAME: str = "...";` produces a bogus str (len ends up adding
|
||||
// to an offset accumulator). We use a fn returning the literal.
|
||||
@@ -1603,19 +1595,19 @@ fn str_eq_d(a: str, b: str) bool = {
|
||||
|
||||
// ---- main entry --------------------------------------------------------
|
||||
|
||||
export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let a: *arena = l.a;
|
||||
let n: i32 = l.dyn_n;
|
||||
let n: i32 = l.dynn;
|
||||
let nu: u64 = n: u64;
|
||||
|
||||
// ---- collect dyn syms into a plt_idx-indexed array ----
|
||||
let dynsyms: **lsym = amalloc(a, nu * 8u64): **lsym;
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (s.is_dyn != 0) {
|
||||
if (s.plt_idx >= 0) {
|
||||
if (s.plt_idx < n) {
|
||||
dynsyms[s.plt_idx] = s;
|
||||
if (s.isdyn != 0) {
|
||||
if (s.pltidx >= 0) {
|
||||
if (s.pltidx < n) {
|
||||
dynsyms[s.pltidx] = s;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1642,7 +1634,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let j: i32 = 0;
|
||||
for (j < n) {
|
||||
let dsm: *lsym = dynsyms[j];
|
||||
let dl: *lso = dsm.dyn_lib;
|
||||
let dl: *lso = dsm.dynlib;
|
||||
if (dl == so) { used = 1; j = n; }
|
||||
else { j += 1; };
|
||||
};
|
||||
@@ -1682,10 +1674,10 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let j: i32 = 0;
|
||||
for (j < n) {
|
||||
let dsm: *lsym = dynsyms[j];
|
||||
let dl: *lso = dsm.dyn_lib;
|
||||
let dl: *lso = dsm.dynlib;
|
||||
if (dl == cur_so) {
|
||||
let nm0: str = dsm.name;
|
||||
let dv: str = l_so_version(cur_so, nm0);
|
||||
let dv: str = soversion(cur_so, nm0);
|
||||
if (dv.len > 0) {
|
||||
has = 1; j = n;
|
||||
} else { j += 1; };
|
||||
@@ -1698,10 +1690,10 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
let jj: i32 = 0;
|
||||
for (jj < n) {
|
||||
let dsm2: *lsym = dynsyms[jj];
|
||||
let dl2: *lso = dsm2.dyn_lib;
|
||||
let dl2: *lso = dsm2.dynlib;
|
||||
if (dl2 == cur_so) {
|
||||
let nm2: str = dsm2.name;
|
||||
let vname: str = l_so_version(cur_so, nm2);
|
||||
let vname: str = soversion(cur_so, nm2);
|
||||
if (vname.len > 0) {
|
||||
let seen: i32 = 0;
|
||||
let k: i32 = 0;
|
||||
@@ -1815,9 +1807,9 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
pi = 0;
|
||||
for (pi < n) {
|
||||
let dsm3: *lsym = dynsyms[pi];
|
||||
let dl3: *lso = dsm3.dyn_lib;
|
||||
let dl3: *lso = dsm3.dynlib;
|
||||
let nm3: str = dsm3.name;
|
||||
let vname: str = l_so_version(dl3, nm3);
|
||||
let vname: str = soversion(dl3, nm3);
|
||||
if (vname.len == 0) {
|
||||
d_wr16(versym_for, (pi: u64) * 2u64, VER_NDX_GLOBAL_D);
|
||||
} else {
|
||||
@@ -2058,7 +2050,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
for (r != nil) {
|
||||
if (r.sym != nil) {
|
||||
let rsym: *lsym = r.sym;
|
||||
if (rsym.is_dyn != 0) {
|
||||
if (rsym.isdyn != 0) {
|
||||
if (r.kind != R_X86_64_PC32_D) {
|
||||
if (r.kind != R_X86_64_PLT32_D) {
|
||||
os.write(2, "w6l: dynamic reloc kind unsupported\n".ptr, 35u64);
|
||||
@@ -2066,7 +2058,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
};
|
||||
};
|
||||
let site: u64 = text_va + r.off;
|
||||
let stub: u64 = plt_va + (rsym.plt_idx: u64) * PLT_STUB_BYTES_D;
|
||||
let stub: u64 = plt_va + (rsym.pltidx: u64) * PLT_STUB_BYTES_D;
|
||||
let disp: i64 = (stub: i64) - (site: i64) + r.addend;
|
||||
d_wr32(l.text, r.off, (disp: i32): u32);
|
||||
};
|
||||
@@ -2196,31 +2188,31 @@ def TEXT_OFF: u64 = 4096u64; // 0x1000
|
||||
|
||||
// ---- little-endian byte writers ----------------------------------------
|
||||
|
||||
fn wr_u16(buf: *u8, off: u64, v: u16) void = {
|
||||
fn wru16(buf: *u8, off: u64, v: u16) void = {
|
||||
buf[off] = (v & 255u16): u8;
|
||||
buf[off + 1u64] = ((v >> 8u16) & 255u16): u8;
|
||||
};
|
||||
|
||||
fn wr_u32(buf: *u8, off: u64, v: u32) void = {
|
||||
fn wru32(buf: *u8, off: u64, v: u32) void = {
|
||||
buf[off] = (v & 255u32): u8;
|
||||
buf[off + 1u64] = ((v >> 8u32) & 255u32): u8;
|
||||
buf[off + 2u64] = ((v >> 16u32) & 255u32): u8;
|
||||
buf[off + 3u64] = ((v >> 24u32) & 255u32): u8;
|
||||
};
|
||||
|
||||
fn wr_u64(buf: *u8, off: u64, v: u64) void = {
|
||||
wr_u32(buf, off, (v & 4294967295u64): u32);
|
||||
wr_u32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32);
|
||||
fn wru64(buf: *u8, off: u64, v: u64) void = {
|
||||
wru32(buf, off, (v & 4294967295u64): u32);
|
||||
wru32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32);
|
||||
};
|
||||
|
||||
// ---- emit ---------------------------------------------------------------
|
||||
|
||||
export fn l_emit_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// Dispatch: any loaded shared object plus any dynamic ref means
|
||||
// we owe the loader a real PT_INTERP/PT_DYNAMIC binary.
|
||||
if (l.sos != nil) {
|
||||
if (l.dyn_n > 0) {
|
||||
return l_emit_dyn_elf(l, fd, base, entry);
|
||||
if (l.dynn > 0) {
|
||||
return emitdynelf(l, fd, base, entry);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2237,29 +2229,29 @@ export fn l_emit_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
hdr[4u64] = ELFCLASS64;
|
||||
hdr[5u64] = ELFDATA2LSB;
|
||||
hdr[6u64] = EV_CURRENT: u8;
|
||||
wr_u16(hdr, 16u64, ET_EXEC); // e_type
|
||||
wr_u16(hdr, 18u64, EM_X86_64_W); // e_machine
|
||||
wr_u32(hdr, 20u64, EV_CURRENT); // e_version
|
||||
wr_u64(hdr, 24u64, entry); // e_entry
|
||||
wr_u64(hdr, 32u64, 64u64); // e_phoff = sizeof(Ehdr)
|
||||
wr_u64(hdr, 40u64, 0u64); // e_shoff
|
||||
wr_u32(hdr, 48u64, 0u32); // e_flags
|
||||
wr_u16(hdr, 52u64, 64u16); // e_ehsize
|
||||
wr_u16(hdr, 54u64, 56u16); // e_phentsize
|
||||
wr_u16(hdr, 56u64, 1u16); // e_phnum
|
||||
wr_u16(hdr, 58u64, 0u16); // e_shentsize
|
||||
wr_u16(hdr, 60u64, 0u16); // e_shnum
|
||||
wr_u16(hdr, 62u64, 0u16); // e_shstrndx
|
||||
wru16(hdr, 16u64, ET_EXEC); // e_type
|
||||
wru16(hdr, 18u64, EM_X86_64_W); // e_machine
|
||||
wru32(hdr, 20u64, EV_CURRENT); // e_version
|
||||
wru64(hdr, 24u64, entry); // e_entry
|
||||
wru64(hdr, 32u64, 64u64); // e_phoff = sizeof(Ehdr)
|
||||
wru64(hdr, 40u64, 0u64); // e_shoff
|
||||
wru32(hdr, 48u64, 0u32); // e_flags
|
||||
wru16(hdr, 52u64, 64u16); // e_ehsize
|
||||
wru16(hdr, 54u64, 56u16); // e_phentsize
|
||||
wru16(hdr, 56u64, 1u16); // e_phnum
|
||||
wru16(hdr, 58u64, 0u16); // e_shentsize
|
||||
wru16(hdr, 60u64, 0u16); // e_shnum
|
||||
wru16(hdr, 62u64, 0u16); // e_shstrndx
|
||||
|
||||
// --- Phdr (56 bytes) at offset 64 ---
|
||||
wr_u32(hdr, 64u64, PT_LOAD); // p_type
|
||||
wr_u32(hdr, 68u64, PF_R | PF_X); // p_flags
|
||||
wr_u64(hdr, 72u64, 0u64); // p_offset
|
||||
wr_u64(hdr, 80u64, base); // p_vaddr
|
||||
wr_u64(hdr, 88u64, base); // p_paddr
|
||||
wr_u64(hdr, 96u64, filesz); // p_filesz
|
||||
wr_u64(hdr, 104u64, filesz); // p_memsz
|
||||
wr_u64(hdr, 112u64, TEXT_OFF); // p_align
|
||||
wru32(hdr, 64u64, PT_LOAD); // p_type
|
||||
wru32(hdr, 68u64, PF_R | PF_X); // p_flags
|
||||
wru64(hdr, 72u64, 0u64); // p_offset
|
||||
wru64(hdr, 80u64, base); // p_vaddr
|
||||
wru64(hdr, 88u64, base); // p_paddr
|
||||
wru64(hdr, 96u64, filesz); // p_filesz
|
||||
wru64(hdr, 104u64, filesz); // p_memsz
|
||||
wru64(hdr, 112u64, TEXT_OFF); // p_align
|
||||
|
||||
// Write [0..0x1000) then .text.
|
||||
let n1: i64 = os.writefull(fd, hdr, TEXT_OFF);
|
||||
@@ -2296,17 +2288,7 @@ fn mklnk(a: *arena) *lnk = {
|
||||
return l;
|
||||
};
|
||||
|
||||
fn cstreq(a: *u8, lit: str) bool = {
|
||||
let n: u64 = lit.len: u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
let li: i32 = i: i32;
|
||||
if (a[i] != lit[li]) { return false; };
|
||||
i += 1u64;
|
||||
};
|
||||
if (a[i] != 0u8) { return false; };
|
||||
return true;
|
||||
};
|
||||
// `cstreq` lives in obj.ww — same bundle, single definition.
|
||||
|
||||
// `cstrlen` lives in obj.ww — same bundle, single definition.
|
||||
|
||||
@@ -2549,12 +2531,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
let l: *lnk = mklnk(a);
|
||||
|
||||
// Seed _start so libwwrt-style start.o is recognised as wanted.
|
||||
l_intern(l, "_start");
|
||||
intern(l, "_start");
|
||||
|
||||
// Load positional inputs first (preserving order).
|
||||
let k: i32 = 0;
|
||||
for (k < ninputs) {
|
||||
if (l_load(l, inputs[k]) != 0) {
|
||||
if (load(l, inputs[k]) != 0) {
|
||||
return 1;
|
||||
};
|
||||
k += 1;
|
||||
@@ -2573,19 +2555,19 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
return 1;
|
||||
};
|
||||
if (isso(p) != 0) {
|
||||
if (l_load_so(l, p) != 0) { return 1; };
|
||||
if (loadso(l, p) != 0) { return 1; };
|
||||
} else {
|
||||
if (l_load(l, p) != 0) { return 1; };
|
||||
if (load(l, p) != 0) { return 1; };
|
||||
};
|
||||
lf += 1;
|
||||
};
|
||||
|
||||
if (l_resolve(l) != 0) { return 1; };
|
||||
if (l_relocate(l, BASE + CODE_VA_OFF) != 0) { return 1; };
|
||||
if (resolve(l) != 0) { return 1; };
|
||||
if (relocate(l, BASE + CODE_VA_OFF) != 0) { return 1; };
|
||||
|
||||
let entrysym: *lsym = l_lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = l_lookup(l, "main"); }
|
||||
else { if (entrysym.defined == 0) { entrysym = l_lookup(l, "main"); }; };
|
||||
let entrysym: *lsym = lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
||||
else { if (entrysym.defined == 0) { entrysym = lookup(l, "main"); }; };
|
||||
if (entrysym == nil) {
|
||||
os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64);
|
||||
return 1;
|
||||
@@ -2603,7 +2585,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
|
||||
let entryva: u64 = BASE + CODE_VA_OFF + entrysym.val;
|
||||
let rc: i32 = l_emit_elf(l, fd, BASE, entryva);
|
||||
let rc: i32 = emitelf(l, fd, BASE, entryva);
|
||||
os.close(fd);
|
||||
return rc;
|
||||
};
|
||||
|
||||
@@ -23,17 +23,7 @@ fn mklnk(a: *arena) *lnk = {
|
||||
return l;
|
||||
};
|
||||
|
||||
fn cstreq(a: *u8, lit: str) bool = {
|
||||
let n: u64 = lit.len: u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
let li: i32 = i: i32;
|
||||
if (a[i] != lit[li]) { return false; };
|
||||
i += 1u64;
|
||||
};
|
||||
if (a[i] != 0u8) { return false; };
|
||||
return true;
|
||||
};
|
||||
// `cstreq` lives in obj.ww — same bundle, single definition.
|
||||
|
||||
// `cstrlen` lives in obj.ww — same bundle, single definition.
|
||||
|
||||
@@ -276,12 +266,12 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
let l: *lnk = mklnk(a);
|
||||
|
||||
// Seed _start so libwwrt-style start.o is recognised as wanted.
|
||||
l_intern(l, "_start");
|
||||
intern(l, "_start");
|
||||
|
||||
// Load positional inputs first (preserving order).
|
||||
let k: i32 = 0;
|
||||
for (k < ninputs) {
|
||||
if (l_load(l, inputs[k]) != 0) {
|
||||
if (load(l, inputs[k]) != 0) {
|
||||
return 1;
|
||||
};
|
||||
k += 1;
|
||||
@@ -300,19 +290,19 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
return 1;
|
||||
};
|
||||
if (isso(p) != 0) {
|
||||
if (l_load_so(l, p) != 0) { return 1; };
|
||||
if (loadso(l, p) != 0) { return 1; };
|
||||
} else {
|
||||
if (l_load(l, p) != 0) { return 1; };
|
||||
if (load(l, p) != 0) { return 1; };
|
||||
};
|
||||
lf += 1;
|
||||
};
|
||||
|
||||
if (l_resolve(l) != 0) { return 1; };
|
||||
if (l_relocate(l, BASE + CODE_VA_OFF) != 0) { return 1; };
|
||||
if (resolve(l) != 0) { return 1; };
|
||||
if (relocate(l, BASE + CODE_VA_OFF) != 0) { return 1; };
|
||||
|
||||
let entrysym: *lsym = l_lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = l_lookup(l, "main"); }
|
||||
else { if (entrysym.defined == 0) { entrysym = l_lookup(l, "main"); }; };
|
||||
let entrysym: *lsym = lookup(l, "_start");
|
||||
if (entrysym == nil) { entrysym = lookup(l, "main"); }
|
||||
else { if (entrysym.defined == 0) { entrysym = lookup(l, "main"); }; };
|
||||
if (entrysym == nil) {
|
||||
os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64);
|
||||
return 1;
|
||||
@@ -330,7 +320,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
|
||||
};
|
||||
|
||||
let entryva: u64 = BASE + CODE_VA_OFF + entrysym.val;
|
||||
let rc: i32 = l_emit_elf(l, fd, BASE, entryva);
|
||||
let rc: i32 = emitelf(l, fd, BASE, entryva);
|
||||
os.close(fd);
|
||||
return rc;
|
||||
};
|
||||
|
||||
@@ -75,8 +75,8 @@ def RELA_ADDEND: u64 = 16u64;
|
||||
|
||||
// ---- file slurp --------------------------------------------------------
|
||||
|
||||
fn read_all(path_cs: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path_cs, os.O_RDONLY, 0i32);
|
||||
fn readall(path: *u8) (*u8, u64) = {
|
||||
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
|
||||
if (fd < 0) { return nil, 0u64; };
|
||||
let n: i64 = os.filesize(fd);
|
||||
if (n < 0i64) { os.close(fd); return nil, 0u64; };
|
||||
@@ -89,7 +89,7 @@ fn read_all(path_cs: *u8) (*u8, u64) = {
|
||||
|
||||
// ---- text buffer growth ------------------------------------------------
|
||||
|
||||
fn emit_text(l: *lnk, src: *u8, n: u64) void = {
|
||||
fn emittext(l: *lnk, src: *u8, n: u64) void = {
|
||||
if (l.textlen + n > l.textcap) {
|
||||
let nc: u64 = l.textcap;
|
||||
if (nc == 0u64) { nc = 4096u64; };
|
||||
@@ -122,7 +122,7 @@ fn cstrlen(p: *u8) u64 = {
|
||||
return n;
|
||||
};
|
||||
|
||||
fn cstr_eq(p: *u8, lit: str) bool = {
|
||||
fn cstreq(p: *u8, lit: str) bool = {
|
||||
let n: u64 = lit.len: u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
@@ -134,8 +134,8 @@ fn cstr_eq(p: *u8, lit: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Build a ww str from a NUL-terminated *u8 (for passing to l_intern).
|
||||
fn cstr_to_str(a: *arena, p: *u8) str = {
|
||||
// Build a ww str from a NUL-terminated *u8 (for passing to intern).
|
||||
fn cstrtostr(a: *arena, p: *u8) str = {
|
||||
let n: u64 = cstrlen(p);
|
||||
return astrndup(a, p, n);
|
||||
};
|
||||
@@ -159,7 +159,7 @@ type armember = struct {
|
||||
mnext: *armember,
|
||||
};
|
||||
|
||||
fn is_archive(p: *u8, len: u64) bool = {
|
||||
fn isarchive(p: *u8, len: u64) bool = {
|
||||
if (len < 8u64) { return false; };
|
||||
if (p[0u64] != 33u8) { return false; }; // '!'
|
||||
if (p[1u64] != 60u8) { return false; }; // '<'
|
||||
@@ -172,8 +172,8 @@ fn is_archive(p: *u8, len: u64) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// ar_field — parse a space-padded decimal integer of width n.
|
||||
fn ar_field(p: *u8, n: u64) u64 = {
|
||||
// arfield — parse a space-padded decimal integer of width n.
|
||||
fn arfield(p: *u8, n: u64) u64 = {
|
||||
let v: u64 = 0u64;
|
||||
let i: u64 = 0u64;
|
||||
for (i < n) {
|
||||
@@ -186,10 +186,10 @@ fn ar_field(p: *u8, n: u64) u64 = {
|
||||
return v;
|
||||
};
|
||||
|
||||
// elf_globals — return a linked list of names of globally-defined
|
||||
// elfglobals — return a linked list of names of globally-defined
|
||||
// (STB_GLOBAL) symbols whose section is `.text`. Names are arena
|
||||
// copies, so the source ELF buffer can be freed afterward.
|
||||
fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
fn elfglobals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
if (len < EHDR_SIZE) { return nil; };
|
||||
if (buf[0u64] != 127u8) { return nil; };
|
||||
if (buf[1u64] != 69u8) { return nil; };
|
||||
@@ -212,7 +212,7 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
let sh_name: u32 = rd_u32(buf, sh_off + SHDR_NAME);
|
||||
let nm: *u8 = shstr + (sh_name: u64);
|
||||
if (sh_type == SHT_PROGBITS: u32) {
|
||||
if (cstr_eq(nm, ".text")) { idx_text = i: i32; };
|
||||
if (cstreq(nm, ".text")) { idx_text = i: i32; };
|
||||
};
|
||||
if (sh_type == SHT_SYMTAB: u32) { idx_symtab = i: i32; };
|
||||
i += 1u32;
|
||||
@@ -244,7 +244,7 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
if ((st_shndx: i32) == idx_text) {
|
||||
let nm_p: *u8 = strtab + (st_name: u64);
|
||||
if (nm_p[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(a, nm_p);
|
||||
let nm: str = cstrtostr(a, nm_p);
|
||||
let de: *defent = amalloc(a, 32u64): *defent;
|
||||
de.name = nm;
|
||||
de.dnext = head;
|
||||
@@ -258,13 +258,13 @@ fn elf_globals(a: *arena, buf: *u8, len: u64) *defent = {
|
||||
return head;
|
||||
};
|
||||
|
||||
// member_defines_undef — true if any of m's defined globals matches a
|
||||
// memberdefinesundef — true if any of m's defined globals matches a
|
||||
// currently-undefined symbol in the linker's symbol table. Names not
|
||||
// already interned are uninteresting (the link doesn't need them yet).
|
||||
fn member_defines_undef(l: *lnk, m: *armember) bool = {
|
||||
fn memberdefinesundef(l: *lnk, m: *armember) bool = {
|
||||
let de: *defent = m.defs;
|
||||
for (de != nil) {
|
||||
let s: *lsym = l_lookup(l, de.name);
|
||||
let s: *lsym = lookup(l, de.name);
|
||||
if (s != nil) {
|
||||
if (s.defined == 0) { return true; };
|
||||
};
|
||||
@@ -273,17 +273,17 @@ fn member_defines_undef(l: *lnk, m: *armember) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// load_archive — port of cmd/w6l/obj.c:load_archive.
|
||||
// loadarchive — port of cmd/w6l/obj.c:load_archive.
|
||||
//
|
||||
// Pass 1 indexes every regular member. Pass 2 iteratively pulls in any
|
||||
// member that supplies a currently-undefined symbol; each pull may
|
||||
// introduce fresh undefs, so we loop until quiescent.
|
||||
fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
fn loadarchive(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
|
||||
let head: *armember = nil;
|
||||
let tail: *armember = nil;
|
||||
let pos: u64 = 8u64; // past "!<arch>\n"
|
||||
for (pos + 60u64 <= len) {
|
||||
let hdr_size: u64 = ar_field(buf + pos + 48u64, 10u64);
|
||||
let hdr_size: u64 = arfield(buf + pos + 48u64, 10u64);
|
||||
let hdr_end: u64 = pos + 60u64;
|
||||
if (hdr_end + hdr_size > len) { break; };
|
||||
let first: u8 = buf[pos];
|
||||
@@ -299,7 +299,7 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
i += 1u64;
|
||||
};
|
||||
m.data = mb;
|
||||
m.defs = elf_globals(l.a, mb, hdr_size);
|
||||
m.defs = elfglobals(l.a, mb, hdr_size);
|
||||
m.loaded = 0;
|
||||
m.mnext = nil;
|
||||
if (head == nil) { head = m; }
|
||||
@@ -316,8 +316,8 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let m: *armember = head;
|
||||
for (m != nil) {
|
||||
if (m.loaded == 0) {
|
||||
if (member_defines_undef(l, m)) {
|
||||
if (load_image(l, path_cs, m.data, m.size) == 0) {
|
||||
if (memberdefinesundef(l, m)) {
|
||||
if (loadimage(l, path, m.data, m.size) == 0) {
|
||||
m.loaded = 1;
|
||||
changed = 1;
|
||||
};
|
||||
@@ -331,21 +331,21 @@ fn load_archive(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
|
||||
// ---- main loader -------------------------------------------------------
|
||||
|
||||
export fn l_load(l: *lnk, path_cs: *u8) i32 = {
|
||||
export fn load(l: *lnk, path: *u8) i32 = {
|
||||
let bufp: *u8;
|
||||
let buflen: u64;
|
||||
bufp, buflen = read_all(path_cs);
|
||||
bufp, buflen = readall(path);
|
||||
if (bufp == nil) {
|
||||
os.write(2, "w6l: cannot read object\n".ptr, 23u64);
|
||||
return -1;
|
||||
};
|
||||
if (is_archive(bufp, buflen)) {
|
||||
return load_archive(l, path_cs, bufp, buflen);
|
||||
if (isarchive(bufp, buflen)) {
|
||||
return loadarchive(l, path, bufp, buflen);
|
||||
};
|
||||
return load_image(l, path_cs, bufp, buflen);
|
||||
return loadimage(l, path, bufp, buflen);
|
||||
};
|
||||
|
||||
fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
fn loadimage(l: *lnk, path: *u8, buf: *u8, len: u64) i32 = {
|
||||
if (len < EHDR_SIZE) { return -1; };
|
||||
// magic: 0x7f, 'E', 'L', 'F'
|
||||
if (buf[0u64] != 127u8) { return -1; };
|
||||
@@ -374,11 +374,11 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let sh_name: u32 = rd_u32(buf, sh_off + SHDR_NAME);
|
||||
let nm: *u8 = shstr + (sh_name: u64);
|
||||
if (sh_type == SHT_PROGBITS: u32) {
|
||||
if (cstr_eq(nm, ".text")) { idx_text = i: i32; };
|
||||
if (cstreq(nm, ".text")) { idx_text = i: i32; };
|
||||
};
|
||||
if (sh_type == SHT_SYMTAB: u32) { idx_symtab = i: i32; };
|
||||
if (sh_type == SHT_RELA: u32) {
|
||||
if (cstr_eq(nm, ".rela.text")) { idx_rela = i: i32; };
|
||||
if (cstreq(nm, ".rela.text")) { idx_rela = i: i32; };
|
||||
};
|
||||
i += 1u32;
|
||||
};
|
||||
@@ -407,16 +407,16 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
|
||||
// Track this object.
|
||||
let ob: *lobj = amalloc(l.a, 64u64): *lobj;
|
||||
ob.path = cstr_to_str(l.a, path_cs);
|
||||
ob.path = cstrtostr(l.a, path);
|
||||
ob.buf = buf;
|
||||
ob.len = len;
|
||||
ob.text_off = l.textlen;
|
||||
ob.text_size = text_size;
|
||||
ob.textoff = l.textlen;
|
||||
ob.textsize = text_size;
|
||||
ob.onext = l.objs;
|
||||
l.objs = ob;
|
||||
|
||||
// Append .text bytes to the combined image.
|
||||
emit_text(l, buf + text_off, text_size);
|
||||
emittext(l, buf + text_off, text_size);
|
||||
|
||||
// Walk symbols. We don't keep a per-object map[] of *lsym. Instead
|
||||
// the reloc loop re-walks symtab and re-interns by name. Simpler
|
||||
@@ -429,8 +429,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let st_value: u64 = rd_u64(buf, sym_p + SYM_VALUE);
|
||||
let nm_p: *u8 = strtab + (st_name: u64);
|
||||
if (nm_p[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(l.a, nm_p);
|
||||
let gs: *lsym = l_intern(l, nm);
|
||||
let nm: str = cstrtostr(l.a, nm_p);
|
||||
let gs: *lsym = intern(l, nm);
|
||||
if (st_shndx != 0u16) {
|
||||
if ((st_shndx: i32) == idx_text) {
|
||||
if (gs.defined != 0) {
|
||||
@@ -439,8 +439,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
} else {
|
||||
gs.defined = 1;
|
||||
gs.owner = ob;
|
||||
gs.idx_in_owner = si: i32;
|
||||
gs.val = ob.text_off + st_value;
|
||||
gs.idxinowner = si: i32;
|
||||
gs.val = ob.textoff + st_value;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -463,7 +463,7 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let r_sym_idx: u32 = (r_info >> 32u64): u32;
|
||||
let r_kind: i32 = ((r_info & 4294967295u64): u32): i32;
|
||||
let nr: *lrel = amalloc(l.a, 48u64): *lrel;
|
||||
nr.off = ob.text_off + r_off;
|
||||
nr.off = ob.textoff + r_off;
|
||||
nr.kind = r_kind;
|
||||
nr.addend = r_addend: i64;
|
||||
// Look up the referenced sym by name (re-walk symtab).
|
||||
@@ -472,8 +472,8 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = {
|
||||
let s_name: u32 = rd_u32(buf, s_p + SYM_NAME);
|
||||
let s_nm: *u8 = strtab + (s_name: u64);
|
||||
if (s_nm[0u64] != 0u8) {
|
||||
let nm: str = cstr_to_str(l.a, s_nm);
|
||||
nr.sym = l_intern(l, nm);
|
||||
let nm: str = cstrtostr(l.a, s_nm);
|
||||
nr.sym = intern(l, nm);
|
||||
};
|
||||
};
|
||||
nr.rnext = l.rels;
|
||||
|
||||
@@ -24,31 +24,31 @@ def TEXT_OFF: u64 = 4096u64; // 0x1000
|
||||
|
||||
// ---- little-endian byte writers ----------------------------------------
|
||||
|
||||
fn wr_u16(buf: *u8, off: u64, v: u16) void = {
|
||||
fn wru16(buf: *u8, off: u64, v: u16) void = {
|
||||
buf[off] = (v & 255u16): u8;
|
||||
buf[off + 1u64] = ((v >> 8u16) & 255u16): u8;
|
||||
};
|
||||
|
||||
fn wr_u32(buf: *u8, off: u64, v: u32) void = {
|
||||
fn wru32(buf: *u8, off: u64, v: u32) void = {
|
||||
buf[off] = (v & 255u32): u8;
|
||||
buf[off + 1u64] = ((v >> 8u32) & 255u32): u8;
|
||||
buf[off + 2u64] = ((v >> 16u32) & 255u32): u8;
|
||||
buf[off + 3u64] = ((v >> 24u32) & 255u32): u8;
|
||||
};
|
||||
|
||||
fn wr_u64(buf: *u8, off: u64, v: u64) void = {
|
||||
wr_u32(buf, off, (v & 4294967295u64): u32);
|
||||
wr_u32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32);
|
||||
fn wru64(buf: *u8, off: u64, v: u64) void = {
|
||||
wru32(buf, off, (v & 4294967295u64): u32);
|
||||
wru32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32);
|
||||
};
|
||||
|
||||
// ---- emit ---------------------------------------------------------------
|
||||
|
||||
export fn l_emit_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
// Dispatch: any loaded shared object plus any dynamic ref means
|
||||
// we owe the loader a real PT_INTERP/PT_DYNAMIC binary.
|
||||
if (l.sos != nil) {
|
||||
if (l.dyn_n > 0) {
|
||||
return l_emit_dyn_elf(l, fd, base, entry);
|
||||
if (l.dynn > 0) {
|
||||
return emitdynelf(l, fd, base, entry);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -65,29 +65,29 @@ export fn l_emit_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = {
|
||||
hdr[4u64] = ELFCLASS64;
|
||||
hdr[5u64] = ELFDATA2LSB;
|
||||
hdr[6u64] = EV_CURRENT: u8;
|
||||
wr_u16(hdr, 16u64, ET_EXEC); // e_type
|
||||
wr_u16(hdr, 18u64, EM_X86_64_W); // e_machine
|
||||
wr_u32(hdr, 20u64, EV_CURRENT); // e_version
|
||||
wr_u64(hdr, 24u64, entry); // e_entry
|
||||
wr_u64(hdr, 32u64, 64u64); // e_phoff = sizeof(Ehdr)
|
||||
wr_u64(hdr, 40u64, 0u64); // e_shoff
|
||||
wr_u32(hdr, 48u64, 0u32); // e_flags
|
||||
wr_u16(hdr, 52u64, 64u16); // e_ehsize
|
||||
wr_u16(hdr, 54u64, 56u16); // e_phentsize
|
||||
wr_u16(hdr, 56u64, 1u16); // e_phnum
|
||||
wr_u16(hdr, 58u64, 0u16); // e_shentsize
|
||||
wr_u16(hdr, 60u64, 0u16); // e_shnum
|
||||
wr_u16(hdr, 62u64, 0u16); // e_shstrndx
|
||||
wru16(hdr, 16u64, ET_EXEC); // e_type
|
||||
wru16(hdr, 18u64, EM_X86_64_W); // e_machine
|
||||
wru32(hdr, 20u64, EV_CURRENT); // e_version
|
||||
wru64(hdr, 24u64, entry); // e_entry
|
||||
wru64(hdr, 32u64, 64u64); // e_phoff = sizeof(Ehdr)
|
||||
wru64(hdr, 40u64, 0u64); // e_shoff
|
||||
wru32(hdr, 48u64, 0u32); // e_flags
|
||||
wru16(hdr, 52u64, 64u16); // e_ehsize
|
||||
wru16(hdr, 54u64, 56u16); // e_phentsize
|
||||
wru16(hdr, 56u64, 1u16); // e_phnum
|
||||
wru16(hdr, 58u64, 0u16); // e_shentsize
|
||||
wru16(hdr, 60u64, 0u16); // e_shnum
|
||||
wru16(hdr, 62u64, 0u16); // e_shstrndx
|
||||
|
||||
// --- Phdr (56 bytes) at offset 64 ---
|
||||
wr_u32(hdr, 64u64, PT_LOAD); // p_type
|
||||
wr_u32(hdr, 68u64, PF_R | PF_X); // p_flags
|
||||
wr_u64(hdr, 72u64, 0u64); // p_offset
|
||||
wr_u64(hdr, 80u64, base); // p_vaddr
|
||||
wr_u64(hdr, 88u64, base); // p_paddr
|
||||
wr_u64(hdr, 96u64, filesz); // p_filesz
|
||||
wr_u64(hdr, 104u64, filesz); // p_memsz
|
||||
wr_u64(hdr, 112u64, TEXT_OFF); // p_align
|
||||
wru32(hdr, 64u64, PT_LOAD); // p_type
|
||||
wru32(hdr, 68u64, PF_R | PF_X); // p_flags
|
||||
wru64(hdr, 72u64, 0u64); // p_offset
|
||||
wru64(hdr, 80u64, base); // p_vaddr
|
||||
wru64(hdr, 88u64, base); // p_paddr
|
||||
wru64(hdr, 96u64, filesz); // p_filesz
|
||||
wru64(hdr, 104u64, filesz); // p_memsz
|
||||
wru64(hdr, 112u64, TEXT_OFF); // p_align
|
||||
|
||||
// Write [0..0x1000) then .text.
|
||||
let n1: i64 = os.writefull(fd, hdr, TEXT_OFF);
|
||||
|
||||
@@ -17,14 +17,14 @@ use dyn;
|
||||
def R_X86_64_PC32: i32 = 2;
|
||||
def R_X86_64_PLT32: i32 = 4;
|
||||
|
||||
export fn l_resolve(l: *lnk) i32 = {
|
||||
export fn resolve(l: *lnk) i32 = {
|
||||
// Initialise dynamic-linking sentinels. amalloc zeroes, so
|
||||
// is_dyn/dyn_lib start clean — but plt_idx and dynsym_idx
|
||||
// isdyn/dynlib start clean — but pltidx and dynsymidx
|
||||
// must be -1, not 0.
|
||||
let si: *lsym = l.syms;
|
||||
for (si != nil) {
|
||||
si.plt_idx = -1;
|
||||
si.dynsym_idx = -1;
|
||||
si.pltidx = -1;
|
||||
si.dynsymidx = -1;
|
||||
si = si.snext;
|
||||
};
|
||||
|
||||
@@ -36,14 +36,14 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
if (r.sym != nil) {
|
||||
if (r.sym.defined == 0) {
|
||||
let sym: *lsym = r.sym;
|
||||
if (sym.is_dyn == 0) {
|
||||
if (sym.isdyn == 0) {
|
||||
let so: *lso = l.sos;
|
||||
for (so != nil) {
|
||||
if (l_so_provides(so, sym.name) != 0) {
|
||||
sym.is_dyn = 1;
|
||||
sym.dyn_lib = so;
|
||||
sym.plt_idx = l.dyn_n;
|
||||
l.dyn_n += 1;
|
||||
if (soprovides(so, sym.name) != 0) {
|
||||
sym.isdyn = 1;
|
||||
sym.dynlib = so;
|
||||
sym.pltidx = l.dynn;
|
||||
l.dynn += 1;
|
||||
so = nil; // break
|
||||
} else {
|
||||
so = so.sonext;
|
||||
@@ -60,7 +60,7 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
for (r2 != nil) {
|
||||
if (r2.sym != nil) {
|
||||
if (r2.sym.defined == 0) {
|
||||
if (r2.sym.is_dyn == 0) {
|
||||
if (r2.sym.isdyn == 0) {
|
||||
os.write(2, "w6l: undefined reference to '".ptr, 28u64);
|
||||
let nm: str = r2.sym.name;
|
||||
os.write(2, nm.ptr, nm.len: u64);
|
||||
@@ -74,20 +74,20 @@ export fn l_resolve(l: *lnk) i32 = {
|
||||
return l.errs;
|
||||
};
|
||||
|
||||
fn patch_u32(p: *u8, v: u32) void = {
|
||||
fn patchu32(p: *u8, v: u32) void = {
|
||||
p[0] = (v & 255u32): u8;
|
||||
p[1] = ((v >> 8u32) & 255u32): u8;
|
||||
p[2] = ((v >> 16u32) & 255u32): u8;
|
||||
p[3] = ((v >> 24u32) & 255u32): u8;
|
||||
};
|
||||
|
||||
export fn l_relocate(l: *lnk, base: u64) i32 = {
|
||||
export fn relocate(l: *lnk, base: u64) i32 = {
|
||||
let r: *lrel = l.rels;
|
||||
for (r != nil) {
|
||||
if (r.sym != nil) {
|
||||
// Dynamic refs are patched later in dynout once the
|
||||
// PLT vaddr is known.
|
||||
if (r.sym.is_dyn != 0) {
|
||||
if (r.sym.isdyn != 0) {
|
||||
r = r.rnext;
|
||||
continue;
|
||||
};
|
||||
@@ -97,12 +97,12 @@ export fn l_relocate(l: *lnk, base: u64) i32 = {
|
||||
let site: u64 = base + r.off;
|
||||
let target: i64 = (base + r.sym.val): i64;
|
||||
let rel: i64 = (target - site: i64) + r.addend;
|
||||
patch_u32(l.text + r.off, rel: u32);
|
||||
patchu32(l.text + r.off, rel: u32);
|
||||
} else { if (k == R_X86_64_PLT32) {
|
||||
let site: u64 = base + r.off;
|
||||
let target: i64 = (base + r.sym.val): i64;
|
||||
let rel: i64 = (target - site: i64) + r.addend;
|
||||
patch_u32(l.text + r.off, rel: u32);
|
||||
patchu32(l.text + r.off, rel: u32);
|
||||
} else {
|
||||
os.write(2, "w6l: unsupported reloc kind\n".ptr, 27u64);
|
||||
l.errs += 1;
|
||||
|
||||
@@ -10,15 +10,15 @@ type lsym = struct {
|
||||
val: u64, // offset within combined .text once linked
|
||||
defined: i32, // 1 if some lobj defines this symbol
|
||||
owner: *lobj,
|
||||
idx_in_owner: i32,
|
||||
// Dynamic-linking fields. Set by l_resolve when an undefined sym
|
||||
// is provided by some loaded lso. plt_idx and dynsym_idx default
|
||||
// to -1 (set explicitly by l_resolve, not by amalloc-zeroing).
|
||||
is_dyn: i32,
|
||||
dyn_lib: *lso,
|
||||
dyn_version: str, // matched export's version; len 0 if none
|
||||
plt_idx: i32,
|
||||
dynsym_idx: i32,
|
||||
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, not by amalloc-zeroing).
|
||||
isdyn: i32,
|
||||
dynlib: *lso,
|
||||
dynversion: str, // matched export's version; len 0 if none
|
||||
pltidx: i32,
|
||||
dynsymidx: i32,
|
||||
snext: *lsym,
|
||||
};
|
||||
|
||||
@@ -34,14 +34,14 @@ type lobj = struct {
|
||||
path: str,
|
||||
buf: *u8, // object bytes
|
||||
len: u64,
|
||||
text_off: u64, // offset of .text in combined output
|
||||
text_size: u64,
|
||||
textoff: u64, // offset of .text in combined output
|
||||
textsize: u64,
|
||||
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
|
||||
// l_so_provides_v's first-match semantics agree with the C version.
|
||||
// soprovides_v's first-match semantics agree with the C version.
|
||||
type lexport = struct {
|
||||
name: str,
|
||||
version: str, // len 0 for unversioned globals
|
||||
@@ -65,7 +65,7 @@ type lnk = struct {
|
||||
textcap: u64,
|
||||
textlen: u64,
|
||||
errs: i32,
|
||||
dyn_n: i32, // number of syms routed through PLT
|
||||
dynn: i32, // number of syms routed through PLT
|
||||
};
|
||||
|
||||
fn streq(a: str, b: str) bool = {
|
||||
@@ -78,7 +78,7 @@ fn streq(a: str, b: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
export fn l_intern(l: *lnk, name: str) *lsym = {
|
||||
export fn intern(l: *lnk, name: str) *lsym = {
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (streq(s.name, name)) { return s; };
|
||||
@@ -91,7 +91,7 @@ export fn l_intern(l: *lnk, name: str) *lsym = {
|
||||
return n;
|
||||
};
|
||||
|
||||
export fn l_lookup(l: *lnk, name: str) *lsym = {
|
||||
export fn lookup(l: *lnk, name: str) *lsym = {
|
||||
let s: *lsym = l.syms;
|
||||
for (s != nil) {
|
||||
if (streq(s.name, name)) { return s; };
|
||||
|
||||
Reference in New Issue
Block a user