Add debug symbol support for disassembler
This commit is contained in:
18
src/block.rs
18
src/block.rs
@@ -45,6 +45,24 @@ impl Block {
|
||||
self.pointer = location;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_string(&mut self) -> Result<String> {
|
||||
let mut bytes = vec![];
|
||||
|
||||
loop {
|
||||
let c = self.get()?;
|
||||
|
||||
if c == 0 {
|
||||
return match String::from_utf8(bytes) {
|
||||
Ok(string) => Ok(string),
|
||||
_ => Err("Invalid string in block")
|
||||
}
|
||||
}
|
||||
|
||||
bytes.push(c);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<usize> for Block {
|
||||
|
||||
@@ -5,11 +5,69 @@ use machine::MAGIC_HEADER;
|
||||
use Result;
|
||||
use ops;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::clone::Clone;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
type OpCode = u8;
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize)]
|
||||
pub struct DebugSymbols {
|
||||
methods: HashMap<usize, String>,
|
||||
constants: Vec<usize>,
|
||||
labels: HashMap<usize, String>,
|
||||
}
|
||||
|
||||
impl DebugSymbols {
|
||||
fn add_methods(&mut self, mut block: Block) -> Result<()> {
|
||||
while block.has_i32() {
|
||||
let addr = block.read_u32()? as usize;
|
||||
let name = block.read_string()?;
|
||||
self.methods.insert(addr, name);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_labels(&mut self, mut block: Block) -> Result<()> {
|
||||
while block.has_i32() {
|
||||
let addr = block.read_u32()? as usize;
|
||||
let mut fullname = block.read_string()?;
|
||||
let hash_offset = fullname.find('#').unwrap_or(0usize);
|
||||
fullname.drain(..=hash_offset);
|
||||
self.labels.insert(addr, fullname);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_lookup(&mut self, addr: usize) {
|
||||
self.constants.push(addr);
|
||||
}
|
||||
|
||||
fn lookup_method(&self, location: usize) -> Option<String> {
|
||||
match self.methods.get(&location) {
|
||||
Some(name) => Some(name.to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_method_idx(&self, idx: usize) -> Option<String> {
|
||||
match self.methods.get(&self.constants[idx]) {
|
||||
Some(name) => Some(name.to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_label(&self, location: usize) -> Option<String> {
|
||||
match self.labels.get(&location) {
|
||||
Some(name) => Some(name.to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Instruction {
|
||||
op: OpCode,
|
||||
@@ -19,12 +77,17 @@ pub struct Instruction {
|
||||
wide: bool,
|
||||
pos: usize,
|
||||
label: bool,
|
||||
#[serde(skip)]
|
||||
symbols: Rc<DebugSymbols>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.label {
|
||||
writeln!(f, "\nLBL_{}:", self.pos)?;
|
||||
let name = self.symbols
|
||||
.lookup_label(self.pos)
|
||||
.unwrap_or_else(|| format!("LBL_{}", self.pos));
|
||||
writeln!(f, "\n{}:", name)?;
|
||||
}
|
||||
|
||||
if self.wide {
|
||||
@@ -35,10 +98,19 @@ impl fmt::Display for Instruction {
|
||||
|
||||
for (typ, val) in self.types.iter().zip(&self.params) {
|
||||
match *typ {
|
||||
ops::Args::Label => write!(f, " LBL_{}", val)?,
|
||||
ops::Args::Label => {
|
||||
let name = self.symbols
|
||||
.lookup_label(*val as usize)
|
||||
.unwrap_or_else(|| format!("LBL_{}", val));
|
||||
write!(f, " {}", name)?;
|
||||
},
|
||||
ops::Args::Constant => {
|
||||
if self.op == 0xB6 {
|
||||
write!(f, " func_{}", val)?;
|
||||
let name = self.symbols
|
||||
.lookup_method_idx(*val as usize)
|
||||
.unwrap_or_else(|| format!("func_{}", val));
|
||||
|
||||
write!(f, " {}", name)?;
|
||||
} else {
|
||||
write!(f, " CONST_{}", val)?;
|
||||
}
|
||||
@@ -81,13 +153,7 @@ pub struct Method {
|
||||
}
|
||||
|
||||
impl Method {
|
||||
pub fn new(i: i32, pos: usize, args: usize, vars: usize) -> Self {
|
||||
let name = if pos == 0 {
|
||||
"main".to_string()
|
||||
} else {
|
||||
format!("func_{}", i)
|
||||
};
|
||||
|
||||
pub fn new(name: String, pos: usize, args: usize, vars: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
pos,
|
||||
@@ -173,16 +239,18 @@ pub struct Disassembler {
|
||||
disassembly: Disassembly,
|
||||
text: Block,
|
||||
pool: Block,
|
||||
symbols: Rc<DebugSymbols>,
|
||||
suspects: Vec<usize>,
|
||||
found: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Disassembler {
|
||||
pub fn new(text: Block, pool: Block) -> Disassembler {
|
||||
pub fn new(text: Block, pool: Block, symbols: DebugSymbols) -> Disassembler {
|
||||
Disassembler {
|
||||
disassembly: Disassembly::default(),
|
||||
text,
|
||||
pool,
|
||||
symbols: Rc::new(symbols),
|
||||
suspects: Vec::new(),
|
||||
found: Vec::new(),
|
||||
}
|
||||
@@ -245,6 +313,7 @@ impl Disassembler {
|
||||
wide,
|
||||
pos,
|
||||
label: false,
|
||||
symbols: self.symbols.clone(),
|
||||
};
|
||||
|
||||
// eprintln!("i 0x{:x} ({}) @ {}", opcode, name, pos);
|
||||
@@ -282,7 +351,8 @@ impl Disassembler {
|
||||
}
|
||||
|
||||
fn disasm_method(&mut self, i: i32, pc: usize, args: usize, vars: usize) -> Result<Method> {
|
||||
let mut method = Method::new(i, pc, args, vars);
|
||||
let name = self.symbols.lookup_method(pc).unwrap_or_else(|| format!("func_{}", i));
|
||||
let mut method = Method::new(name, pc, args, vars);
|
||||
|
||||
while self.text.has_i8() && !self.is_method(self.text.cur()) {
|
||||
self.disasm_instruction(&mut method)?;
|
||||
@@ -320,7 +390,8 @@ impl Disassembler {
|
||||
|
||||
pub fn disassemble(&mut self) -> Result<Disassembly> {
|
||||
while self.pool.has_i32() {
|
||||
self.disassembly.constants.push(Constant::new(self.pool.read_i32()?));
|
||||
let addr = self.pool.read_i32()?;
|
||||
self.disassembly.constants.push(Constant::new(addr));
|
||||
}
|
||||
|
||||
self.suspects = self.find_methods()?;
|
||||
@@ -346,7 +417,7 @@ impl Disassembly {
|
||||
if magic != MAGIC_HEADER {
|
||||
return Err("Invalid magic header");
|
||||
}
|
||||
let pool = match reader.read_block() {
|
||||
let mut pool = match reader.read_block() {
|
||||
Ok(a) => a,
|
||||
Err(_) => return Err("Failed to read constants block")
|
||||
};
|
||||
@@ -355,7 +426,23 @@ impl Disassembly {
|
||||
Err(_) => return Err("Failed to read text block")
|
||||
};
|
||||
|
||||
Disassembler::new(text, pool).disassemble()
|
||||
|
||||
let mut symbols = DebugSymbols::default();
|
||||
if let Ok(block) = reader.read_block() {
|
||||
symbols.add_methods(block)?;
|
||||
if let Ok(labels) = reader.read_block() {
|
||||
symbols.add_labels(labels)?;
|
||||
}
|
||||
|
||||
while pool.has_i32() {
|
||||
let addr = pool.read_i32()?;
|
||||
symbols.add_lookup(addr as usize);
|
||||
}
|
||||
|
||||
pool.seek(0)?;
|
||||
}
|
||||
|
||||
Disassembler::new(text, pool, symbols).disassemble()
|
||||
}
|
||||
|
||||
pub fn disassemble_file(file: &str) -> Result<Disassembly> {
|
||||
@@ -372,7 +459,7 @@ impl Disassembly {
|
||||
|
||||
impl fmt::Display for Disassembly {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.constants.is_empty() {
|
||||
if !self.constants.is_empty() {
|
||||
writeln!(f, ".constant")?;
|
||||
for (i, c) in self.constants.iter().enumerate() {
|
||||
if !c.method {
|
||||
|
||||
Reference in New Issue
Block a user