v0.2: prev snapshot of regs, refactored state/prev to share build_register_view
This commit is contained in:
@@ -218,60 +218,15 @@ fn install_globals<'js>(
|
||||
|
||||
// ---------- state ----------
|
||||
{
|
||||
let state_obj = Object::new(c.clone())?;
|
||||
|
||||
// state.pc — accessor returning BigInt
|
||||
{
|
||||
let target = target.clone();
|
||||
state_obj.prop(
|
||||
"pc",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let slot = target.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(&c, "no target attached"))?;
|
||||
let r = t.regs().map_err(|e| throw(&c, &e))?;
|
||||
u64_to_bigint(&c, r.rip)
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
|
||||
// state.sp
|
||||
{
|
||||
let target = target.clone();
|
||||
state_obj.prop(
|
||||
"sp",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let slot = target.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(&c, "no target attached"))?;
|
||||
let r = t.regs().map_err(|e| throw(&c, &e))?;
|
||||
u64_to_bigint(&c, r.rsp)
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
|
||||
// state.registers — accessor returning a fresh object each time
|
||||
{
|
||||
let target = target.clone();
|
||||
state_obj.prop(
|
||||
"registers",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let slot = target.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(&c, "no target attached"))?;
|
||||
let r = t.regs().map_err(|e| throw(&c, &e))?;
|
||||
let obj = Object::new(c.clone())?;
|
||||
let set = |name: &str, val: u64| -> rquickjs::Result<()> {
|
||||
obj.set(name, u64_to_bigint(&c, val)?)
|
||||
};
|
||||
set("rax", r.rax)?; set("rbx", r.rbx)?; set("rcx", r.rcx)?;
|
||||
set("rdx", r.rdx)?; set("rsi", r.rsi)?; set("rdi", r.rdi)?;
|
||||
set("rbp", r.rbp)?; set("rsp", r.rsp)?; set("rip", r.rip)?;
|
||||
set("r8", r.r8)?; set("r9", r.r9)?; set("r10", r.r10)?;
|
||||
set("r11", r.r11)?; set("r12", r.r12)?; set("r13", r.r13)?;
|
||||
set("r14", r.r14)?; set("r15", r.r15)?;
|
||||
set("eflags", r.eflags)?;
|
||||
Ok(obj.into_value())
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
let target_for_state = target.clone();
|
||||
let state_obj = build_register_view(
|
||||
&c,
|
||||
move |c| -> rquickjs::Result<libc::user_regs_struct> {
|
||||
let slot = target_for_state.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(c, "no target attached"))?;
|
||||
t.regs().map_err(|e| throw(c, &e))
|
||||
},
|
||||
)?;
|
||||
|
||||
// state.memory(addr) → object with u8/u32/u64/i64/bytes
|
||||
{
|
||||
@@ -346,6 +301,23 @@ fn install_globals<'js>(
|
||||
globals.set("state", state_obj)?;
|
||||
}
|
||||
|
||||
// ---------- prev ----------
|
||||
// Mirror of state for register-only data, backed by the pre-resume
|
||||
// snapshot. Property accessors throw if no snapshot is available yet
|
||||
// (i.e. immediately after spawn, before any step/cont).
|
||||
{
|
||||
let target_for_prev = target.clone();
|
||||
let prev_obj = build_register_view(
|
||||
&c,
|
||||
move |c| -> rquickjs::Result<libc::user_regs_struct> {
|
||||
let slot = target_for_prev.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(c, "no target attached"))?;
|
||||
t.prev_regs().ok_or_else(|| throw(c, "no prev snapshot yet (step/cont first)"))
|
||||
},
|
||||
)?;
|
||||
globals.set("prev", prev_obj)?;
|
||||
}
|
||||
|
||||
// ---------- JS-side prelude ---------------------------------------------
|
||||
// Loaded last, so it can reference any of the native globals above.
|
||||
// Lives in src/prelude.js, embedded at compile time.
|
||||
@@ -357,6 +329,60 @@ fn install_globals<'js>(
|
||||
|
||||
// --- helpers --------------------------------------------------------------
|
||||
|
||||
/// Build a JS object with `pc`, `sp`, and `registers` accessor properties,
|
||||
/// each backed by a closure that fetches a `user_regs_struct` on demand.
|
||||
/// Used for both `state` (live regs via ptrace) and `prev` (snapshot).
|
||||
fn build_register_view<'js, F>(c: &Ctx<'js>, fetch: F) -> rquickjs::Result<Object<'js>>
|
||||
where
|
||||
F: Fn(&Ctx<'js>) -> rquickjs::Result<libc::user_regs_struct> + Clone + 'static,
|
||||
{
|
||||
let obj = Object::new(c.clone())?;
|
||||
|
||||
{
|
||||
let fetch = fetch.clone();
|
||||
obj.prop(
|
||||
"pc",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let r = fetch(&c)?;
|
||||
u64_to_bigint(&c, r.rip)
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
{
|
||||
let fetch = fetch.clone();
|
||||
obj.prop(
|
||||
"sp",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let r = fetch(&c)?;
|
||||
u64_to_bigint(&c, r.rsp)
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
{
|
||||
let fetch = fetch;
|
||||
obj.prop(
|
||||
"registers",
|
||||
Accessor::from(move |c: Ctx<'js>| -> rquickjs::Result<Value<'js>> {
|
||||
let r = fetch(&c)?;
|
||||
let inner = Object::new(c.clone())?;
|
||||
let set = |name: &str, val: u64| -> rquickjs::Result<()> {
|
||||
inner.set(name, u64_to_bigint(&c, val)?)
|
||||
};
|
||||
set("rax", r.rax)?; set("rbx", r.rbx)?; set("rcx", r.rcx)?;
|
||||
set("rdx", r.rdx)?; set("rsi", r.rsi)?; set("rdi", r.rdi)?;
|
||||
set("rbp", r.rbp)?; set("rsp", r.rsp)?; set("rip", r.rip)?;
|
||||
set("r8", r.r8)?; set("r9", r.r9)?; set("r10", r.r10)?;
|
||||
set("r11", r.r11)?; set("r12", r.r12)?; set("r13", r.r13)?;
|
||||
set("r14", r.r14)?; set("r15", r.r15)?;
|
||||
set("eflags", r.eflags)?;
|
||||
Ok(inner.into_value())
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
/// Create a JS Error and turn it into an rquickjs::Error::Exception. The idiom
|
||||
/// in rquickjs for "throw an error from a Rust callback" is to call
|
||||
/// `Exception::throw_*(ctx, msg)`.
|
||||
|
||||
Reference in New Issue
Block a user