mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
Announce the interpreter as mapped code, so ld.so can have a unit
gex's loader places ld.so at INTERP_BASE itself, so no guest mmap reported it and the ELF-unit hook (nt_cgen::on_elf_maps, cgen::libprov) never saw it: it always ran on the runtime translator (1.8 MiB of it at Hollow Knight's first frame in the page). Both hosts now push CodeMap::of_placed for its executable segments into Kernel::new_code at load, and the drain after the first syscall places its unit like any library's (GEX_ELF_UNITS / ?elfunits=ld-linux-x86-64.so.2). tests/elf-units-remap builds the interpreter's unit at 0x4a00000 too and checks it is placed once. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_1896c5b302a54696aaa92c5f4047f20b
This commit is contained in:
1 parent
1fc1a8dd89
commit
29cbcc4dc6
4 files changed
+65
-4
No files matched your search
@@ -1071,6 +1071,44 @@ pub struct CodeMap {
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
impl CodeMap {
|
||||
/// The executable segments of an ELF object gex's own loader placed at
|
||||
/// load bias `bias` (the interpreter, at exec): what its `mmap` would have
|
||||
/// reported had `ld.so` mapped it, so a host's ELF units see it too.
|
||||
pub fn of_placed(path: &str, bytes: &[u8], bias: u64) -> Vec<CodeMap> {
|
||||
let rd = |o: usize, n: usize| -> Option<u64> {
|
||||
let b = bytes.get(o..o + n)?;
|
||||
let mut v = [0u8; 8];
|
||||
v[..n].copy_from_slice(b);
|
||||
Some(u64::from_le_bytes(v))
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
if bytes.get(0..4) != Some(b"\x7fELF") || bytes.get(4) != Some(&2) {
|
||||
return out;
|
||||
}
|
||||
let (Some(phoff), Some(phentsize), Some(phnum)) = (rd(0x20, 8), rd(0x36, 2), rd(0x38, 2)) else {
|
||||
return out;
|
||||
};
|
||||
for i in 0..phnum as usize {
|
||||
let ph = phoff as usize + i * phentsize as usize;
|
||||
let (Some(ty), Some(flags), Some(off), Some(vaddr), Some(filesz)) =
|
||||
(rd(ph, 4), rd(ph + 4, 4), rd(ph + 8, 8), rd(ph + 16, 8), rd(ph + 32, 8))
|
||||
else {
|
||||
break;
|
||||
};
|
||||
if ty == 1 && flags & 1 != 0 && filesz != 0 {
|
||||
out.push(CodeMap {
|
||||
start: bias + (vaddr & !0xfff),
|
||||
end: crate::mem::page_align_up(bias + vaddr + filesz),
|
||||
path: path.to_string(),
|
||||
offset: off & !0xfff,
|
||||
});
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/// A `MAP_SHARED` file mapping, and where its bytes belong.
|
||||
///
|
||||
/// A file mapping here is an eager copy rather than a page table pointed at
|
||||
|
||||
@@ -3782,6 +3782,14 @@ pub extern "C" fn gex_load(ptr: u32, len: u32, argv: u32, argv_len: u32) -> u64
|
||||
// than copied: this runs once, and a second `gex_load` in the same Worker
|
||||
// is an `execve`, which gets its placement from the global table like
|
||||
// every other process.
|
||||
// The interpreter, placed by gex's loader rather than mapped by the guest,
|
||||
// announced as its `mmap` would have been: its function-tier unit
|
||||
// (`?elfunits=ld-linux-x86-64.so.2`) is placed after the first syscall.
|
||||
if let (Some(path), Some(_)) = (&program.exe.interp, &program.interp) {
|
||||
if let Some(bytes) = read_file(path) {
|
||||
kernel.new_code.extend(gex_kernel::CodeMap::of_placed(path, &bytes, program.interp_base()));
|
||||
}
|
||||
}
|
||||
kernel.pins = std::mem::take(&mut *PINS.0.borrow_mut()).into_iter().collect();
|
||||
// This host's memory has to be allocated up to whatever the map hands
|
||||
// out, so the map asks before it hands anything out.
|
||||
|
||||
@@ -1005,6 +1005,15 @@ fn run_program_with(
|
||||
.bind_host(path, Arc::new(w.clone()) as Arc<dyn gex_kernel::unix::HostListener>);
|
||||
w
|
||||
}));
|
||||
// The interpreter, placed by gex's loader rather than mapped by the guest,
|
||||
// announced as its `mmap` would have been: its function-tier unit
|
||||
// (`GEX_ELF_UNITS=ld-linux-x86-64.so.2`) is placed after the first syscall.
|
||||
if let (Some(path), Some(_)) = (&program.exe.interp, &program.interp) {
|
||||
if let Some(bytes) = kernel.read_file(path) {
|
||||
let maps = gex_kernel::CodeMap::of_placed(path, &bytes, program.interp_base());
|
||||
kernel.new_code.extend(maps);
|
||||
}
|
||||
}
|
||||
let tid = kernel.pid;
|
||||
let prof_proc = profile::new_process(&kernel, program, kernel.pid, &opts.args, &opts.env);
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
# code, and a library mapped again must get its unit again -- a unit whose
|
||||
# code was unmapped is retired, not revived by the next object's mapping
|
||||
# (nt_cgen::on_retire, ftab::on_retire; the browser's cgen::on_retire).
|
||||
# The interpreter has a unit too: gex's loader places it, and the kernel
|
||||
# announces it as the guest's own mmap would have (CodeMap::of_placed).
|
||||
#
|
||||
# tests/elf-units-remap/run.sh [gexc] (needs gcc; host glibc as the guest's)
|
||||
set -u
|
||||
@@ -17,19 +19,23 @@ gcc -O1 -o "$W/main" "$HERE/main.c" -ldl || exit 1
|
||||
# Both at one pinned base, so the units built for it apply.
|
||||
BASE=0x30000000
|
||||
printf '%s 0x10000 liba.so\n%s 0x10000 libb.so\n' $BASE $BASE > "$W/pins.txt"
|
||||
export GEX_CGEN_CACHE="$W/cgen" GEX_ELF_UNITS=liba.so,libb.so GEX_ELF_UNITS_BUILD=0
|
||||
INTERP=$(readelf -lW "$W/main" | sed -n 's/.*Requesting program interpreter: \(.*\)]/\1/p')
|
||||
export GEX_CGEN_CACHE="$W/cgen" GEX_ELF_UNITS=liba.so,libb.so,${INTERP##*/} GEX_ELF_UNITS_BUILD=0
|
||||
for l in liba libb; do
|
||||
"$GEXC" cgen-image --elf "$W/$l.so" --base $BASE --jobs 2 > "$W/$l.cgen.log" 2>&1 || { cat "$W/$l.cgen.log"; exit 1; }
|
||||
done
|
||||
# At gex_abi::INTERP_BASE.
|
||||
"$GEXC" cgen-image --elf "$INTERP" --base 0x4a00000 --jobs 2 > "$W/interp.cgen.log" 2>&1 || { cat "$W/interp.cgen.log"; exit 1; }
|
||||
native=$("$W/main" "$W")
|
||||
fail=0
|
||||
for flags in "" "--fjit"; do
|
||||
gex=$(timeout 60 "$GEXC" run $flags --pin "$W/pins.txt" "$W/main" "$W" 2> "$W/err.txt")
|
||||
placed=$(grep -c 'function tier for' "$W/err.txt")
|
||||
if [ "$native" = "$gex" ] && [ "$placed" = 4 ]; then
|
||||
placed=$(grep -c 'function tier for .*/lib[ab]\.so' "$W/err.txt")
|
||||
interp=$(grep -c "function tier for $INTERP at 0x4a00000" "$W/err.txt")
|
||||
if [ "$native" = "$gex" ] && [ "$placed" = 4 ] && [ "$interp" = 1 ]; then
|
||||
echo "ok elf-units-remap ${flags:-t1}"
|
||||
else
|
||||
echo "FAIL elf-units-remap ${flags:-t1} (units placed $placed times, want 4)"
|
||||
echo "FAIL elf-units-remap ${flags:-t1} (library units placed $placed times, want 4; the interpreter's $interp, want 1)"
|
||||
printf 'native:\n%s\ngex:\n%s\n' "$native" "$gex"; tail -20 "$W/err.txt"; fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
Reference in new issue
Block a user