// 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) package w6l; import os; import mem; import 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; def INTERP: str = "/lib64/ld-linux-x86-64.so.2"; // ---- byte writers ------------------------------------------------------ fn dwr8(buf: *u8, off: u64, v: u8) void = { buf[off] = v; }; fn dwr16(buf: *u8, off: u64, v: u16) void = { buf[off] = (v & 255u16): u8; buf[off + 1u64] = ((v >> 8u16) & 255u16): u8; }; fn dwr32(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 dwr64(buf: *u8, off: u64, v: u64) void = { dwr32(buf, off, (v & 4294967295u64): u32); dwr32(buf, off + 4u64, ((v >> 32u64) & 4294967295u64): u32); }; fn dwri64(buf: *u8, off: u64, v: i64) void = { dwr64(buf, off, v: u64); }; fn dwri32(buf: *u8, off: u64, v: i32) void = { dwr32(buf, off, v: u32); }; // ---- byte readers ------------------------------------------------------ fn drdu16(p: *u8, off: u64) u16 = { let b0: u16 = p[off]: u16; let b1: u16 = p[off + 1u64]: u16; return b0 | (b1 << 8u16); }; fn drdu32(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 drdi32(p: *u8, off: u64) i32 = { return drdu32(p, off): i32; }; fn dbcopy(dst: *u8, off: u64, src: *u8, n: u64) void = { let i: u64 = 0u64; for (i < n) { dst[off + i] = src[i]; i += 1u64; }; }; // elfhash — SysV ELF hash. Used for .gnu.version_r's vna_hash. fn elfhash(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 alignup(off: u64, a: u64) u64 = { return (off + a - 1u64) & ~(a - 1u64); }; fn streqd(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 = { // .data shares the R+W PT_LOAD with .got.plt and .dynamic. // Placed after .dynamic so the segment is one contiguous run; // relocate runs from here so the dyn layout's datava lands in // patched offsets. 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 maxsos: i32 = 0; let so: *lso = l.sos; for (so != nil) { maxsos += 1; so = so.sonext; }; let sosused: **lso = amalloc(a, (maxsos: 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) { sosused[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 vlibsosidxbuf: *u8 = amalloc(a, (maxsos: u64) * 4u64): *u8; let vlibfirstbuf: *u8 = amalloc(a, (maxsos: u64) * 4u64): *u8; let vlibcountbuf: *u8 = amalloc(a, (maxsos: u64) * 4u64): *u8; let nvlibs: i32 = 0; let verlibidxbuf: *u8 = amalloc(a, nu * 4u64): *u8; let vernameptrarr: **u8 = amalloc(a, nu * 8u64): **u8; let vernamelenbuf: *u8 = amalloc(a, nu * 4u64): *u8; let verdynstroff: *u8 = amalloc(a, nu * 4u64): *u8; let vervnaother: *u8 = amalloc(a, nu * 2u64): *u8; let nvers: i32 = 0; let si: i32 = 0; for (si < nsos) { let curso: *lso = sosused[si]; let has: i32 = 0; let j: i32 = 0; for (j < n) { let dsm: *lsym = dynsyms[j]; let dl: *lso = dsm.dynlib; if (dl == curso) { let nm0: str = dsm.name; let dv: str = soversion(curso, nm0); if (dv.len > 0) { has = 1; j = n; } else { j += 1; }; } else { j += 1; }; }; if (has != 0) { dwr32(vlibsosidxbuf, (nvlibs: u64) * 4u64, si: u32); dwr32(vlibfirstbuf, (nvlibs: u64) * 4u64, nvers: u32); let added: i32 = 0; let jj: i32 = 0; for (jj < n) { let dsm2: *lsym = dynsyms[jj]; let dl2: *lso = dsm2.dynlib; if (dl2 == curso) { let nm2: str = dsm2.name; let vname: str = soversion(curso, nm2); if (vname.len > 0) { let seen: i32 = 0; let k: i32 = 0; for (k < added) { let kk: i32 = nvers - added + k; let existing: str; existing.ptr = vernameptrarr[kk]; existing.len = drdi32(vernamelenbuf, (kk: u64) * 4u64); if (streqd(existing, vname)) { seen = 1; k = added; } else { k += 1; }; }; if (seen == 0) { dwr32(verlibidxbuf, (nvers: u64) * 4u64, nvlibs: u32); vernameptrarr[nvers] = vname.ptr; dwri32(vernamelenbuf, (nvers: u64) * 4u64, vname.len); nvers += 1; added += 1; }; }; }; jj += 1; }; dwr32(vlibcountbuf, (nvlibs: u64) * 4u64, added: u32); nvlibs += 1; }; si += 1; }; // Assign vna_other indices starting at 2, walking vlib then per-version. let nextvna: u16 = 2u16; let vi: i32 = 0; for (vi < nvlibs) { let first: i32 = drdi32(vlibfirstbuf, (vi: u64) * 4u64); let cnt: i32 = drdi32(vlibcountbuf, (vi: u64) * 4u64); let k: i32 = 0; for (k < cnt) { dwr16(vervnaother, ((first + k): u64) * 2u64, nextvna); nextvna += 1u16; k += 1; }; vi += 1; }; // ---- compute dynstr size ---- let dynstrsz: u64 = 1u64; // leading NUL let pi: i32 = 0; for (pi < nsos) { let so4: *lso = sosused[pi]; dynstrsz += so4.soname.len: u64; dynstrsz += 1u64; pi += 1; }; pi = 0; for (pi < n) { let dsm4: *lsym = dynsyms[pi]; dynstrsz += dsm4.name.len: u64; dynstrsz += 1u64; pi += 1; }; pi = 0; for (pi < nvers) { let nmlen: i32 = drdi32(vernamelenbuf, (pi: u64) * 4u64); dynstrsz += nmlen: u64; dynstrsz += 1u64; pi += 1; }; // ---- fill dynstr ---- let dynstr: *u8 = amalloc(a, dynstrsz): *u8; let dynstrpos: u64 = 1u64; // past leading NUL let sonamestr: *u8 = amalloc(a, (nsos: u64) * 4u64): *u8; pi = 0; for (pi < nsos) { dwr32(sonamestr, (pi: u64) * 4u64, dynstrpos: u32); let so2: *lso = sosused[pi]; let snm: str = so2.soname; dbcopy(dynstr, dynstrpos, snm.ptr, snm.len: u64); dynstrpos += snm.len: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; let symnamestr: *u8 = amalloc(a, nu * 4u64): *u8; pi = 0; for (pi < n) { dwr32(symnamestr, (pi: u64) * 4u64, dynstrpos: u32); let dsm: *lsym = dynsyms[pi]; let snm: str = dsm.name; dbcopy(dynstr, dynstrpos, snm.ptr, snm.len: u64); dynstrpos += snm.len: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; pi = 0; for (pi < nvers) { dwr32(verdynstroff, (pi: u64) * 4u64, dynstrpos: u32); let nmp: *u8 = vernameptrarr[pi]; let nmlen: i32 = drdi32(vernamelenbuf, (pi: u64) * 4u64); dbcopy(dynstr, dynstrpos, nmp, nmlen: u64); dynstrpos += nmlen: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; // ---- per-dyn-sym versym index ---- let versymfor: *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) { dwr16(versymfor, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); } else { let matched: i32 = 0; let vk: i32 = 0; for (vk < nvers) { let vlx: i32 = drdi32(verlibidxbuf, (vk: u64) * 4u64); let sosx: i32 = drdi32(vlibsosidxbuf, (vlx: u64) * 4u64); if (sosused[sosx] == dl3) { let exi: str; exi.ptr = vernameptrarr[vk]; exi.len = drdi32(vernamelenbuf, (vk: u64) * 4u64); if (streqd(exi, vname)) { let other: u16 = drdu16(vervnaother, (vk: u64) * 2u64); dwr16(versymfor, (pi: u64) * 2u64, other); matched = 1; vk = nvers; } else { vk += 1; }; } else { vk += 1; }; }; if (matched == 0) { dwr16(versymfor, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); }; }; pi += 1; }; // ---- compute byte sizes ---- let ehdrsz: u64 = 64u64; let nphdrs: u64 = 4u64; let phdrsz: u64 = nphdrs * 56u64; let interpsz: u64 = (INTERP.len: u64) + 1u64; let nsymstotal: u64 = 1u64 + nu; let dynsymsz: u64 = nsymstotal * 24u64; let nbuckets: u32 = 1u32; let nchain: u32 = nsymstotal: u32; let hashsz: u64 = (2u64 + (nbuckets: u64) + (nchain: u64)) * 4u64; let relapltsz: u64 = nu * 24u64; let pltsz: u64 = nu * PLT_STUB_BYTES_D; let gotpltsz: u64 = (3u64 + nu) * 8u64; let versymsz: u64 = nsymstotal * 2u64; let verneedsz: u64 = 0u64; let vli: i32 = 0; for (vli < nvlibs) { let cnt: i32 = drdi32(vlibcountbuf, (vli: u64) * 4u64); verneedsz += 16u64 + 16u64 * (cnt: u64); vli += 1; }; let withver: i32 = 0; if (nvlibs > 0) { withver = 1; }; let extra: u64 = 0u64; if (withver != 0) { extra = 3u64; }; let ndyn: u64 = (nsos: u64) + 11u64 + extra; let dynamicsz: u64 = ndyn * 16u64; // ---- compute file offsets ---- let off: u64 = ehdrsz + phdrsz; let interpoff: u64 = off; off += interpsz; off = alignup(off, 8u64); let dynstroff: u64 = off; off += dynstrsz; off = alignup(off, 8u64); let dynsymoff: u64 = off; off += dynsymsz; let hashoff: u64 = off; off += hashsz; off = alignup(off, 2u64); let versymoff: u64 = off; off += versymsz; off = alignup(off, 4u64); let verneedoff: u64 = off; off += verneedsz; off = alignup(off, 8u64); let relapltoff: u64 = off; off += relapltsz; let textoff: u64 = alignup(off, PAGE); let pltoff: u64 = textoff + l.textlen; let rxend: u64 = pltoff + pltsz; let gotpltoff: u64 = alignup(rxend, PAGE); let dynamicoff: u64 = gotpltoff + gotpltsz; let dataoff: u64 = dynamicoff + dynamicsz; let fileend: u64 = dataoff + l.datalen; let interpva: u64 = base + interpoff; let dynstrva: u64 = base + dynstroff; let dynsymva: u64 = base + dynsymoff; let hashva: u64 = base + hashoff; let versymva: u64 = base + versymoff; let verneedva: u64 = base + verneedoff; let relapltva: u64 = base + relapltoff; let textva: u64 = base + textoff; let pltva: u64 = base + pltoff; let gotpltva: u64 = base + gotpltoff; let dynamicva: u64 = base + dynamicoff; let datava: u64 = base + dataoff; // Apply relocations now that the dyn layout's textva/datava are // pinned. main.ww defers this so each path uses its own VAs. if (relocate(l, textva, datava) != 0) { return 1; }; // BSS optimisation — same trailing-zero scan as out.ww. let bsslen: u64 = 0u64; if (l.datalen > 0u64) { for (bsslen < l.datalen) { let b: u8 = l.data[l.datalen - 1u64 - bsslen]; if (b != 0u8) { break; }; bsslen += 1u64; }; }; let datafilelen: u64 = l.datalen - bsslen; let filedataend: u64 = dataoff + datafilelen; // ---- build .dynsym ---- let dynsymbuf: *u8 = amalloc(a, dynsymsz): *u8; pi = 0; for (pi < n) { let eoff: u64 = (1u64 + (pi: u64)) * 24u64; dwr32(dynsymbuf, eoff + 0u64, drdu32(symnamestr, (pi: u64) * 4u64)); dwr8(dynsymbuf, eoff + 4u64, (STB_GLOBAL_D << 4u8) | (STT_FUNC_D & 15u8)); dwr8(dynsymbuf, eoff + 5u64, 0u8); dwr16(dynsymbuf, eoff + 6u64, 0u16); dwr64(dynsymbuf, eoff + 8u64, 0u64); dwr64(dynsymbuf, eoff + 16u64, 0u64); pi += 1; }; // ---- build .hash (SysV, 1 bucket) ---- let hashbuf: *u8 = amalloc(a, hashsz): *u8; dwr32(hashbuf, 0u64, nbuckets); dwr32(hashbuf, 4u64, nchain); let bucket0: u32 = 0u32; if (nsymstotal > 1u64) { bucket0 = 1u32; }; dwr32(hashbuf, 8u64, bucket0); let ci: u64 = 1u64; for (ci < nsymstotal) { let nxt: u32 = 0u32; if (ci + 1u64 < nsymstotal) { nxt = (ci + 1u64): u32; }; dwr32(hashbuf, 8u64 + (nbuckets: u64) * 4u64 + ci * 4u64, nxt); ci += 1u64; }; // ---- build .rela.plt ---- let relapltbuf: *u8 = amalloc(a, relapltsz): *u8; pi = 0; for (pi < n) { let roff: u64 = (pi: u64) * 24u64; dwr64(relapltbuf, roff + 0u64, gotpltva + (3u64 + (pi: u64)) * 8u64); let info: u64 = ((1u64 + (pi: u64)) << 32u64) | (R_X86_64_JUMP_SLOT_D: u64); dwr64(relapltbuf, roff + 8u64, info); dwri64(relapltbuf, roff + 16u64, 0i64); pi += 1; }; // ---- build .gnu.version (u16 per dynsym entry) ---- let versymbuf: *u8 = amalloc(a, versymsz): *u8; dwr16(versymbuf, 0u64, VER_NDX_LOCAL_D); pi = 0; for (pi < n) { dwr16(versymbuf, 2u64 + (pi: u64) * 2u64, drdu16(versymfor, (pi: u64) * 2u64)); pi += 1; }; // ---- build .gnu.version_r ---- let verneedbuf: *u8 = amalloc(a, verneedsz): *u8; if (verneedsz > 0u64) { let vnoff: u64 = 0u64; vli = 0; for (vli < nvlibs) { let sosidx: i32 = drdi32(vlibsosidxbuf, (vli: u64) * 4u64); let first: i32 = drdi32(vlibfirstbuf, (vli: u64) * 4u64); let cnt: i32 = drdi32(vlibcountbuf, (vli: u64) * 4u64); let vnstart: u64 = vnoff; dwr16(verneedbuf, vnoff + 0u64, 1u16); dwr16(verneedbuf, vnoff + 2u64, cnt: u16); dwr32(verneedbuf, vnoff + 4u64, drdu32(sonamestr, (sosidx: u64) * 4u64)); dwr32(verneedbuf, vnoff + 8u64, 16u32); vnoff += 16u64; let k: i32 = 0; for (k < cnt) { let vk: i32 = first + k; let nm: str; nm.ptr = vernameptrarr[vk]; nm.len = drdi32(vernamelenbuf, (vk: u64) * 4u64); let h: u32 = elfhash(nm); dwr32(verneedbuf, vnoff + 0u64, h); dwr16(verneedbuf, vnoff + 4u64, 0u16); dwr16(verneedbuf, vnoff + 6u64, drdu16(vervnaother, (vk: u64) * 2u64)); dwr32(verneedbuf, vnoff + 8u64, drdu32(verdynstroff, (vk: u64) * 4u64)); let nxt: u32 = 0u32; if (k + 1 < cnt) { nxt = 16u32; }; dwr32(verneedbuf, vnoff + 12u64, nxt); vnoff += 16u64; k += 1; }; let vnnxt: u32 = 0u32; if (vli + 1 < nvlibs) { vnnxt = (vnoff - vnstart): u32; }; dwr32(verneedbuf, vnstart + 12u64, vnnxt); vli += 1; }; }; // ---- build .plt ---- let pltbuf: *u8 = amalloc(a, pltsz): *u8; pi = 0; for (pi < n) { let poff: u64 = (pi: u64) * PLT_STUB_BYTES_D; let stubva: u64 = pltva + poff; let nextip: u64 = stubva + 6u64; let slotva: u64 = gotpltva + (3u64 + (pi: u64)) * 8u64; let disp: i64 = (slotva: i64) - (nextip: i64); dwr8(pltbuf, poff + 0u64, 255u8); dwr8(pltbuf, poff + 1u64, 37u8); dwr32(pltbuf, poff + 2u64, (disp: i32): u32); pi += 1; }; // ---- build .got.plt ---- let gotpltbuf: *u8 = amalloc(a, gotpltsz): *u8; dwr64(gotpltbuf, 0u64, dynamicva); // ---- build .dynamic ---- let dynamicbuf: *u8 = amalloc(a, dynamicsz): *u8; let dk: u64 = 0u64; pi = 0; for (pi < nsos) { dwri64(dynamicbuf, dk * 16u64 + 0u64, DT_NEEDED); dwr64(dynamicbuf, dk * 16u64 + 8u64, drdu32(sonamestr, (pi: u64) * 4u64): u64); dk += 1u64; pi += 1; }; dwri64(dynamicbuf, dk * 16u64, DT_HASH); dwr64(dynamicbuf, dk * 16u64 + 8u64, hashva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_STRTAB); dwr64(dynamicbuf, dk * 16u64 + 8u64, dynstrva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_SYMTAB); dwr64(dynamicbuf, dk * 16u64 + 8u64, dynsymva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_STRSZ); dwr64(dynamicbuf, dk * 16u64 + 8u64, dynstrsz); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_SYMENT); dwr64(dynamicbuf, dk * 16u64 + 8u64, 24u64); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_PLTGOT); dwr64(dynamicbuf, dk * 16u64 + 8u64, gotpltva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_PLTRELSZ); dwr64(dynamicbuf, dk * 16u64 + 8u64, relapltsz); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_PLTREL); dwr64(dynamicbuf, dk * 16u64 + 8u64, DT_RELA: u64);dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_JMPREL); dwr64(dynamicbuf, dk * 16u64 + 8u64, relapltva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_BIND_NOW); dwr64(dynamicbuf, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (withver != 0) { dwri64(dynamicbuf, dk * 16u64, DT_VERSYM); dwr64(dynamicbuf, dk * 16u64 + 8u64, versymva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_VERNEED); dwr64(dynamicbuf, dk * 16u64 + 8u64, verneedva); dk += 1u64; dwri64(dynamicbuf, dk * 16u64, DT_VERNEEDNUM); dwr64(dynamicbuf, dk * 16u64 + 8u64, nvlibs: u64);dk += 1u64; }; dwri64(dynamicbuf, dk * 16u64, DT_NULL); dwr64(dynamicbuf, 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 = textva + r.off; let stub: u64 = pltva + (rsym.pltidx: u64) * PLT_STUB_BYTES_D; let disp: i64 = (stub: i64) - (site: i64) + r.addend; dwr32(l.text, r.off, (disp: i32): u32); }; }; r = r.rnext; }; // ---- assemble file buffer ---- let filebuf: *u8 = os.alloc(fileend): *u8; if (filebuf == nil) { os.write(2, "w6l: out of memory\n".ptr, 18u64); return 1; }; // Ehdr dwr8(filebuf, 0u64, 127u8); dwr8(filebuf, 1u64, 69u8); dwr8(filebuf, 2u64, 76u8); dwr8(filebuf, 3u64, 70u8); dwr8(filebuf, 4u64, ELFCLASS64_D); dwr8(filebuf, 5u64, ELFDATA2LSB_D); dwr8(filebuf, 6u64, EV_CURRENT_D: u8); dwr16(filebuf, 16u64, ET_EXEC_D); dwr16(filebuf, 18u64, EM_X86_64_D); dwr32(filebuf, 20u64, EV_CURRENT_D); dwr64(filebuf, 24u64, entry); dwr64(filebuf, 32u64, ehdrsz); dwr64(filebuf, 40u64, 0u64); dwr32(filebuf, 48u64, 0u32); dwr16(filebuf, 52u64, ehdrsz: u16); dwr16(filebuf, 54u64, 56u16); dwr16(filebuf, 56u64, nphdrs: u16); dwr16(filebuf, 58u64, 0u16); dwr16(filebuf, 60u64, 0u16); dwr16(filebuf, 62u64, 0u16); // Phdrs at offset 64. let p0: u64 = 64u64; dwr32(filebuf, p0 + 0u64, PT_LOAD_D); dwr32(filebuf, p0 + 4u64, PF_R_D | PF_X_D); dwr64(filebuf, p0 + 8u64, 0u64); dwr64(filebuf, p0 + 16u64, base); dwr64(filebuf, p0 + 24u64, base); dwr64(filebuf, p0 + 32u64, rxend); dwr64(filebuf, p0 + 40u64, rxend); dwr64(filebuf, p0 + 48u64, PAGE); let p1: u64 = 64u64 + 56u64; dwr32(filebuf, p1 + 0u64, PT_LOAD_D); dwr32(filebuf, p1 + 4u64, PF_R_D | PF_W_D); dwr64(filebuf, p1 + 8u64, gotpltoff); dwr64(filebuf, p1 + 16u64, gotpltva); dwr64(filebuf, p1 + 24u64, gotpltva); // filesz trims the .data trailing zeros (BSS); memsz covers // .got.plt + .dynamic + the full .data so the loader zero-fills. dwr64(filebuf, p1 + 32u64, filedataend - gotpltoff); dwr64(filebuf, p1 + 40u64, fileend - gotpltoff); dwr64(filebuf, p1 + 48u64, PAGE); let p2: u64 = 64u64 + 112u64; dwr32(filebuf, p2 + 0u64, PT_INTERP_D); dwr32(filebuf, p2 + 4u64, PF_R_D); dwr64(filebuf, p2 + 8u64, interpoff); dwr64(filebuf, p2 + 16u64, interpva); dwr64(filebuf, p2 + 24u64, interpva); dwr64(filebuf, p2 + 32u64, interpsz); dwr64(filebuf, p2 + 40u64, interpsz); dwr64(filebuf, p2 + 48u64, 1u64); let p3: u64 = 64u64 + 168u64; dwr32(filebuf, p3 + 0u64, PT_DYNAMIC_D); dwr32(filebuf, p3 + 4u64, PF_R_D | PF_W_D); dwr64(filebuf, p3 + 8u64, dynamicoff); dwr64(filebuf, p3 + 16u64, dynamicva); dwr64(filebuf, p3 + 24u64, dynamicva); dwr64(filebuf, p3 + 32u64, dynamicsz); dwr64(filebuf, p3 + 40u64, dynamicsz); dwr64(filebuf, p3 + 48u64, 8u64); // Sections. dbcopy(filebuf, interpoff, INTERP.ptr, INTERP.len: u64); dwr8(filebuf, interpoff + (INTERP.len: u64), 0u8); dbcopy(filebuf, dynstroff, dynstr, dynstrsz); dbcopy(filebuf, dynsymoff, dynsymbuf, dynsymsz); dbcopy(filebuf, hashoff, hashbuf, hashsz); dbcopy(filebuf, versymoff, versymbuf, versymsz); if (verneedsz > 0u64) { dbcopy(filebuf, verneedoff, verneedbuf, verneedsz); }; dbcopy(filebuf, relapltoff, relapltbuf, relapltsz); if (l.textlen > 0u64) { dbcopy(filebuf, textoff, l.text, l.textlen); }; dbcopy(filebuf, pltoff, pltbuf, pltsz); dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz); dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz); if (datafilelen > 0u64) { dbcopy(filebuf, dataoff, l.data, datafilelen); }; let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, filedataend); match (wr) { case let v: i64 => { if (v != filedataend: i64) { return 1; }; }; case let e: os.oserror => return 1; }; return 0; };