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;
|
self.pointer = location;
|
||||||
Ok(())
|
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 {
|
impl ops::Index<usize> for Block {
|
||||||
|
|||||||
@@ -5,11 +5,69 @@ use machine::MAGIC_HEADER;
|
|||||||
use Result;
|
use Result;
|
||||||
use ops;
|
use ops;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
type OpCode = u8;
|
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)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct Instruction {
|
pub struct Instruction {
|
||||||
op: OpCode,
|
op: OpCode,
|
||||||
@@ -19,12 +77,17 @@ pub struct Instruction {
|
|||||||
wide: bool,
|
wide: bool,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
label: bool,
|
label: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
symbols: Rc<DebugSymbols>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Instruction {
|
impl fmt::Display for Instruction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if self.label {
|
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 {
|
if self.wide {
|
||||||
@@ -35,10 +98,19 @@ impl fmt::Display for Instruction {
|
|||||||
|
|
||||||
for (typ, val) in self.types.iter().zip(&self.params) {
|
for (typ, val) in self.types.iter().zip(&self.params) {
|
||||||
match *typ {
|
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 => {
|
ops::Args::Constant => {
|
||||||
if self.op == 0xB6 {
|
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 {
|
} else {
|
||||||
write!(f, " CONST_{}", val)?;
|
write!(f, " CONST_{}", val)?;
|
||||||
}
|
}
|
||||||
@@ -81,13 +153,7 @@ pub struct Method {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Method {
|
impl Method {
|
||||||
pub fn new(i: i32, pos: usize, args: usize, vars: usize) -> Self {
|
pub fn new(name: String, pos: usize, args: usize, vars: usize) -> Self {
|
||||||
let name = if pos == 0 {
|
|
||||||
"main".to_string()
|
|
||||||
} else {
|
|
||||||
format!("func_{}", i)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
pos,
|
pos,
|
||||||
@@ -173,16 +239,18 @@ pub struct Disassembler {
|
|||||||
disassembly: Disassembly,
|
disassembly: Disassembly,
|
||||||
text: Block,
|
text: Block,
|
||||||
pool: Block,
|
pool: Block,
|
||||||
|
symbols: Rc<DebugSymbols>,
|
||||||
suspects: Vec<usize>,
|
suspects: Vec<usize>,
|
||||||
found: Vec<usize>,
|
found: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Disassembler {
|
impl Disassembler {
|
||||||
pub fn new(text: Block, pool: Block) -> Disassembler {
|
pub fn new(text: Block, pool: Block, symbols: DebugSymbols) -> Disassembler {
|
||||||
Disassembler {
|
Disassembler {
|
||||||
disassembly: Disassembly::default(),
|
disassembly: Disassembly::default(),
|
||||||
text,
|
text,
|
||||||
pool,
|
pool,
|
||||||
|
symbols: Rc::new(symbols),
|
||||||
suspects: Vec::new(),
|
suspects: Vec::new(),
|
||||||
found: Vec::new(),
|
found: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -245,6 +313,7 @@ impl Disassembler {
|
|||||||
wide,
|
wide,
|
||||||
pos,
|
pos,
|
||||||
label: false,
|
label: false,
|
||||||
|
symbols: self.symbols.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// eprintln!("i 0x{:x} ({}) @ {}", opcode, name, pos);
|
// 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> {
|
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()) {
|
while self.text.has_i8() && !self.is_method(self.text.cur()) {
|
||||||
self.disasm_instruction(&mut method)?;
|
self.disasm_instruction(&mut method)?;
|
||||||
@@ -320,7 +390,8 @@ impl Disassembler {
|
|||||||
|
|
||||||
pub fn disassemble(&mut self) -> Result<Disassembly> {
|
pub fn disassemble(&mut self) -> Result<Disassembly> {
|
||||||
while self.pool.has_i32() {
|
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()?;
|
self.suspects = self.find_methods()?;
|
||||||
@@ -346,7 +417,7 @@ impl Disassembly {
|
|||||||
if magic != MAGIC_HEADER {
|
if magic != MAGIC_HEADER {
|
||||||
return Err("Invalid magic header");
|
return Err("Invalid magic header");
|
||||||
}
|
}
|
||||||
let pool = match reader.read_block() {
|
let mut pool = match reader.read_block() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(_) => return Err("Failed to read constants block")
|
Err(_) => return Err("Failed to read constants block")
|
||||||
};
|
};
|
||||||
@@ -355,7 +426,23 @@ impl Disassembly {
|
|||||||
Err(_) => return Err("Failed to read text block")
|
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> {
|
pub fn disassemble_file(file: &str) -> Result<Disassembly> {
|
||||||
@@ -372,7 +459,7 @@ impl Disassembly {
|
|||||||
|
|
||||||
impl fmt::Display for Disassembly {
|
impl fmt::Display for Disassembly {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if self.constants.is_empty() {
|
if !self.constants.is_empty() {
|
||||||
writeln!(f, ".constant")?;
|
writeln!(f, ".constant")?;
|
||||||
for (i, c) in self.constants.iter().enumerate() {
|
for (i, c) in self.constants.iter().enumerate() {
|
||||||
if !c.method {
|
if !c.method {
|
||||||
|
|||||||
Reference in New Issue
Block a user