diff --git a/src/block.rs b/src/block.rs index b5e734f..a76c23c 100644 --- a/src/block.rs +++ b/src/block.rs @@ -45,6 +45,24 @@ impl Block { self.pointer = location; Ok(()) } + + pub fn read_string(&mut self) -> Result { + 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 for Block { diff --git a/src/disassembler.rs b/src/disassembler.rs index c587919..259e1c2 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -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, + constants: Vec, + labels: HashMap, +} + +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 { + match self.methods.get(&location) { + Some(name) => Some(name.to_string()), + None => None, + } + } + + fn lookup_method_idx(&self, idx: usize) -> Option { + match self.methods.get(&self.constants[idx]) { + Some(name) => Some(name.to_string()), + None => None, + } + } + + fn lookup_label(&self, location: usize) -> Option { + 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, } 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, suspects: Vec, found: Vec, } 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 { - 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 { 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 { @@ -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 {