amalloc has 0 callers post-γ-2; the *arena threaded through w6a's init/dupstr/bufinit and the asm_.a / buf.a fields are vestigial. Drop `import mem;` from opcodes/parse/obj/main, remove asm_.a and buf.a struct fields, strip *arena from init/dupstr/bufinit signatures, update 12 call sites. main.combined.ww auto-regenerated. Comment at main.ww:36 retidied "argv/arena" → "argv-style" to match post-strip reality. Verified 132/132 incl. 991_w6a_ww + 995_self_rebuild byte-identity.
413 lines
12 KiB
Plaintext
413 lines
12 KiB
Plaintext
// selfhost/cmd/w6a/obj.ww — port of cmd/w6a/obj.c.
|
|
//
|
|
// Emit a tiny ELF64 relocatable object. Layout (in file order):
|
|
// [0] ELF header
|
|
// [1] Section .text (program bytes)
|
|
// [2] Section .rela.text (relocations)
|
|
// [3] Section .symtab
|
|
// [4] Section .strtab
|
|
// [5] Section .shstrtab
|
|
// [6] Section header table
|
|
//
|
|
// Symtab indices: 0 = STN_UNDEF, 1.. = our syms. Only GLOBAL symbols.
|
|
|
|
package w6a;
|
|
|
|
import os;
|
|
import opcodes;
|
|
|
|
// Local wrappers around os.writeall's tagged return — collapse the
|
|
// (i64 | oserror) back to a boolean / int sentinel for the
|
|
// length-checked / fire-and-forget write patterns below.
|
|
fn wrn(fd: i32, p: *u8, n: u64, want: i64) bool = {
|
|
let r: (i64 | os.oserror) = os.writeall(fd, p, n);
|
|
match (r) {
|
|
case let v: i64 => return v == want;
|
|
case let e: os.oserror => return false;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
fn wrdrop(fd: i32, p: *u8, n: u64) void = {
|
|
let r: (i64 | os.oserror) = os.writeall(fd, p, n);
|
|
match (r) {
|
|
case let v: i64 => { };
|
|
case let e: os.oserror => { };
|
|
};
|
|
};
|
|
|
|
// ---- ELF constants ----------------------------------------------------
|
|
def ELFCLASS64: u8 = 2u8;
|
|
def ELFDATA2LSB: u8 = 1u8;
|
|
def EV_CURRENT_W: u32 = 1u32;
|
|
def ET_REL_W: u16 = 1u16;
|
|
def EM_X86_64_W: u16 = 62u16;
|
|
|
|
def SHT_NULL_C: u32 = 0u32;
|
|
def SHT_PROGBITS_C: u32 = 1u32;
|
|
def SHT_SYMTAB_C: u32 = 2u32;
|
|
def SHT_STRTAB_C: u32 = 3u32;
|
|
def SHT_RELA_C: u32 = 4u32;
|
|
|
|
def SHF_WRITE: u64 = 1u64;
|
|
def SHF_ALLOC: u64 = 2u64;
|
|
def SHF_EXECINSTR: u64 = 4u64;
|
|
def SHF_INFO_LINK: u64 = 64u64; // 0x40
|
|
|
|
def STB_GLOBAL: u8 = 1u8;
|
|
def STT_NOTYPE: u8 = 0u8;
|
|
def STT_OBJECT: u8 = 1u8;
|
|
def STT_FUNC: u8 = 2u8;
|
|
|
|
// Sizes of fixed structures.
|
|
def EHDR_SZ: u64 = 64u64;
|
|
def SHDR_SZ: u64 = 64u64;
|
|
def SYM_SZ: u64 = 24u64;
|
|
def RELA_SZ: u64 = 24u64;
|
|
|
|
// ---- LE byte writers (own the bytes — write into a *u8 + offset) ----
|
|
|
|
fn wru8(p: *u8, off: u64, v: u8) void = { p[off] = v; };
|
|
fn wru16(p: *u8, off: u64, v: u16) void = {
|
|
p[off] = (v & 255u16): u8;
|
|
p[off + 1u64] = ((v >> 8u16) & 255u16): u8;
|
|
};
|
|
fn wru32(p: *u8, off: u64, v: u32) void = {
|
|
p[off] = (v & 255u32): u8;
|
|
p[off + 1u64] = ((v >> 8u32) & 255u32): u8;
|
|
p[off + 2u64] = ((v >> 16u32) & 255u32): u8;
|
|
p[off + 3u64] = ((v >> 24u32) & 255u32): u8;
|
|
};
|
|
fn wru64(p: *u8, off: u64, v: u64) void = {
|
|
wru32(p, off, (v & 4294967295u64): u32);
|
|
wru32(p, off + 4u64, ((v >> 32u64) & 4294967295u64): u32);
|
|
};
|
|
|
|
// ---- growable byte buffer ---------------------------------------------
|
|
|
|
type buf = struct {
|
|
p: *u8,
|
|
n: u64,
|
|
cap: u64,
|
|
};
|
|
|
|
fn bufinit(b: *buf) void = {
|
|
b.cap = 256u64;
|
|
b.n = 0u64;
|
|
let np: []u8 = alloc([], b.cap)!;
|
|
b.p = np.ptr;
|
|
};
|
|
|
|
fn bufgrow(b: *buf, need: u64) void = {
|
|
if (b.n + need <= b.cap) { return; };
|
|
let nc: u64 = b.cap;
|
|
for (nc < b.n + need) { nc = nc * 2u64; };
|
|
let np: []u8 = alloc([], nc)!;
|
|
let i: u64 = 0u64;
|
|
for (i < b.n) { np[i] = b.p[i]; i += 1u64; };
|
|
b.p = np.ptr;
|
|
b.cap = nc;
|
|
};
|
|
|
|
fn bufputb(b: *buf, src: *u8, n: u64) void = {
|
|
bufgrow(b, n);
|
|
let i: u64 = 0u64;
|
|
for (i < n) { b.p[b.n + i] = src[i]; i += 1u64; };
|
|
b.n += n;
|
|
};
|
|
|
|
// Write a NUL-terminated C-string copy of `s` into b. Returns offset
|
|
// where it started (suitable for st_name / sh_name fields).
|
|
fn bufputcstr(b: *buf, s: str) u32 = {
|
|
let off: u32 = b.n: u32;
|
|
bufgrow(b, s.len: u64 + 1u64);
|
|
let i: i32 = 0;
|
|
for (i < s.len) { b.p[b.n] = s[i]; b.n += 1u64; i += 1; };
|
|
b.p[b.n] = 0u8;
|
|
b.n += 1u64;
|
|
return off;
|
|
};
|
|
|
|
// ---- emitelf ---------------------------------------------------------
|
|
|
|
export fn emitelf(a: *asm_, fd: i32) i32 = {
|
|
let shstr: buf; bufinit(&shstr);
|
|
let str_: buf; bufinit(&str_);
|
|
let sym: buf; bufinit(&sym);
|
|
let rela: buf; bufinit(&rela);
|
|
let relad: buf; bufinit(&relad);
|
|
|
|
// Index 0 = empty.
|
|
let zero: u8 = 0u8;
|
|
bufputb(&shstr, &zero, 1u64);
|
|
bufputb(&str_, &zero, 1u64);
|
|
|
|
let hasdata: bool = a.datalen > 0u64;
|
|
let hasdatarelocs: bool = false;
|
|
let rscan: *areloc = a.relocs;
|
|
for (rscan != nil) {
|
|
if (rscan.section == 1) { hasdatarelocs = true; };
|
|
rscan = rscan.rnext;
|
|
};
|
|
|
|
// Section indices (mirror cmd/w6a/obj.c):
|
|
// without data, without data-relocs:
|
|
// 1=.text 2=.rela.text 3=.symtab 4=.strtab 5=.shstrtab
|
|
// with data, no data-relocs:
|
|
// 1=.text 2=.rela.text 3=.data 4=.symtab 5=.strtab 6=.shstrtab
|
|
// with data + data-relocs:
|
|
// 1=.text 2=.rela.text 3=.data 4=.rela.data 5=.symtab
|
|
// 6=.strtab 7=.shstrtab
|
|
let SH_TEXT: u16 = 1u16;
|
|
let SH_DATA: u16 = 0u16;
|
|
let SH_RELAD: u16 = 0u16;
|
|
let SH_SYMTAB: u16 = 3u16;
|
|
if (hasdata) {
|
|
SH_DATA = 3u16;
|
|
if (hasdatarelocs) {
|
|
SH_RELAD = 4u16;
|
|
SH_SYMTAB = 5u16;
|
|
} else {
|
|
SH_SYMTAB = 4u16;
|
|
};
|
|
};
|
|
let SH_STRTAB: u16 = SH_SYMTAB + 1u16;
|
|
let SH_SHSTR: u16 = SH_STRTAB + 1u16;
|
|
|
|
// Section name offsets. Append .data / .rela.data only when
|
|
// used so the .shstrtab buffer stays byte-identical for the
|
|
// no-DATAW case (test 991 byte-diff invariant).
|
|
let shntext: u32 = bufputcstr(&shstr, ".text");
|
|
let shnrela: u32 = bufputcstr(&shstr, ".rela.text");
|
|
let shndata: u32 = 0u32;
|
|
let shnrelad: u32 = 0u32;
|
|
if (hasdata) { shndata = bufputcstr(&shstr, ".data"); };
|
|
if (hasdata) { if (hasdatarelocs) {
|
|
shnrelad = bufputcstr(&shstr, ".rela.data");
|
|
};};
|
|
let shnsymtab: u32 = bufputcstr(&shstr, ".symtab");
|
|
let shnstrtab: u32 = bufputcstr(&shstr, ".strtab");
|
|
let shnshstrtab: u32 = bufputcstr(&shstr, ".shstrtab");
|
|
|
|
// Symbol 0 — STN_UNDEF (24 zero bytes).
|
|
let zsym: [24]u8;
|
|
let zi: i32 = 0;
|
|
for (zi < 24) { zsym[zi] = 0u8; zi += 1; };
|
|
bufputb(&sym, zsym.ptr, 24u64);
|
|
|
|
// Build symbols.
|
|
let idx: i32 = 1;
|
|
let s: *asym = a.syms;
|
|
for (s != nil) {
|
|
let entry: [24]u8;
|
|
let ei: i32 = 0;
|
|
for (ei < 24) { entry[ei] = 0u8; ei += 1; };
|
|
let stname: u32 = bufputcstr(&str_, s.name);
|
|
wru32(entry.ptr, 0u64, stname);
|
|
if (s.defined != 0) {
|
|
if (s.isdata != 0) {
|
|
wru8(entry.ptr, 4u64, ((STB_GLOBAL << 4u8) | STT_OBJECT));
|
|
wru16(entry.ptr, 6u64, SH_DATA);
|
|
} else {
|
|
wru8(entry.ptr, 4u64, ((STB_GLOBAL << 4u8) | STT_FUNC));
|
|
wru16(entry.ptr, 6u64, SH_TEXT);
|
|
};
|
|
wru64(entry.ptr, 8u64, s.addr);
|
|
} else {
|
|
wru8(entry.ptr, 4u64, ((STB_GLOBAL << 4u8) | STT_NOTYPE));
|
|
wru16(entry.ptr, 6u64, 0u16);
|
|
};
|
|
bufputb(&sym, entry.ptr, 24u64);
|
|
s.idx = idx;
|
|
idx += 1;
|
|
s = s.snext;
|
|
};
|
|
|
|
// Build relocations — split into text vs data buffers.
|
|
let r: *areloc = a.relocs;
|
|
for (r != nil) {
|
|
let entry: [24]u8;
|
|
wru64(entry.ptr, 0u64, r.off);
|
|
let rinfo: u64 = (r.asy.idx: u64 << 32u64) | (r.kind: u64 & 4294967295u64);
|
|
wru64(entry.ptr, 8u64, rinfo);
|
|
wru64(entry.ptr, 16u64, r.addend: u64);
|
|
if (r.section == 1) {
|
|
bufputb(&relad, entry.ptr, 24u64);
|
|
} else {
|
|
bufputb(&rela, entry.ptr, 24u64);
|
|
};
|
|
r = r.rnext;
|
|
};
|
|
|
|
// File offsets.
|
|
let off: u64 = EHDR_SZ;
|
|
let offtext: u64 = off; off = off + a.textlen;
|
|
let offrela: u64 = off; off = off + rela.n;
|
|
let offdata: u64 = off; if (hasdata) { off = off + a.datalen; };
|
|
let offrelad: u64 = off; if (hasdata) { if (hasdatarelocs) {
|
|
off = off + relad.n;
|
|
};};
|
|
let offsym: u64 = off; off = off + sym.n;
|
|
let offstr: u64 = off; off = off + str_.n;
|
|
let offshstr: u64 = off; off = off + shstr.n;
|
|
for ((off & 7u64) != 0u64) { off += 1u64; };
|
|
let offshdr: u64 = off;
|
|
let NSECT: u16 = 6u16;
|
|
if (hasdata) {
|
|
if (hasdatarelocs) { NSECT = 8u16; }
|
|
else { NSECT = 7u16; };
|
|
};
|
|
|
|
// ---- Ehdr ----
|
|
let eh: [64]u8;
|
|
let i: i32 = 0;
|
|
for (i < 64) { eh[i] = 0u8; i += 1; };
|
|
eh[0] = 127u8; // 0x7f
|
|
eh[1] = 69u8; // 'E'
|
|
eh[2] = 76u8; // 'L'
|
|
eh[3] = 70u8; // 'F'
|
|
eh[4] = ELFCLASS64;
|
|
eh[5] = ELFDATA2LSB;
|
|
eh[6] = EV_CURRENT_W: u8;
|
|
wru16(eh.ptr, 16u64, ET_REL_W);
|
|
wru16(eh.ptr, 18u64, EM_X86_64_W);
|
|
wru32(eh.ptr, 20u64, EV_CURRENT_W);
|
|
wru64(eh.ptr, 24u64, 0u64); // e_entry
|
|
wru64(eh.ptr, 32u64, 0u64); // e_phoff
|
|
wru64(eh.ptr, 40u64, offshdr); // e_shoff
|
|
wru32(eh.ptr, 48u64, 0u32); // e_flags
|
|
wru16(eh.ptr, 52u64, 64u16); // e_ehsize
|
|
wru16(eh.ptr, 54u64, 0u16); // e_phentsize
|
|
wru16(eh.ptr, 56u64, 0u16); // e_phnum
|
|
wru16(eh.ptr, 58u64, 64u16); // e_shentsize
|
|
wru16(eh.ptr, 60u64, NSECT); // e_shnum
|
|
wru16(eh.ptr, 62u64, SH_SHSTR); // e_shstrndx
|
|
|
|
if (!wrn(fd, eh.ptr, 64u64, 64i64)) { return -1; };
|
|
if (a.textlen > 0u64) {
|
|
if (!wrn(fd, a.text, a.textlen, a.textlen: i64)) { return -1; };
|
|
};
|
|
if (rela.n > 0u64) {
|
|
if (!wrn(fd, rela.p, rela.n, rela.n: i64)) { return -1; };
|
|
};
|
|
if (hasdata) {
|
|
if (a.datalen > 0u64) {
|
|
if (!wrn(fd, a.data, a.datalen, a.datalen: i64)) { return -1; };
|
|
};
|
|
if (hasdatarelocs) {
|
|
if (relad.n > 0u64) {
|
|
if (!wrn(fd, relad.p, relad.n, relad.n: i64)) { return -1; };
|
|
};
|
|
};
|
|
};
|
|
if (sym.n > 0u64) {
|
|
if (!wrn(fd, sym.p, sym.n, sym.n: i64)) { return -1; };
|
|
};
|
|
if (str_.n > 0u64) {
|
|
if (!wrn(fd, str_.p, str_.n, str_.n: i64)) { return -1; };
|
|
};
|
|
if (shstr.n > 0u64) {
|
|
if (!wrn(fd, shstr.p, shstr.n, shstr.n: i64)) { return -1; };
|
|
};
|
|
|
|
// Pad to 8 before shdrs.
|
|
let written: u64 = EHDR_SZ + a.textlen + rela.n + sym.n + str_.n + shstr.n;
|
|
if (hasdata) {
|
|
written += a.datalen;
|
|
if (hasdatarelocs) { written += relad.n; };
|
|
};
|
|
for ((written & 7u64) != 0u64) {
|
|
wrdrop(fd, &zero, 1u64);
|
|
written += 1u64;
|
|
};
|
|
|
|
// Section header table — 6 headers of 64 bytes each = 384 bytes.
|
|
let shbuf: [64]u8;
|
|
// SHT_NULL
|
|
let sn: i32 = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
// .text
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shntext);
|
|
wru32(shbuf.ptr, 4u64, SHT_PROGBITS_C);
|
|
wru64(shbuf.ptr, 8u64, SHF_ALLOC | SHF_EXECINSTR);
|
|
wru64(shbuf.ptr, 24u64, offtext);
|
|
wru64(shbuf.ptr, 32u64, a.textlen);
|
|
wru64(shbuf.ptr, 48u64, 1u64); // sh_addralign
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
// .rela.text
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shnrela);
|
|
wru32(shbuf.ptr, 4u64, SHT_RELA_C);
|
|
wru64(shbuf.ptr, 8u64, SHF_INFO_LINK);
|
|
wru64(shbuf.ptr, 24u64, offrela);
|
|
wru64(shbuf.ptr, 32u64, rela.n);
|
|
wru32(shbuf.ptr, 40u64, SH_SYMTAB: u32); // sh_link
|
|
wru32(shbuf.ptr, 44u64, 1u32); // sh_info = .text idx
|
|
wru64(shbuf.ptr, 48u64, 8u64);
|
|
wru64(shbuf.ptr, 56u64, RELA_SZ);
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
if (hasdata) {
|
|
// .data
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shndata);
|
|
wru32(shbuf.ptr, 4u64, SHT_PROGBITS_C);
|
|
wru64(shbuf.ptr, 8u64, SHF_ALLOC | SHF_WRITE);
|
|
wru64(shbuf.ptr, 24u64, offdata);
|
|
wru64(shbuf.ptr, 32u64, a.datalen);
|
|
wru64(shbuf.ptr, 48u64, 8u64); // sh_addralign
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
if (hasdatarelocs) {
|
|
// .rela.data
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shnrelad);
|
|
wru32(shbuf.ptr, 4u64, SHT_RELA_C);
|
|
wru64(shbuf.ptr, 8u64, SHF_INFO_LINK);
|
|
wru64(shbuf.ptr, 24u64, offrelad);
|
|
wru64(shbuf.ptr, 32u64, relad.n);
|
|
wru32(shbuf.ptr, 40u64, SH_SYMTAB: u32);
|
|
wru32(shbuf.ptr, 44u64, SH_DATA: u32); // applies to .data
|
|
wru64(shbuf.ptr, 48u64, 8u64);
|
|
wru64(shbuf.ptr, 56u64, RELA_SZ);
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
};
|
|
};
|
|
// .symtab
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shnsymtab);
|
|
wru32(shbuf.ptr, 4u64, SHT_SYMTAB_C);
|
|
wru64(shbuf.ptr, 24u64, offsym);
|
|
wru64(shbuf.ptr, 32u64, sym.n);
|
|
wru32(shbuf.ptr, 40u64, SH_STRTAB: u32); // sh_link
|
|
wru32(shbuf.ptr, 44u64, 1u32); // sh_info = one local (STN_UNDEF)
|
|
wru64(shbuf.ptr, 48u64, 8u64);
|
|
wru64(shbuf.ptr, 56u64, SYM_SZ);
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
// .strtab
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shnstrtab);
|
|
wru32(shbuf.ptr, 4u64, SHT_STRTAB_C);
|
|
wru64(shbuf.ptr, 24u64, offstr);
|
|
wru64(shbuf.ptr, 32u64, str_.n);
|
|
wru64(shbuf.ptr, 48u64, 1u64);
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
// .shstrtab
|
|
sn = 0;
|
|
for (sn < 64) { shbuf[sn] = 0u8; sn += 1; };
|
|
wru32(shbuf.ptr, 0u64, shnshstrtab);
|
|
wru32(shbuf.ptr, 4u64, SHT_STRTAB_C);
|
|
wru64(shbuf.ptr, 24u64, offshstr);
|
|
wru64(shbuf.ptr, 32u64, shstr.n);
|
|
wru64(shbuf.ptr, 48u64, 1u64);
|
|
wrdrop(fd, shbuf.ptr, 64u64);
|
|
|
|
return 0;
|
|
};
|