// selfhost/cmd/w6l/dynout.ww — port of cmd/w6l/dynout.c. // // Emit a dynamic-linked ELF executable. The shape is the simplest // valid one: PT_INTERP + PT_DYNAMIC + DT_BIND_NOW so the loader // resolves every PLT slot at startup (no lazy binding, no PLT0 // trampoline). SysV .hash, not .gnu.hash. Non-PIE, fixed base. // // Layout: // [0] Ehdr // [64] Phdrs (PT_LOAD R+X, PT_LOAD R+W, PT_INTERP, PT_DYNAMIC) // [interp_off] "/lib64/ld-linux-x86-64.so.2\0" // [dynstr_off] .dynstr // [dynsym_off] .dynsym // [hash_off] .hash // [versym_off] .gnu.version // [verneed_off] .gnu.version_r // [relaplt_off] .rela.plt // [pad to 0x1000] // [text_off] .text // [plt_off] .plt // [pad to next page] // [gotplt_off] .got.plt (writable; mapped by PT_LOAD #2) // [dynamic_off] .dynamic (writable; covered by PT_DYNAMIC) // // cgen workaround notes: // - 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.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. use os; use mem; use sym; // ELF constants def ET_EXEC_D: u16 = 2u16; def EM_X86_64_D: u16 = 62u16; def EV_CURRENT_D: u32 = 1u32; def ELFCLASS64_D: u8 = 2u8; def ELFDATA2LSB_D: u8 = 1u8; def PT_LOAD_D: u32 = 1u32; def PT_DYNAMIC_D: u32 = 2u32; def PT_INTERP_D: u32 = 3u32; def PF_X_D: u32 = 1u32; def PF_W_D: u32 = 2u32; def PF_R_D: u32 = 4u32; def DT_NULL: i64 = 0i64; def DT_NEEDED: i64 = 1i64; def DT_PLTRELSZ: i64 = 2i64; def DT_PLTGOT: i64 = 3i64; def DT_HASH: i64 = 4i64; def DT_STRTAB: i64 = 5i64; def DT_SYMTAB: i64 = 6i64; def DT_STRSZ: i64 = 10i64; def DT_SYMENT: i64 = 11i64; def DT_PLTREL: i64 = 20i64; def DT_RELA: i64 = 7i64; def DT_JMPREL: i64 = 23i64; def DT_BIND_NOW: i64 = 24i64; def DT_VERSYM: i64 = 1879048176i64; // 0x6ffffff0 def DT_VERNEED: i64 = 1879048190i64; // 0x6ffffffe def DT_VERNEEDNUM: i64 = 1879048191i64; // 0x6fffffff def VER_NDX_LOCAL_D: u16 = 0u16; def VER_NDX_GLOBAL_D: u16 = 1u16; def R_X86_64_PC32_D: i32 = 2; def R_X86_64_PLT32_D: i32 = 4; def R_X86_64_JUMP_SLOT_D: u32 = 7u32; def STB_GLOBAL_D: u8 = 1u8; def STT_FUNC_D: u8 = 2u8; def PLT_STUB_BYTES_D: u64 = 8u64; def PAGE: u64 = 4096u64; // The PT_INTERP literal. Returned via a fn to dodge `def : str = "..."` // breakage in the current cgen. fn interp_str_v() str = { return "/lib64/ld-linux-x86-64.so.2"; }; // ---- byte writers ------------------------------------------------------ fn d_wr8(buf: *u8, off: u64, v: u8) void = { buf[off] = v; }; fn d_wr16(buf: *u8, off: u64, v: u16) void = { buf[off] = (v & 255u16): u8; buf[off + 1u64] = ((v >> 8u16) & 255u16): u8; }; fn d_wr32(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 d_wr64(buf: *u8, off: u64, v: u64) void = { d_wr32(buf, off, (v & 4294967295u64): u32); d_wr32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32); }; fn d_wri64(buf: *u8, off: u64, v: i64) void = { d_wr64(buf, off, v: u64); }; fn d_wri32(buf: *u8, off: u64, v: i32) void = { d_wr32(buf, off, v: u32); }; // ---- byte readers ------------------------------------------------------ fn d_rd_u16(p: *u8, off: u64) u16 = { let b0: u16 = p[off]: u16; let b1: u16 = p[off + 1u64]: u16; return b0 | (b1 << 8u16); }; fn d_rd_u32(p: *u8, off: u64) u32 = { let b0: u32 = p[off]: u32; let b1: u32 = p[off + 1u64]: u32; let b2: u32 = p[off + 2u64]: u32; let b3: u32 = p[off + 3u64]: u32; return b0 | (b1 << 8u32) | (b2 << 16u32) | (b3 << 24u32); }; fn d_rd_i32(p: *u8, off: u64) i32 = { return d_rd_u32(p, off): i32; }; fn d_bcopy(dst: *u8, off: u64, src: *u8, n: u64) void = { let i: u64 = 0u64; for (i < n) { dst[off + i] = src[i]; i += 1u64; }; }; // elf_hash — SysV ELF hash. Used for .gnu.version_r's vna_hash. fn elf_hash(name: str) u32 = { let h: u32 = 0u32; let i: i32 = 0; for (i < name.len) { let c: u32 = (name[i]: u8): u32; h = (h << 4u32) + c; let g: u32 = h & 4026531840u32; // 0xf0000000 if (g != 0u32) { h = h ^ (g >> 24u32); }; h = h & ~g; i += 1; }; return h; }; fn align_up(off: u64, a: u64) u64 = { return (off + a - 1u64) & ~(a - 1u64); }; fn str_eq_d(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; }; // ---- main entry -------------------------------------------------------- export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { let a: *arena = l.a; 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.isdyn != 0) { if (s.pltidx >= 0) { if (s.pltidx < n) { dynsyms[s.pltidx] = s; }; }; }; s = s.snext; }; let i: i32 = 0; for (i < n) { if (dynsyms[i] == nil) { os.write(2, "w6l: dynout: no sym for plt_idx\n".ptr, 31u64); return 1; }; i += 1; }; // ---- collect used .so's (in l.sos order) ---- let max_sos: i32 = 0; let so: *lso = l.sos; for (so != nil) { max_sos += 1; so = so.sonext; }; let sos_used: **lso = amalloc(a, (max_sos: u64) * 8u64): **lso; let nsos: i32 = 0; so = l.sos; for (so != nil) { let used: i32 = 0; let j: i32 = 0; for (j < n) { let dsm: *lsym = dynsyms[j]; let dl: *lso = dsm.dynlib; if (dl == so) { used = 1; j = n; } else { j += 1; }; }; if (used != 0) { sos_used[nsos] = so; nsos += 1; }; so = so.sonext; }; // ---- build flat version table grouped by vlib ---- // vlib_sos_idx[k] = sos_used index for vlib k. // vlib_first[k] = ver index of first version under vlib k. // vlib_count[k] = number of versions under vlib k. // ver_lib_idx[v] = vlib index that version v belongs to. // ver_name_ptr_arr[v] = name's *u8 (interned in the .so's verdef strings). // ver_name_len_buf[v] = name length (i32). // ver_dynstr_off[v] = offset within .dynstr (assigned after layout). // ver_vna_other[v] = versym index (starting at 2). let vlib_sos_idx_buf: *u8 = amalloc(a, (max_sos: u64) * 4u64): *u8; let vlib_first_buf: *u8 = amalloc(a, (max_sos: u64) * 4u64): *u8; let vlib_count_buf: *u8 = amalloc(a, (max_sos: u64) * 4u64): *u8; let n_vlibs: i32 = 0; let ver_lib_idx_buf: *u8 = amalloc(a, nu * 4u64): *u8; let ver_name_ptr_arr: **u8 = amalloc(a, nu * 8u64): **u8; let ver_name_len_buf: *u8 = amalloc(a, nu * 4u64): *u8; let ver_dynstr_off: *u8 = amalloc(a, nu * 4u64): *u8; let ver_vna_other: *u8 = amalloc(a, nu * 2u64): *u8; let n_vers: i32 = 0; let si: i32 = 0; for (si < nsos) { let cur_so: *lso = sos_used[si]; let has: i32 = 0; let j: i32 = 0; for (j < n) { let dsm: *lsym = dynsyms[j]; let dl: *lso = dsm.dynlib; if (dl == cur_so) { let nm0: str = dsm.name; let dv: str = soversion(cur_so, nm0); if (dv.len > 0) { has = 1; j = n; } else { j += 1; }; } else { j += 1; }; }; if (has != 0) { d_wr32(vlib_sos_idx_buf, (n_vlibs: u64) * 4u64, si: u32); d_wr32(vlib_first_buf, (n_vlibs: u64) * 4u64, n_vers: u32); let added: i32 = 0; let jj: i32 = 0; for (jj < n) { let dsm2: *lsym = dynsyms[jj]; let dl2: *lso = dsm2.dynlib; if (dl2 == cur_so) { let nm2: str = dsm2.name; let vname: str = soversion(cur_so, nm2); if (vname.len > 0) { let seen: i32 = 0; let k: i32 = 0; for (k < added) { let kk: i32 = n_vers - added + k; let existing: str; existing.ptr = ver_name_ptr_arr[kk]; existing.len = d_rd_i32(ver_name_len_buf, (kk: u64) * 4u64); if (str_eq_d(existing, vname)) { seen = 1; k = added; } else { k += 1; }; }; if (seen == 0) { d_wr32(ver_lib_idx_buf, (n_vers: u64) * 4u64, n_vlibs: u32); ver_name_ptr_arr[n_vers] = vname.ptr; d_wri32(ver_name_len_buf, (n_vers: u64) * 4u64, vname.len); n_vers += 1; added += 1; }; }; }; jj += 1; }; d_wr32(vlib_count_buf, (n_vlibs: u64) * 4u64, added: u32); n_vlibs += 1; }; si += 1; }; // Assign vna_other indices starting at 2, walking vlib then per-version. let next_vna: u16 = 2u16; let vi: i32 = 0; for (vi < n_vlibs) { let first: i32 = d_rd_i32(vlib_first_buf, (vi: u64) * 4u64); let cnt: i32 = d_rd_i32(vlib_count_buf, (vi: u64) * 4u64); let k: i32 = 0; for (k < cnt) { d_wr16(ver_vna_other, ((first + k): u64) * 2u64, next_vna); next_vna += 1u16; k += 1; }; vi += 1; }; // ---- compute dynstr size ---- let dynstr_sz: u64 = 1u64; // leading NUL let pi: i32 = 0; for (pi < nsos) { let so4: *lso = sos_used[pi]; dynstr_sz += so4.soname.len: u64; dynstr_sz += 1u64; pi += 1; }; pi = 0; for (pi < n) { let dsm4: *lsym = dynsyms[pi]; dynstr_sz += dsm4.name.len: u64; dynstr_sz += 1u64; pi += 1; }; pi = 0; for (pi < n_vers) { let nm_len: i32 = d_rd_i32(ver_name_len_buf, (pi: u64) * 4u64); dynstr_sz += nm_len: u64; dynstr_sz += 1u64; pi += 1; }; // ---- fill dynstr ---- let dynstr: *u8 = amalloc(a, dynstr_sz): *u8; let dynstr_pos: u64 = 1u64; // past leading NUL let soname_str: *u8 = amalloc(a, (nsos: u64) * 4u64): *u8; pi = 0; for (pi < nsos) { d_wr32(soname_str, (pi: u64) * 4u64, dynstr_pos: u32); let so2: *lso = sos_used[pi]; let snm: str = so2.soname; d_bcopy(dynstr, dynstr_pos, snm.ptr, snm.len: u64); dynstr_pos += snm.len: u64; dynstr[dynstr_pos] = 0u8; dynstr_pos += 1u64; pi += 1; }; let symname_str: *u8 = amalloc(a, nu * 4u64): *u8; pi = 0; for (pi < n) { d_wr32(symname_str, (pi: u64) * 4u64, dynstr_pos: u32); let dsm: *lsym = dynsyms[pi]; let snm: str = dsm.name; d_bcopy(dynstr, dynstr_pos, snm.ptr, snm.len: u64); dynstr_pos += snm.len: u64; dynstr[dynstr_pos] = 0u8; dynstr_pos += 1u64; pi += 1; }; pi = 0; for (pi < n_vers) { d_wr32(ver_dynstr_off, (pi: u64) * 4u64, dynstr_pos: u32); let nm_p: *u8 = ver_name_ptr_arr[pi]; let nm_len: i32 = d_rd_i32(ver_name_len_buf, (pi: u64) * 4u64); d_bcopy(dynstr, dynstr_pos, nm_p, nm_len: u64); dynstr_pos += nm_len: u64; dynstr[dynstr_pos] = 0u8; dynstr_pos += 1u64; pi += 1; }; // ---- per-dyn-sym versym index ---- let versym_for: *u8 = amalloc(a, nu * 2u64): *u8; pi = 0; for (pi < n) { let dsm3: *lsym = dynsyms[pi]; let dl3: *lso = dsm3.dynlib; let nm3: str = dsm3.name; let vname: str = soversion(dl3, nm3); if (vname.len == 0) { d_wr16(versym_for, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); } else { let matched: i32 = 0; let vk: i32 = 0; for (vk < n_vers) { let vlx: i32 = d_rd_i32(ver_lib_idx_buf, (vk: u64) * 4u64); let sosx: i32 = d_rd_i32(vlib_sos_idx_buf, (vlx: u64) * 4u64); if (sos_used[sosx] == dl3) { let exi: str; exi.ptr = ver_name_ptr_arr[vk]; exi.len = d_rd_i32(ver_name_len_buf, (vk: u64) * 4u64); if (str_eq_d(exi, vname)) { let other: u16 = d_rd_u16(ver_vna_other, (vk: u64) * 2u64); d_wr16(versym_for, (pi: u64) * 2u64, other); matched = 1; vk = n_vers; } else { vk += 1; }; } else { vk += 1; }; }; if (matched == 0) { d_wr16(versym_for, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); }; }; pi += 1; }; // ---- compute byte sizes ---- let ehdr_sz: u64 = 64u64; let n_phdrs: u64 = 4u64; let phdr_sz: u64 = n_phdrs * 56u64; let is_local: str = interp_str_v(); let interp_sz: u64 = (is_local.len: u64) + 1u64; let nsyms_total: u64 = 1u64 + nu; let dynsym_sz: u64 = nsyms_total * 24u64; let nbuckets: u32 = 1u32; let nchain: u32 = nsyms_total: u32; let hash_sz: u64 = (2u64 + (nbuckets: u64) + (nchain: u64)) * 4u64; let relaplt_sz: u64 = nu * 24u64; let plt_sz: u64 = nu * PLT_STUB_BYTES_D; let gotplt_sz: u64 = (3u64 + nu) * 8u64; let versym_sz: u64 = nsyms_total * 2u64; let verneed_sz: u64 = 0u64; let vli: i32 = 0; for (vli < n_vlibs) { let cnt: i32 = d_rd_i32(vlib_count_buf, (vli: u64) * 4u64); verneed_sz += 16u64 + 16u64 * (cnt: u64); vli += 1; }; let with_ver: i32 = 0; if (n_vlibs > 0) { with_ver = 1; }; let extra: u64 = 0u64; if (with_ver != 0) { extra = 3u64; }; let ndyn: u64 = (nsos: u64) + 11u64 + extra; let dynamic_sz: u64 = ndyn * 16u64; // ---- compute file offsets ---- let off: u64 = ehdr_sz + phdr_sz; let interp_off: u64 = off; off += interp_sz; off = align_up(off, 8u64); let dynstr_off: u64 = off; off += dynstr_sz; off = align_up(off, 8u64); let dynsym_off: u64 = off; off += dynsym_sz; let hash_off: u64 = off; off += hash_sz; off = align_up(off, 2u64); let versym_off: u64 = off; off += versym_sz; off = align_up(off, 4u64); let verneed_off: u64 = off; off += verneed_sz; off = align_up(off, 8u64); let relaplt_off: u64 = off; off += relaplt_sz; let text_off: u64 = align_up(off, PAGE); let plt_off: u64 = text_off + l.textlen; let rx_end: u64 = plt_off + plt_sz; let gotplt_off: u64 = align_up(rx_end, PAGE); let dynamic_off: u64 = gotplt_off + gotplt_sz; let file_end: u64 = dynamic_off + dynamic_sz; let interp_va: u64 = base + interp_off; let dynstr_va: u64 = base + dynstr_off; let dynsym_va: u64 = base + dynsym_off; let hash_va: u64 = base + hash_off; let versym_va: u64 = base + versym_off; let verneed_va: u64 = base + verneed_off; let relaplt_va: u64 = base + relaplt_off; let text_va: u64 = base + text_off; let plt_va: u64 = base + plt_off; let gotplt_va: u64 = base + gotplt_off; let dynamic_va: u64 = base + dynamic_off; // ---- build .dynsym ---- let dynsym_buf: *u8 = amalloc(a, dynsym_sz): *u8; pi = 0; for (pi < n) { let eoff: u64 = (1u64 + (pi: u64)) * 24u64; d_wr32(dynsym_buf, eoff + 0u64, d_rd_u32(symname_str, (pi: u64) * 4u64)); d_wr8(dynsym_buf, eoff + 4u64, (STB_GLOBAL_D << 4u8) | (STT_FUNC_D & 15u8)); d_wr8(dynsym_buf, eoff + 5u64, 0u8); d_wr16(dynsym_buf, eoff + 6u64, 0u16); d_wr64(dynsym_buf, eoff + 8u64, 0u64); d_wr64(dynsym_buf, eoff + 16u64, 0u64); pi += 1; }; // ---- build .hash (SysV, 1 bucket) ---- let hash_buf: *u8 = amalloc(a, hash_sz): *u8; d_wr32(hash_buf, 0u64, nbuckets); d_wr32(hash_buf, 4u64, nchain); let bucket0: u32 = 0u32; if (nsyms_total > 1u64) { bucket0 = 1u32; }; d_wr32(hash_buf, 8u64, bucket0); let ci: u64 = 1u64; for (ci < nsyms_total) { let nxt: u32 = 0u32; if (ci + 1u64 < nsyms_total) { nxt = (ci + 1u64): u32; }; d_wr32(hash_buf, 8u64 + (nbuckets: u64) * 4u64 + ci * 4u64, nxt); ci += 1u64; }; // ---- build .rela.plt ---- let relaplt_buf: *u8 = amalloc(a, relaplt_sz): *u8; pi = 0; for (pi < n) { let roff: u64 = (pi: u64) * 24u64; d_wr64(relaplt_buf, roff + 0u64, gotplt_va + (3u64 + (pi: u64)) * 8u64); let info: u64 = ((1u64 + (pi: u64)) << 32u64) | (R_X86_64_JUMP_SLOT_D: u64); d_wr64(relaplt_buf, roff + 8u64, info); d_wri64(relaplt_buf, roff + 16u64, 0i64); pi += 1; }; // ---- build .gnu.version (u16 per dynsym entry) ---- let versym_buf: *u8 = amalloc(a, versym_sz): *u8; d_wr16(versym_buf, 0u64, VER_NDX_LOCAL_D); pi = 0; for (pi < n) { d_wr16(versym_buf, 2u64 + (pi: u64) * 2u64, d_rd_u16(versym_for, (pi: u64) * 2u64)); pi += 1; }; // ---- build .gnu.version_r ---- let verneed_buf: *u8 = amalloc(a, verneed_sz): *u8; if (verneed_sz > 0u64) { let vnoff: u64 = 0u64; vli = 0; for (vli < n_vlibs) { let sos_idx: i32 = d_rd_i32(vlib_sos_idx_buf, (vli: u64) * 4u64); let first: i32 = d_rd_i32(vlib_first_buf, (vli: u64) * 4u64); let cnt: i32 = d_rd_i32(vlib_count_buf, (vli: u64) * 4u64); let vn_start: u64 = vnoff; d_wr16(verneed_buf, vnoff + 0u64, 1u16); d_wr16(verneed_buf, vnoff + 2u64, cnt: u16); d_wr32(verneed_buf, vnoff + 4u64, d_rd_u32(soname_str, (sos_idx: u64) * 4u64)); d_wr32(verneed_buf, vnoff + 8u64, 16u32); vnoff += 16u64; let k: i32 = 0; for (k < cnt) { let vk: i32 = first + k; let nm: str; nm.ptr = ver_name_ptr_arr[vk]; nm.len = d_rd_i32(ver_name_len_buf, (vk: u64) * 4u64); let h: u32 = elf_hash(nm); d_wr32(verneed_buf, vnoff + 0u64, h); d_wr16(verneed_buf, vnoff + 4u64, 0u16); d_wr16(verneed_buf, vnoff + 6u64, d_rd_u16(ver_vna_other, (vk: u64) * 2u64)); d_wr32(verneed_buf, vnoff + 8u64, d_rd_u32(ver_dynstr_off, (vk: u64) * 4u64)); let nxt: u32 = 0u32; if (k + 1 < cnt) { nxt = 16u32; }; d_wr32(verneed_buf, vnoff + 12u64, nxt); vnoff += 16u64; k += 1; }; let vn_nxt: u32 = 0u32; if (vli + 1 < n_vlibs) { vn_nxt = (vnoff - vn_start): u32; }; d_wr32(verneed_buf, vn_start + 12u64, vn_nxt); vli += 1; }; }; // ---- build .plt ---- let plt_buf: *u8 = amalloc(a, plt_sz): *u8; pi = 0; for (pi < n) { let p_off: u64 = (pi: u64) * PLT_STUB_BYTES_D; let stub_va: u64 = plt_va + p_off; let next_ip: u64 = stub_va + 6u64; let slot_va: u64 = gotplt_va + (3u64 + (pi: u64)) * 8u64; let disp: i64 = (slot_va: i64) - (next_ip: i64); d_wr8(plt_buf, p_off + 0u64, 255u8); d_wr8(plt_buf, p_off + 1u64, 37u8); d_wr32(plt_buf, p_off + 2u64, (disp: i32): u32); pi += 1; }; // ---- build .got.plt ---- let gotplt_buf: *u8 = amalloc(a, gotplt_sz): *u8; d_wr64(gotplt_buf, 0u64, dynamic_va); // ---- build .dynamic ---- let dynamic_buf: *u8 = amalloc(a, dynamic_sz): *u8; let dk: u64 = 0u64; pi = 0; for (pi < nsos) { d_wri64(dynamic_buf, dk * 16u64 + 0u64, DT_NEEDED); d_wr64(dynamic_buf, dk * 16u64 + 8u64, d_rd_u32(soname_str, (pi: u64) * 4u64): u64); dk += 1u64; pi += 1; }; d_wri64(dynamic_buf, dk * 16u64, DT_HASH); d_wr64(dynamic_buf, dk * 16u64 + 8u64, hash_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_STRTAB); d_wr64(dynamic_buf, dk * 16u64 + 8u64, dynstr_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_SYMTAB); d_wr64(dynamic_buf, dk * 16u64 + 8u64, dynsym_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_STRSZ); d_wr64(dynamic_buf, dk * 16u64 + 8u64, dynstr_sz); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_SYMENT); d_wr64(dynamic_buf, dk * 16u64 + 8u64, 24u64); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_PLTGOT); d_wr64(dynamic_buf, dk * 16u64 + 8u64, gotplt_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_PLTRELSZ); d_wr64(dynamic_buf, dk * 16u64 + 8u64, relaplt_sz); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_PLTREL); d_wr64(dynamic_buf, dk * 16u64 + 8u64, DT_RELA: u64);dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_JMPREL); d_wr64(dynamic_buf, dk * 16u64 + 8u64, relaplt_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_BIND_NOW); d_wr64(dynamic_buf, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (with_ver != 0) { d_wri64(dynamic_buf, dk * 16u64, DT_VERSYM); d_wr64(dynamic_buf, dk * 16u64 + 8u64, versym_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_VERNEED); d_wr64(dynamic_buf, dk * 16u64 + 8u64, verneed_va); dk += 1u64; d_wri64(dynamic_buf, dk * 16u64, DT_VERNEEDNUM); d_wr64(dynamic_buf, dk * 16u64 + 8u64, n_vlibs: u64);dk += 1u64; }; d_wri64(dynamic_buf, dk * 16u64, DT_NULL); d_wr64(dynamic_buf, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (dk != ndyn) { os.write(2, "w6l: dynamic entry count mismatch\n".ptr, 33u64); return 1; }; // ---- patch .text relocs targeting dynamic syms ---- let r: *lrel = l.rels; for (r != nil) { if (r.sym != nil) { let rsym: *lsym = r.sym; 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); return 1; }; }; let site: u64 = text_va + r.off; 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); }; }; r = r.rnext; }; // ---- assemble file buffer ---- let file_buf: *u8 = os.alloc(file_end): *u8; if (file_buf == nil) { os.write(2, "w6l: out of memory\n".ptr, 18u64); return 1; }; // Ehdr d_wr8(file_buf, 0u64, 127u8); d_wr8(file_buf, 1u64, 69u8); d_wr8(file_buf, 2u64, 76u8); d_wr8(file_buf, 3u64, 70u8); d_wr8(file_buf, 4u64, ELFCLASS64_D); d_wr8(file_buf, 5u64, ELFDATA2LSB_D); d_wr8(file_buf, 6u64, EV_CURRENT_D: u8); d_wr16(file_buf, 16u64, ET_EXEC_D); d_wr16(file_buf, 18u64, EM_X86_64_D); d_wr32(file_buf, 20u64, EV_CURRENT_D); d_wr64(file_buf, 24u64, entry); d_wr64(file_buf, 32u64, ehdr_sz); d_wr64(file_buf, 40u64, 0u64); d_wr32(file_buf, 48u64, 0u32); d_wr16(file_buf, 52u64, ehdr_sz: u16); d_wr16(file_buf, 54u64, 56u16); d_wr16(file_buf, 56u64, n_phdrs: u16); d_wr16(file_buf, 58u64, 0u16); d_wr16(file_buf, 60u64, 0u16); d_wr16(file_buf, 62u64, 0u16); // Phdrs at offset 64. let p0: u64 = 64u64; d_wr32(file_buf, p0 + 0u64, PT_LOAD_D); d_wr32(file_buf, p0 + 4u64, PF_R_D | PF_X_D); d_wr64(file_buf, p0 + 8u64, 0u64); d_wr64(file_buf, p0 + 16u64, base); d_wr64(file_buf, p0 + 24u64, base); d_wr64(file_buf, p0 + 32u64, rx_end); d_wr64(file_buf, p0 + 40u64, rx_end); d_wr64(file_buf, p0 + 48u64, PAGE); let p1: u64 = 64u64 + 56u64; d_wr32(file_buf, p1 + 0u64, PT_LOAD_D); d_wr32(file_buf, p1 + 4u64, PF_R_D | PF_W_D); d_wr64(file_buf, p1 + 8u64, gotplt_off); d_wr64(file_buf, p1 + 16u64, gotplt_va); d_wr64(file_buf, p1 + 24u64, gotplt_va); d_wr64(file_buf, p1 + 32u64, file_end - gotplt_off); d_wr64(file_buf, p1 + 40u64, file_end - gotplt_off); d_wr64(file_buf, p1 + 48u64, PAGE); let p2: u64 = 64u64 + 112u64; d_wr32(file_buf, p2 + 0u64, PT_INTERP_D); d_wr32(file_buf, p2 + 4u64, PF_R_D); d_wr64(file_buf, p2 + 8u64, interp_off); d_wr64(file_buf, p2 + 16u64, interp_va); d_wr64(file_buf, p2 + 24u64, interp_va); d_wr64(file_buf, p2 + 32u64, interp_sz); d_wr64(file_buf, p2 + 40u64, interp_sz); d_wr64(file_buf, p2 + 48u64, 1u64); let p3: u64 = 64u64 + 168u64; d_wr32(file_buf, p3 + 0u64, PT_DYNAMIC_D); d_wr32(file_buf, p3 + 4u64, PF_R_D | PF_W_D); d_wr64(file_buf, p3 + 8u64, dynamic_off); d_wr64(file_buf, p3 + 16u64, dynamic_va); d_wr64(file_buf, p3 + 24u64, dynamic_va); d_wr64(file_buf, p3 + 32u64, dynamic_sz); d_wr64(file_buf, p3 + 40u64, dynamic_sz); d_wr64(file_buf, p3 + 48u64, 8u64); // Sections. let interp_local: str = interp_str_v(); d_bcopy(file_buf, interp_off, interp_local.ptr, interp_local.len: u64); d_wr8(file_buf, interp_off + (interp_local.len: u64), 0u8); d_bcopy(file_buf, dynstr_off, dynstr, dynstr_sz); d_bcopy(file_buf, dynsym_off, dynsym_buf, dynsym_sz); d_bcopy(file_buf, hash_off, hash_buf, hash_sz); d_bcopy(file_buf, versym_off, versym_buf, versym_sz); if (verneed_sz > 0u64) { d_bcopy(file_buf, verneed_off, verneed_buf, verneed_sz); }; d_bcopy(file_buf, relaplt_off, relaplt_buf, relaplt_sz); if (l.textlen > 0u64) { d_bcopy(file_buf, text_off, l.text, l.textlen); }; d_bcopy(file_buf, plt_off, plt_buf, plt_sz); d_bcopy(file_buf, gotplt_off, gotplt_buf, gotplt_sz); d_bcopy(file_buf, dynamic_off, dynamic_buf, dynamic_sz); let wrote: i64 = os.writefull(fd, file_buf, file_end); if (wrote != file_end: i64) { return 1; }; return 0; };