Add debug symbol support for disassembler

This commit is contained in:
2019-04-03 13:22:04 +02:00
parent 4d618efe42
commit 21b6fc4e64
2 changed files with 121 additions and 16 deletions

View File

@@ -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 {

View File

@@ -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 {