// 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 main; import os; import rt; import strings; 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"; 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); }; 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); }; 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 n: i32 = l.dynn; let nu: u64 = n: u64; let dynsyms: []*lsym = alloc([], nu)!; 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) { let wm1: str = "w6l: dynout: no sym for plt_idx\n"; os.write(2, wm1.ptr, wm1.len: u64); return 1; }; i += 1; }; // Used .so's are collected in l.sos order. let maxsos: i32 = 0; let so: *lso = l.sos; for (so != nil) { maxsos += 1; so = so.sonext; }; let sosused: []*lso = alloc([], maxsos: u64)!; 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; }; // 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 = alloc([], (maxsos: u64) * 4u64)!; let vlibfirstbuf: []u8 = alloc([], (maxsos: u64) * 4u64)!; let vlibcountbuf: []u8 = alloc([], (maxsos: u64) * 4u64)!; let nvlibs: i32 = 0; let verlibidxbuf: []u8 = alloc([], nu * 4u64)!; let vernameptrarr: []*u8 = alloc([], nu)!; let vernamelenbuf: []u8 = alloc([], nu * 4u64)!; let verdynstroff: []u8 = alloc([], nu * 4u64)!; let vervnaother: []u8 = alloc([], nu * 2u64)!; 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.ptr, (nvlibs: u64) * 4u64, si: u32); dwr32(vlibfirstbuf.ptr, (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.ptr, (kk: u64) * 4u64); if (strings.compare(existing, vname) == 0) { seen = 1; k = added; } else { k += 1; }; }; if (seen == 0) { dwr32(verlibidxbuf.ptr, (nvers: u64) * 4u64, nvlibs: u32); vernameptrarr[nvers] = vname.ptr; dwri32(vernamelenbuf.ptr, (nvers: u64) * 4u64, vname.len); nvers += 1; added += 1; }; }; }; jj += 1; }; dwr32(vlibcountbuf.ptr, (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.ptr, (vi: u64) * 4u64); let cnt: i32 = drdi32(vlibcountbuf.ptr, (vi: u64) * 4u64); let k: i32 = 0; for (k < cnt) { dwr16(vervnaother.ptr, ((first + k): u64) * 2u64, nextvna); nextvna += 1u16; k += 1; }; vi += 1; }; 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.ptr, (pi: u64) * 4u64); dynstrsz += nmlen: u64; dynstrsz += 1u64; pi += 1; }; let dynstr: []u8 = alloc([], dynstrsz)!; let dynstrpos: u64 = 1u64; // past leading NUL let sonamestr: []u8 = alloc([], (nsos: u64) * 4u64)!; pi = 0; for (pi < nsos) { dwr32(sonamestr.ptr, (pi: u64) * 4u64, dynstrpos: u32); let so2: *lso = sosused[pi]; let snm: str = so2.soname; dbcopy(dynstr.ptr, dynstrpos, snm.ptr, snm.len: u64); dynstrpos += snm.len: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; let symnamestr: []u8 = alloc([], nu * 4u64)!; pi = 0; for (pi < n) { dwr32(symnamestr.ptr, (pi: u64) * 4u64, dynstrpos: u32); let dsm: *lsym = dynsyms[pi]; let snm: str = dsm.name; dbcopy(dynstr.ptr, dynstrpos, snm.ptr, snm.len: u64); dynstrpos += snm.len: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; pi = 0; for (pi < nvers) { dwr32(verdynstroff.ptr, (pi: u64) * 4u64, dynstrpos: u32); let nmp: *u8 = vernameptrarr[pi]; let nmlen: i32 = drdi32(vernamelenbuf.ptr, (pi: u64) * 4u64); dbcopy(dynstr.ptr, dynstrpos, nmp, nmlen: u64); dynstrpos += nmlen: u64; dynstr[dynstrpos] = 0u8; dynstrpos += 1u64; pi += 1; }; let versymfor: []u8 = alloc([], nu * 2u64)!; 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.ptr, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); } else { let matched: i32 = 0; let vk: i32 = 0; for (vk < nvers) { let vlx: i32 = drdi32(verlibidxbuf.ptr, (vk: u64) * 4u64); let sosx: i32 = drdi32(vlibsosidxbuf.ptr, (vlx: u64) * 4u64); if (sosused[sosx] == dl3) { let exi: str; exi.ptr = vernameptrarr[vk]; exi.len = drdi32(vernamelenbuf.ptr, (vk: u64) * 4u64); if (strings.compare(exi, vname) == 0) { let other: u16 = drdu16(vervnaother.ptr, (vk: u64) * 2u64); dwr16(versymfor.ptr, (pi: u64) * 2u64, other); matched = 1; vk = nvers; } else { vk += 1; }; } else { vk += 1; }; }; if (matched == 0) { dwr16(versymfor.ptr, (pi: u64) * 2u64, VER_NDX_GLOBAL_D); }; }; pi += 1; }; 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.ptr, (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; 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; let dynsymbuf: []u8 = alloc([], dynsymsz)!; pi = 0; for (pi < n) { let eoff: u64 = (1u64 + (pi: u64)) * 24u64; dwr32(dynsymbuf.ptr, eoff + 0u64, drdu32(symnamestr.ptr, (pi: u64) * 4u64)); dwr8(dynsymbuf.ptr, eoff + 4u64, (STB_GLOBAL_D << 4u8) | (STT_FUNC_D & 15u8)); dwr8(dynsymbuf.ptr, eoff + 5u64, 0u8); dwr16(dynsymbuf.ptr, eoff + 6u64, 0u16); dwr64(dynsymbuf.ptr, eoff + 8u64, 0u64); dwr64(dynsymbuf.ptr, eoff + 16u64, 0u64); pi += 1; }; // .hash is SysV with a single bucket. let hashbuf: []u8 = alloc([], hashsz)!; dwr32(hashbuf.ptr, 0u64, nbuckets); dwr32(hashbuf.ptr, 4u64, nchain); let bucket0: u32 = 0u32; if (nsymstotal > 1u64) { bucket0 = 1u32; }; dwr32(hashbuf.ptr, 8u64, bucket0); let ci: u64 = 1u64; for (ci < nsymstotal) { let nxt: u32 = 0u32; if (ci + 1u64 < nsymstotal) { nxt = (ci + 1u64): u32; }; dwr32(hashbuf.ptr, 8u64 + (nbuckets: u64) * 4u64 + ci * 4u64, nxt); ci += 1u64; }; let relapltbuf: []u8 = alloc([], relapltsz)!; pi = 0; for (pi < n) { let roff: u64 = (pi: u64) * 24u64; dwr64(relapltbuf.ptr, roff + 0u64, gotpltva + (3u64 + (pi: u64)) * 8u64); let info: u64 = ((1u64 + (pi: u64)) << 32u64) | (R_X86_64_JUMP_SLOT_D: u64); dwr64(relapltbuf.ptr, roff + 8u64, info); dwri64(relapltbuf.ptr, roff + 16u64, 0i64); pi += 1; }; // .gnu.version is one u16 per dynsym entry. let versymbuf: []u8 = alloc([], versymsz)!; dwr16(versymbuf.ptr, 0u64, VER_NDX_LOCAL_D); pi = 0; for (pi < n) { dwr16(versymbuf.ptr, 2u64 + (pi: u64) * 2u64, drdu16(versymfor.ptr, (pi: u64) * 2u64)); pi += 1; }; let verneedbuf: []u8 = alloc([], verneedsz)!; if (verneedsz > 0u64) { let vnoff: u64 = 0u64; vli = 0; for (vli < nvlibs) { let sosidx: i32 = drdi32(vlibsosidxbuf.ptr, (vli: u64) * 4u64); let first: i32 = drdi32(vlibfirstbuf.ptr, (vli: u64) * 4u64); let cnt: i32 = drdi32(vlibcountbuf.ptr, (vli: u64) * 4u64); let vnstart: u64 = vnoff; dwr16(verneedbuf.ptr, vnoff + 0u64, 1u16); dwr16(verneedbuf.ptr, vnoff + 2u64, cnt: u16); dwr32(verneedbuf.ptr, vnoff + 4u64, drdu32(sonamestr.ptr, (sosidx: u64) * 4u64)); dwr32(verneedbuf.ptr, 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.ptr, (vk: u64) * 4u64); let h: u32 = elfhash(nm); dwr32(verneedbuf.ptr, vnoff + 0u64, h); dwr16(verneedbuf.ptr, vnoff + 4u64, 0u16); dwr16(verneedbuf.ptr, vnoff + 6u64, drdu16(vervnaother.ptr, (vk: u64) * 2u64)); dwr32(verneedbuf.ptr, vnoff + 8u64, drdu32(verdynstroff.ptr, (vk: u64) * 4u64)); let nxt: u32 = 0u32; if (k + 1 < cnt) { nxt = 16u32; }; dwr32(verneedbuf.ptr, vnoff + 12u64, nxt); vnoff += 16u64; k += 1; }; let vnnxt: u32 = 0u32; if (vli + 1 < nvlibs) { vnnxt = (vnoff - vnstart): u32; }; dwr32(verneedbuf.ptr, vnstart + 12u64, vnnxt); vli += 1; }; }; let pltbuf: []u8 = alloc([], pltsz)!; 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.ptr, poff + 0u64, 255u8); dwr8(pltbuf.ptr, poff + 1u64, 37u8); dwr32(pltbuf.ptr, poff + 2u64, (disp: i32): u32); pi += 1; }; let gotpltbuf: []u8 = alloc([], gotpltsz)!; dwr64(gotpltbuf.ptr, 0u64, dynamicva); let dynamicbuf: []u8 = alloc([], dynamicsz)!; let dk: u64 = 0u64; pi = 0; for (pi < nsos) { dwri64(dynamicbuf.ptr, dk * 16u64 + 0u64, DT_NEEDED); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, drdu32(sonamestr.ptr, (pi: u64) * 4u64): u64); dk += 1u64; pi += 1; }; dwri64(dynamicbuf.ptr, dk * 16u64, DT_HASH); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, hashva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_STRTAB); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, dynstrva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_SYMTAB); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, dynsymva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_STRSZ); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, dynstrsz); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_SYMENT); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, 24u64); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_PLTGOT); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, gotpltva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_PLTRELSZ); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, relapltsz); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_PLTREL); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, DT_RELA: u64);dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_JMPREL); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, relapltva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_BIND_NOW); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (withver != 0) { dwri64(dynamicbuf.ptr, dk * 16u64, DT_VERSYM); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, versymva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_VERNEED); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, verneedva); dk += 1u64; dwri64(dynamicbuf.ptr, dk * 16u64, DT_VERNEEDNUM); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, nvlibs: u64);dk += 1u64; }; dwri64(dynamicbuf.ptr, dk * 16u64, DT_NULL); dwr64(dynamicbuf.ptr, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (dk != ndyn) { let wm2: str = "w6l: dynamic entry count mismatch\n"; os.write(2, wm2.ptr, wm2.len: u64); return 1; }; 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) { let wm3: str = "w6l: dynamic reloc kind unsupported\n"; os.write(2, wm3.ptr, wm3.len: u64); 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; }; let filebuf: []u8 = alloc([], fileend)!; filebuf.len = fileend: i32; // Ehdr dwr8(filebuf.ptr, 0u64, 127u8); dwr8(filebuf.ptr, 1u64, 69u8); dwr8(filebuf.ptr, 2u64, 76u8); dwr8(filebuf.ptr, 3u64, 70u8); dwr8(filebuf.ptr, 4u64, ELFCLASS64_D); dwr8(filebuf.ptr, 5u64, ELFDATA2LSB_D); dwr8(filebuf.ptr, 6u64, EV_CURRENT_D: u8); dwr16(filebuf.ptr, 16u64, ET_EXEC_D); dwr16(filebuf.ptr, 18u64, EM_X86_64_D); dwr32(filebuf.ptr, 20u64, EV_CURRENT_D); // #63: main computed `entry` as BASE + CODE_VA_OFF (== PAGE) + val // assuming .text sits at file offset 0x1000, but textoff above is // recomputed and overflows 0x1000 once the dynamic headers exceed the // first page (e.g. ~100 dyn syms). Rebase e_entry onto the actual textoff // so it points at the real _start instead of into the headers. Both // stages (#263). PAGE is dynout-local; equals main.ww's CODE_VA_OFF. dwr64(filebuf.ptr, 24u64, entry - PAGE + textoff); dwr64(filebuf.ptr, 32u64, ehdrsz); dwr64(filebuf.ptr, 40u64, 0u64); dwr32(filebuf.ptr, 48u64, 0u32); dwr16(filebuf.ptr, 52u64, ehdrsz: u16); dwr16(filebuf.ptr, 54u64, 56u16); dwr16(filebuf.ptr, 56u64, nphdrs: u16); dwr16(filebuf.ptr, 58u64, 0u16); dwr16(filebuf.ptr, 60u64, 0u16); dwr16(filebuf.ptr, 62u64, 0u16); // Phdrs at offset 64. let p0: u64 = 64u64; dwr32(filebuf.ptr, p0 + 0u64, PT_LOAD_D); dwr32(filebuf.ptr, p0 + 4u64, PF_R_D | PF_X_D); dwr64(filebuf.ptr, p0 + 8u64, 0u64); dwr64(filebuf.ptr, p0 + 16u64, base); dwr64(filebuf.ptr, p0 + 24u64, base); dwr64(filebuf.ptr, p0 + 32u64, rxend); // The loader page-rounds mappings; gotpltoff independently starts // the next page, so padding is not part of the R+X extent. dwr64(filebuf.ptr, p0 + 40u64, rxend); dwr64(filebuf.ptr, p0 + 48u64, PAGE); let p1: u64 = 64u64 + 56u64; dwr32(filebuf.ptr, p1 + 0u64, PT_LOAD_D); dwr32(filebuf.ptr, p1 + 4u64, PF_R_D | PF_W_D); dwr64(filebuf.ptr, p1 + 8u64, gotpltoff); dwr64(filebuf.ptr, p1 + 16u64, gotpltva); dwr64(filebuf.ptr, 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.ptr, p1 + 32u64, filedataend - gotpltoff); dwr64(filebuf.ptr, p1 + 40u64, fileend - gotpltoff); dwr64(filebuf.ptr, p1 + 48u64, PAGE); let p2: u64 = 64u64 + 112u64; dwr32(filebuf.ptr, p2 + 0u64, PT_INTERP_D); dwr32(filebuf.ptr, p2 + 4u64, PF_R_D); dwr64(filebuf.ptr, p2 + 8u64, interpoff); dwr64(filebuf.ptr, p2 + 16u64, interpva); dwr64(filebuf.ptr, p2 + 24u64, interpva); dwr64(filebuf.ptr, p2 + 32u64, interpsz); dwr64(filebuf.ptr, p2 + 40u64, interpsz); dwr64(filebuf.ptr, p2 + 48u64, 1u64); let p3: u64 = 64u64 + 168u64; dwr32(filebuf.ptr, p3 + 0u64, PT_DYNAMIC_D); dwr32(filebuf.ptr, p3 + 4u64, PF_R_D | PF_W_D); dwr64(filebuf.ptr, p3 + 8u64, dynamicoff); dwr64(filebuf.ptr, p3 + 16u64, dynamicva); dwr64(filebuf.ptr, p3 + 24u64, dynamicva); dwr64(filebuf.ptr, p3 + 32u64, dynamicsz); dwr64(filebuf.ptr, p3 + 40u64, dynamicsz); dwr64(filebuf.ptr, p3 + 48u64, 8u64); dbcopy(filebuf.ptr, interpoff, INTERP.ptr, INTERP.len: u64); dwr8(filebuf.ptr, interpoff + (INTERP.len: u64), 0u8); dbcopy(filebuf.ptr, dynstroff, dynstr.ptr, dynstrsz); dbcopy(filebuf.ptr, dynsymoff, dynsymbuf.ptr, dynsymsz); dbcopy(filebuf.ptr, hashoff, hashbuf.ptr, hashsz); dbcopy(filebuf.ptr, versymoff, versymbuf.ptr, versymsz); if (verneedsz > 0u64) { dbcopy(filebuf.ptr, verneedoff, verneedbuf.ptr, verneedsz); }; dbcopy(filebuf.ptr, relapltoff, relapltbuf.ptr, relapltsz); if (l.textlen > 0u64) { dbcopy(filebuf.ptr, textoff, l.text, l.textlen); }; dbcopy(filebuf.ptr, pltoff, pltbuf.ptr, pltsz); dbcopy(filebuf.ptr, gotpltoff, gotpltbuf.ptr, gotpltsz); dbcopy(filebuf.ptr, dynamicoff, dynamicbuf.ptr, dynamicsz); if (datafilelen > 0u64) { dbcopy(filebuf.ptr, dataoff, l.data, datafilelen); }; let wr: (i64 | os.oserror) = os.writeall(fd, filebuf.ptr, filedataend); match (wr) { case let v: i64 => { if (v != filedataend: i64) { return 1; }; }; case let e: os.oserror => return 1; }; return 0; };