cgen: function-tier units for i386 ELF objects

The load bias of a listed object (GEX_ELF_UNITS, ?elfunits=) was read
from ELFCLASS64 headers only, so an i386 library never got its unit.
Read ELFCLASS32 headers and program headers at their own offsets and
widths, natively (pe_units) and in the browser (libprov).

tests/elf-units-remap/run32.sh: run.sh's two libraries at one address,
built with gcc -m32; four placements and each load's own output, t1
and fjit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_8b3490cccb4848b495dde1c5cdcacba0
This commit is contained in:
velzieandClaude Opus 5.5 committed 2026-10-24 12:32:10 -04:00
1 parent e76dc73110
commit e400f11eb3
3 files changed
+75 -6

No files matched your search

+18 -3
View File
@@ -991,13 +991,28 @@ fn elf_bias(bytes: &[u8], start: u64, offset: u64) -> Option<u64> {
v[..n].copy_from_slice(b);
Some(u64::from_le_bytes(v))
};
if bytes.get(0..4)? != b"\x7fELF" || *bytes.get(4)? != 2 {
if bytes.get(0..4)? != b"\x7fELF" {
return None;
}
let (phoff, phentsize, phnum) = (rd(0x20, 8)? as usize, rd(0x36, 2)? as usize, rd(0x38, 2)? as usize);
// ELFCLASS64, or ELFCLASS32 (an i386 object): the header and program
// header fields at their own offsets and widths.
let wide = match *bytes.get(4)? {
2 => true,
1 => false,
_ => return None,
};
let (phoff, phentsize, phnum) = if wide {
(rd(0x20, 8)? as usize, rd(0x36, 2)? as usize, rd(0x38, 2)? as usize)
} else {
(rd(0x1c, 4)? as usize, rd(0x2a, 2)? as usize, rd(0x2c, 2)? as usize)
};
for i in 0..phnum {
let ph = phoff + i * phentsize;
let (ty, flags, off, vaddr) = (rd(ph, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 8)?, rd(ph + 16, 8)?);
let (ty, flags, off, vaddr) = if wide {
(rd(ph, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 8)?, rd(ph + 16, 8)?)
} else {
(rd(ph, 4)?, rd(ph + 24, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 4)?)
};
if ty == 1 && flags & 1 != 0 && off & !0xfff == offset {
return Some(start.wrapping_sub(vaddr & !0xfff));
}
+18 -3
View File
@@ -1807,13 +1807,28 @@ fn elf_bias(bytes: &[u8], start: u64, offset: u64) -> Option<u64> {
v[..n].copy_from_slice(b);
Some(u64::from_le_bytes(v))
};
if bytes.get(0..4)? != b"\x7fELF" || *bytes.get(4)? != 2 {
if bytes.get(0..4)? != b"\x7fELF" {
return None;
}
let (phoff, phentsize, phnum) = (rd(0x20, 8)? as usize, rd(0x36, 2)? as usize, rd(0x38, 2)? as usize);
// ELFCLASS64, or ELFCLASS32 (an i386 object): the header and program
// header fields at their own offsets and widths.
let wide = match *bytes.get(4)? {
2 => true,
1 => false,
_ => return None,
};
let (phoff, phentsize, phnum) = if wide {
(rd(0x20, 8)? as usize, rd(0x36, 2)? as usize, rd(0x38, 2)? as usize)
} else {
(rd(0x1c, 4)? as usize, rd(0x2a, 2)? as usize, rd(0x2c, 2)? as usize)
};
for i in 0..phnum {
let ph = phoff + i * phentsize;
let (ty, flags, off, vaddr) = (rd(ph, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 8)?, rd(ph + 16, 8)?);
let (ty, flags, off, vaddr) = if wide {
(rd(ph, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 8)?, rd(ph + 16, 8)?)
} else {
(rd(ph, 4)?, rd(ph + 24, 4)?, rd(ph + 4, 4)?, rd(ph + 8, 4)?)
};
if ty == 1 && flags & 1 != 0 && off & !0xfff == offset {
return Some(start.wrapping_sub(vaddr & !0xfff));
}
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
# run.sh's two libraries at one address, built for i386: the load bias of an
# ELFCLASS32 object is found from its own program headers, so each i386
# library gets its function-tier unit (four placements), and each load runs
# its own code.
#
# tests/elf-units-remap/run32.sh [gexc] (needs gcc -m32 and an i386 libc)
set -u
export GEX_LOG=${GEX_LOG:-1}
HERE=$(cd "$(dirname "$0")" && pwd)
GEXC=${1:-target/x86/release/gexc}
W=${WORK:-$(mktemp -d)}
mkdir -p "$W"
if ! echo 'int main(void){return 0;}' | gcc -m32 -x c -o "$W/probe" - 2>/dev/null; then
echo "skip elf-units-remap i386 (no gcc -m32)"
exit 0
fi
gcc -m32 -O1 -shared -fPIC -DK=3 -DC=1 -o "$W/liba.so" "$HERE/lib.c" || exit 1
gcc -m32 -O1 -shared -fPIC -DK=7 -DC=-2 -o "$W/libb.so" "$HERE/lib.c" || exit 1
gcc -m32 -O1 -o "$W/main" "$HERE/main.c" -ldl || exit 1
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
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
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 .*/lib[ab]\.so' "$W/err.txt")
if [ "$native" = "$gex" ] && [ "$placed" = 4 ]; then
echo "ok elf-units-remap i386 ${flags:-t1}"
else
echo "FAIL elf-units-remap i386 ${flags:-t1} (library units placed $placed times, want 4)"
printf 'native:\n%s\ngex:\n%s\n' "$native" "$gex"; tail -20 "$W/err.txt"; fail=1
fi
done
exit $fail