mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
page: a replaced library's bound exports get their bridge back on a miss
In Hollow Knight's page run (hp2, hp3) a thread ran the original x86 mono_unity_liveness_finalize of libmonobdwgc (bound to the Mono provider) and trapped on a wild pointer: its address reached tier-1 instead of the provider. The miss path re-bridged only ELF export providers' addresses; `elfprov::provided` now counts a replaced library's bound addresses too, and a miss elsewhere in a replaced library's text is said (`libprov::note_stray`). hp4 reaches HK's main menu. browser-linux.mjs: --home DIR (entries linked into the guest's $HOME). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_d2453757b8a84fdbb4ee7c9e093141df
This commit is contained in:
1 parent
8c2c9a7dca
commit
be33e8a41b
4 files changed
+28
-4
No files matched your search
@@ -51,7 +51,7 @@ pub extern "C" fn gex_elfprov_off() {
|
||||
|
||||
/// Whether an ELF provider holds `addr` (the function JIT leaves it alone).
|
||||
pub(crate) fn provided(addr: u64) -> bool {
|
||||
lock(&BOUND).iter().any(|b| b.addrs.contains(&addr))
|
||||
lock(&BOUND).iter().any(|b| b.addrs.contains(&addr)) || super::libprov::bound(addr)
|
||||
}
|
||||
|
||||
/// A new process (a forked child): start over from what its kernel has
|
||||
|
||||
@@ -76,6 +76,23 @@ pub(crate) fn replaced(addr: u64) -> bool {
|
||||
lock(&REPLACED).iter().any(|&(lo, hi)| addr >= lo && addr < hi)
|
||||
}
|
||||
|
||||
/// Whether `addr` is one a replaced library's provider is bound at (its
|
||||
/// pairs are sorted by address).
|
||||
pub(crate) fn bound(addr: u64) -> bool {
|
||||
lock(&LIBS).iter().any(|l| l.pairs.binary_search_by_key(&addr, |&(a, _)| a).is_ok())
|
||||
}
|
||||
|
||||
/// A dispatch miss in a replaced library's own text that no binding
|
||||
/// explains: its x86 code was never meant to run (said once per address).
|
||||
pub(crate) fn note_stray(pc: u64) {
|
||||
static SEEN: Mutex<Vec<u64>> = Mutex::new(Vec::new());
|
||||
let mut seen = lock(&SEEN);
|
||||
if seen.len() < 64 && !seen.contains(&pc) {
|
||||
seen.push(pc);
|
||||
crate::warn_line(&format!("gex: the guest reached {pc:#x}, the replaced library's own code (not a bound export): tier-1 runs it\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Guest pages below 4 GiB: a provider's data and code page (its data base
|
||||
/// and the guest addresses it binds are 32-bit numbers to the page).
|
||||
fn alloc_low(size: u64, prot: u32, name: &str) -> Result<u64, String> {
|
||||
|
||||
@@ -10878,7 +10878,8 @@ pub extern "C" fn gex_dispatch_miss(_ctx: i32, pc: i64) -> i32 {
|
||||
});
|
||||
trap();
|
||||
}
|
||||
// An address an ELF export provider holds whose slot lost its bridge.
|
||||
// An address an ELF export provider (or a replaced library's provider)
|
||||
// holds whose slot lost its bridge.
|
||||
if cgen::elfprov::rebridge(pc) {
|
||||
with_host(|h| {
|
||||
set_resume(pc);
|
||||
@@ -10886,6 +10887,9 @@ pub extern "C" fn gex_dispatch_miss(_ctx: i32, pc: i64) -> i32 {
|
||||
});
|
||||
trap();
|
||||
}
|
||||
if cgen::libprov::replaced(pc) {
|
||||
cgen::libprov::note_stray(pc);
|
||||
}
|
||||
remiss(pc);
|
||||
if tracing() || unsafe { TRACE_MISSES } {
|
||||
// Before the translation: the question is which block the translator
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// native run uses, tests/games/*.wl), timed from the page's Nth GL present.
|
||||
//
|
||||
// node tests/games/browser-linux.mjs --name NAME --dir GAME_DIR --exe REL
|
||||
// [--cwd REL] [--args ARGS] [--libs DIR:DIR] [--files LIST]
|
||||
// [--cwd REL] [--args ARGS] [--libs DIR:DIR] [--files LIST] [--home DIR]
|
||||
// [--input SCRIPT.wl] [--size WxH] [--minutes N] [--shot DIR]
|
||||
// [--profile AT,SECS[,MATCH]] [--gameplay N] [--mem64] [--fjit] [--wasmdlls SO=PATH] [--query K=V&...] [--env K=V,K=V] [--end-on-exit]
|
||||
//
|
||||
@@ -26,7 +26,7 @@
|
||||
// Prints `ok` lines (first frame, the input script's start, gameplay) and the
|
||||
// frame rate over the last minute, with a screenshot every 30 s.
|
||||
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
|
||||
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { deflateSync, crc32 } from 'node:zlib';
|
||||
|
||||
@@ -91,6 +91,9 @@ function bundle() {
|
||||
rmSync(stage, { recursive: true, force: true });
|
||||
mkdirSync(join(stage, 'gex/libs'), { recursive: true });
|
||||
mkdirSync(join(stage, 'home/g'), { recursive: true });
|
||||
// --home DIR: its entries in the guest's $HOME (saved settings, so a game skips first-run screens).
|
||||
const home = opt('--home', null);
|
||||
if (home) for (const e of readdirSync(home)) symlinkSync(join(home, e), join(stage, 'home/g', e));
|
||||
mkdirSync(join(stage, 'tmp'), { recursive: true });
|
||||
symlinkSync(gameDir, join(stage, 'game'));
|
||||
symlinkSync(GL, join(stage, 'gex/gl'));
|
||||
|
||||
Reference in new issue
Block a user