v0.5: x86-64 disassembly via iced-x86
- state.disasm(n=8), disasm(addr, n=8) -> [{addr, bytes, text, target?, target_sym?}]
- read_mem_pristine() overlays orig bytes through active INT3 breakpoints
- branch/call targets get symbolized when they land in known symbols
- show.disasm / show.bp / show.regs prelude helpers for ergonomic printing
This commit is contained in:
@@ -298,9 +298,41 @@ fn install_globals<'js>(
|
||||
state_obj.set("memory", mem_fn)?;
|
||||
}
|
||||
|
||||
// state.disasm(n=8) — disassemble n instructions starting at pc.
|
||||
{
|
||||
let target = target.clone();
|
||||
let f = Function::new(c.clone(), move |c: Ctx<'js>, n: rquickjs::function::Opt<usize>| -> rquickjs::Result<Value<'js>> {
|
||||
let n = n.0.unwrap_or(8).max(1);
|
||||
let slot = target.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(&c, "no target attached"))?;
|
||||
let pc = t.regs().map_err(|e| throw(&c, &e))?.rip;
|
||||
let bytes = t.read_mem_pristine(pc, n.saturating_mul(15))
|
||||
.map_err(|e| throw(&c, &e))?;
|
||||
let decoded = crate::disasm::decode(pc, &bytes, n);
|
||||
decoded_to_array(&c, &decoded, t.symbols())
|
||||
})?;
|
||||
state_obj.set("disasm", f)?;
|
||||
}
|
||||
|
||||
globals.set("state", state_obj)?;
|
||||
}
|
||||
|
||||
// ---------- disasm(addr, n=8) ----------
|
||||
{
|
||||
let target = target.clone();
|
||||
let f = Function::new(c.clone(), move |c: Ctx<'js>, addr: Value<'js>, n: rquickjs::function::Opt<usize>| -> rquickjs::Result<Value<'js>> {
|
||||
let addr = coerce_addr(&c, &addr)?;
|
||||
let n = n.0.unwrap_or(8).max(1);
|
||||
let slot = target.borrow();
|
||||
let t = slot.as_ref().ok_or_else(|| throw(&c, "no target attached"))?;
|
||||
let bytes = t.read_mem_pristine(addr, n.saturating_mul(15))
|
||||
.map_err(|e| throw(&c, &e))?;
|
||||
let decoded = crate::disasm::decode(addr, &bytes, n);
|
||||
decoded_to_array(&c, &decoded, t.symbols())
|
||||
})?;
|
||||
globals.set("disasm", f)?;
|
||||
}
|
||||
|
||||
// ---------- prev ----------
|
||||
// Mirror of state for register-only data, backed by the pre-resume
|
||||
// snapshot. Property accessors throw if no snapshot is available yet
|
||||
@@ -457,6 +489,34 @@ fn install_globals<'js>(
|
||||
|
||||
// --- helpers --------------------------------------------------------------
|
||||
|
||||
/// Convert a Vec<Decoded> from the disassembler into a JS Array of records:
|
||||
/// [{addr: BigInt, bytes: "ffce..", text: "mov rax, ...", target?: BigInt, target_sym?: "name"}]
|
||||
/// `bytes` is a hex string for compactness; `target` and `target_sym` are
|
||||
/// only present for branch/call instructions.
|
||||
fn decoded_to_array<'js>(
|
||||
c: &Ctx<'js>,
|
||||
decoded: &[crate::disasm::Decoded],
|
||||
symbols: Option<&crate::symbols::SymbolTable>,
|
||||
) -> rquickjs::Result<Value<'js>> {
|
||||
let arr = rquickjs::Array::new(c.clone())?;
|
||||
for (i, d) in decoded.iter().enumerate() {
|
||||
let obj = Object::new(c.clone())?;
|
||||
obj.set("addr", u64_to_bigint(c, d.addr)?)?;
|
||||
let mut bytes_hex = String::with_capacity(d.bytes.len() * 2);
|
||||
for b in &d.bytes { bytes_hex.push_str(&format!("{:02x}", b)); }
|
||||
obj.set("bytes", bytes_hex)?;
|
||||
obj.set("text", d.text.as_str())?;
|
||||
if let Some(t) = d.branch_target {
|
||||
obj.set("target", u64_to_bigint(c, t)?)?;
|
||||
if let Some(s) = symbols.and_then(|s| s.name_for_addr(t)) {
|
||||
obj.set("target_sym", s)?;
|
||||
}
|
||||
}
|
||||
arr.set(i, obj)?;
|
||||
}
|
||||
Ok(arr.into_value())
|
||||
}
|
||||
|
||||
/// 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).
|
||||
|
||||
Reference in New Issue
Block a user