// selfhost/cmd/6a/obj.ww — port of cmd/6a/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. use os; use mem; use types; // ---- 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_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_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 wr_u8(p: *u8, off: u64, v: u8) void = { p[off] = v; }; fn wr_u16(p: *u8, off: u64, v: u16) void = { p[off] = (v & 255u16): u8; p[off + 1u64] = ((v >> 8u16) & 255u16): u8; }; fn wr_u32(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 wr_u64(p: *u8, off: u64, v: u64) void = { wr_u32(p, off, (v & 4294967295u64): u32); wr_u32(p, off + 4u64, ((v >> 32u64) & 4294967295u64): u32); }; // ---- growable byte buffer --------------------------------------------- type buf = struct { a: *arena, p: *u8, n: u64, cap: u64, }; fn buf_init(b: *buf, a: *arena) void = { b.a = a; b.cap = 256u64; b.n = 0u64; b.p = amalloc(a, b.cap): *u8; }; fn buf_grow(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 = amalloc(b.a, nc): *u8; let i: u64 = 0u64; for (i < b.n) { np[i] = b.p[i]; i += 1u64; }; b.p = np; b.cap = nc; }; fn buf_putb(b: *buf, src: *u8, n: u64) void = { buf_grow(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 buf_put_cstr(b: *buf, s: str) u32 = { let off: u32 = b.n: u32; buf_grow(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; }; // ---- emit_elf --------------------------------------------------------- export fn a_emit_elf(a: *asm_, fd: i32) i32 = { let shstr: buf; buf_init(&shstr, a.a); let str_: buf; buf_init(&str_, a.a); let sym: buf; buf_init(&sym, a.a); let rela: buf; buf_init(&rela, a.a); // Index 0 = empty. let zero: u8 = 0u8; buf_putb(&shstr, &zero, 1u64); buf_putb(&str_, &zero, 1u64); // Section name offsets. let shn_text: u32 = buf_put_cstr(&shstr, ".text"); let shn_rela: u32 = buf_put_cstr(&shstr, ".rela.text"); let shn_symtab: u32 = buf_put_cstr(&shstr, ".symtab"); let shn_strtab: u32 = buf_put_cstr(&shstr, ".strtab"); let shn_shstrtab: u32 = buf_put_cstr(&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; }; buf_putb(&sym, zsym.ptr, 24u64); let SH_TEXT: u16 = 1u16; // 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 st_name: u32 = buf_put_cstr(&str_, s.name); wr_u32(entry.ptr, 0u64, st_name); if (s.defined != 0) { wr_u8(entry.ptr, 4u64, ((STB_GLOBAL << 4u8) | STT_FUNC)); wr_u16(entry.ptr, 6u64, SH_TEXT); wr_u64(entry.ptr, 8u64, s.addr); } else { wr_u8(entry.ptr, 4u64, ((STB_GLOBAL << 4u8) | STT_NOTYPE)); wr_u16(entry.ptr, 6u64, 0u16); }; buf_putb(&sym, entry.ptr, 24u64); s.idx = idx; idx += 1; s = s.snext; }; // Build relocations. let r: *areloc = a.relocs; for (r != nil) { let entry: [24]u8; wr_u64(entry.ptr, 0u64, r.off); let r_info: u64 = (r.asy.idx: u64 << 32u64) | (r.kind: u64 & 4294967295u64); wr_u64(entry.ptr, 8u64, r_info); wr_u64(entry.ptr, 16u64, r.addend: u64); buf_putb(&rela, entry.ptr, 24u64); r = r.rnext; }; // File offsets. let off: u64 = EHDR_SZ; let off_text: u64 = off; off = off + a.textlen; let off_rela: u64 = off; off = off + rela.n; let off_sym: u64 = off; off = off + sym.n; let off_str: u64 = off; off = off + str_.n; let off_shstr: u64 = off; off = off + shstr.n; for ((off & 7u64) != 0u64) { off += 1u64; }; let off_shdr: u64 = off; let NSECT: u16 = 6u16; // ---- 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; wr_u16(eh.ptr, 16u64, ET_REL_W); wr_u16(eh.ptr, 18u64, EM_X86_64_W); wr_u32(eh.ptr, 20u64, EV_CURRENT_W); wr_u64(eh.ptr, 24u64, 0u64); // e_entry wr_u64(eh.ptr, 32u64, 0u64); // e_phoff wr_u64(eh.ptr, 40u64, off_shdr); // e_shoff wr_u32(eh.ptr, 48u64, 0u32); // e_flags wr_u16(eh.ptr, 52u64, 64u16); // e_ehsize wr_u16(eh.ptr, 54u64, 0u16); // e_phentsize wr_u16(eh.ptr, 56u64, 0u16); // e_phnum wr_u16(eh.ptr, 58u64, 64u16); // e_shentsize wr_u16(eh.ptr, 60u64, NSECT); // e_shnum wr_u16(eh.ptr, 62u64, 5u16); // e_shstrndx if (os.writefull(fd, eh.ptr, 64u64) != 64i64) { return -1; }; if (a.textlen > 0u64) { if (os.writefull(fd, a.text, a.textlen) != a.textlen: i64) { return -1; }; }; if (rela.n > 0u64) { if (os.writefull(fd, rela.p, rela.n) != rela.n: i64) { return -1; }; }; if (sym.n > 0u64) { if (os.writefull(fd, sym.p, sym.n) != sym.n: i64) { return -1; }; }; if (str_.n > 0u64) { if (os.writefull(fd, str_.p, str_.n) != str_.n: i64) { return -1; }; }; if (shstr.n > 0u64) { if (os.writefull(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; for ((written & 7u64) != 0u64) { os.writefull(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; }; os.writefull(fd, shbuf.ptr, 64u64); // .text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; wr_u32(shbuf.ptr, 0u64, shn_text); wr_u32(shbuf.ptr, 4u64, SHT_PROGBITS_C); wr_u64(shbuf.ptr, 8u64, SHF_ALLOC | SHF_EXECINSTR); wr_u64(shbuf.ptr, 24u64, off_text); wr_u64(shbuf.ptr, 32u64, a.textlen); wr_u64(shbuf.ptr, 48u64, 1u64); // sh_addralign os.writefull(fd, shbuf.ptr, 64u64); // .rela.text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; wr_u32(shbuf.ptr, 0u64, shn_rela); wr_u32(shbuf.ptr, 4u64, SHT_RELA_C); wr_u64(shbuf.ptr, 8u64, SHF_INFO_LINK); wr_u64(shbuf.ptr, 24u64, off_rela); wr_u64(shbuf.ptr, 32u64, rela.n); wr_u32(shbuf.ptr, 40u64, 3u32); // sh_link = symtab idx wr_u32(shbuf.ptr, 44u64, 1u32); // sh_info = .text idx wr_u64(shbuf.ptr, 48u64, 8u64); wr_u64(shbuf.ptr, 56u64, RELA_SZ); os.writefull(fd, shbuf.ptr, 64u64); // .symtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; wr_u32(shbuf.ptr, 0u64, shn_symtab); wr_u32(shbuf.ptr, 4u64, SHT_SYMTAB_C); wr_u64(shbuf.ptr, 24u64, off_sym); wr_u64(shbuf.ptr, 32u64, sym.n); wr_u32(shbuf.ptr, 40u64, 4u32); // sh_link = strtab wr_u32(shbuf.ptr, 44u64, 1u32); // sh_info = one local (STN_UNDEF) wr_u64(shbuf.ptr, 48u64, 8u64); wr_u64(shbuf.ptr, 56u64, SYM_SZ); os.writefull(fd, shbuf.ptr, 64u64); // .strtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; wr_u32(shbuf.ptr, 0u64, shn_strtab); wr_u32(shbuf.ptr, 4u64, SHT_STRTAB_C); wr_u64(shbuf.ptr, 24u64, off_str); wr_u64(shbuf.ptr, 32u64, str_.n); wr_u64(shbuf.ptr, 48u64, 1u64); os.writefull(fd, shbuf.ptr, 64u64); // .shstrtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; wr_u32(shbuf.ptr, 0u64, shn_shstrtab); wr_u32(shbuf.ptr, 4u64, SHT_STRTAB_C); wr_u64(shbuf.ptr, 24u64, off_shstr); wr_u64(shbuf.ptr, 32u64, shstr.n); wr_u64(shbuf.ptr, 48u64, 1u64); os.writefull(fd, shbuf.ptr, 64u64); return 0; };