// 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 mem; import types; // 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 { a: *arena, p: *u8, n: u64, cap: u64, }; fn bufinit(b: *buf, a: *arena) void = { b.a = a; 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, a.a); let str_: buf; bufinit(&str_, a.a); let sym: buf; bufinit(&sym, a.a); let rela: buf; bufinit(&rela, a.a); let relad: buf; bufinit(&relad, a.a); // 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; };