core: debuggger: load symbols from elf files
Former-commit-id: 3b20be6ecff9d540f7ba0c76d9762f87fac81998 Former-commit-id: 8b08fb90c5c163479c8318dd01f1f92fab475efc
This commit is contained in:
parent
3fa858f969
commit
85db28dac6
|
@ -53,9 +53,9 @@ criterion = "0.3"
|
|||
|
||||
[features]
|
||||
default = ["arm7tdmi_dispatch_table"]
|
||||
debugger = ["nom", "rustyline", "fuzzy-matcher"]
|
||||
gdb = ["gdbstub"]
|
||||
elf_support = ["goblin"]
|
||||
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
|
||||
gdb = ["gdbstub"]
|
||||
# Uses lookup tables when executing instructions instead of `match` statements.
|
||||
# Faster, but consumes more memory.
|
||||
arm7tdmi_dispatch_table = []
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::path::Path;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time;
|
||||
|
||||
use crate::arm7tdmi::arm::ArmInstruction;
|
||||
|
@ -19,6 +20,8 @@ use fuzzy_matcher::FuzzyMatcher;
|
|||
|
||||
use hexdump;
|
||||
|
||||
use goblin;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum DisassMode {
|
||||
ModeArm,
|
||||
|
@ -64,15 +67,43 @@ pub enum Command {
|
|||
TraceToggle(TraceFlags),
|
||||
SaveState(String),
|
||||
LoadState(String),
|
||||
AddSymbolsFile(PathBuf, Option<u32>),
|
||||
ListSymbols(Option<String>),
|
||||
}
|
||||
|
||||
fn find_nearest_symbol(addr: u32, symbols: &HashMap<String, u32>) -> Option<(String, u32)> {
|
||||
let mut smallest_distance = u32::MAX;
|
||||
let mut symbol = String::new();
|
||||
|
||||
for (k, v) in symbols.iter().filter(|(_k, v)| **v > addr) {
|
||||
let distance = v - addr;
|
||||
if distance < smallest_distance {
|
||||
smallest_distance = distance;
|
||||
symbol = k.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
if smallest_distance < 0x1000 {
|
||||
let symaddr = *symbols.get(&symbol).unwrap();
|
||||
Some((symbol, symaddr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Debugger {
|
||||
pub fn run_command(&mut self, command: Command) {
|
||||
use Command::*;
|
||||
#[allow(unreachable_patterns)]
|
||||
match command {
|
||||
Info => {
|
||||
let pc = self.gba.cpu.pc;
|
||||
if let Some((sym, addr)) = find_nearest_symbol(pc, &self.symbols) {
|
||||
println!("PC at {}+{:#x} ({:08x})", sym, addr - pc, pc);
|
||||
} else {
|
||||
println!("PC at {:08x}", pc);
|
||||
}
|
||||
|
||||
println!("{}", self.gba.cpu);
|
||||
println!("IME={}", self.gba.sysbus.io.intc.interrupt_master_enable);
|
||||
println!("IE={:#?}", self.gba.sysbus.io.intc.interrupt_enable);
|
||||
|
@ -87,13 +118,20 @@ impl Debugger {
|
|||
self.gba.cpu.step(&mut self.gba.sysbus);
|
||||
}
|
||||
if let Some(last_executed) = &self.gba.cpu.last_executed {
|
||||
let pc = last_executed.get_pc();
|
||||
let symbol =
|
||||
self.symbols
|
||||
.iter()
|
||||
.find_map(|(key, &val)| if val == pc { Some(key) } else { None });
|
||||
|
||||
let text = if let Some(symbol) = symbol {
|
||||
format!("Executed at {} @0x{:08x}:", symbol, pc)
|
||||
} else {
|
||||
format!("Executed at @0x{:08x}:", pc)
|
||||
};
|
||||
print!(
|
||||
"{}\t{}",
|
||||
Colour::Black
|
||||
.bold()
|
||||
.italic()
|
||||
.on(Colour::White)
|
||||
.paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)),
|
||||
Colour::Black.bold().italic().on(Colour::White).paint(text),
|
||||
last_executed
|
||||
);
|
||||
println!(
|
||||
|
@ -222,30 +260,45 @@ impl Debugger {
|
|||
let save = read_bin_file(&Path::new(&load_path))
|
||||
.expect("failed to read save state from file");
|
||||
self.gba
|
||||
.restore_state(&save)
|
||||
.restore_state(&save, Box::from(self.gba.sysbus.get_bios()))
|
||||
.expect("failed to deserialize");
|
||||
}
|
||||
ListSymbols(Some(pattern)) => {
|
||||
if let Some(symbols) = self.gba.sysbus.cartridge.get_symbols() {
|
||||
let matcher = SkimMatcherV2::default();
|
||||
for (k, v) in symbols
|
||||
.iter()
|
||||
.filter(|(k, _v)| matcher.fuzzy_match(k, &pattern).is_some())
|
||||
{
|
||||
println!("{}=0x{:08x}", k, v);
|
||||
}
|
||||
} else {
|
||||
println!("symbols not loaded!");
|
||||
let matcher = SkimMatcherV2::default();
|
||||
for (k, v) in self
|
||||
.symbols
|
||||
.iter()
|
||||
.filter(|(k, _v)| matcher.fuzzy_match(k, &pattern).is_some())
|
||||
{
|
||||
println!("{}=0x{:08x}", k, v);
|
||||
}
|
||||
}
|
||||
ListSymbols(None) => {
|
||||
if let Some(symbols) = self.gba.sysbus.cartridge.get_symbols() {
|
||||
for (k, v) in symbols.iter() {
|
||||
println!("{}=0x{:08x}", k, v);
|
||||
for (k, v) in self.symbols.iter() {
|
||||
println!("{}=0x{:08x}", k, v);
|
||||
}
|
||||
}
|
||||
AddSymbolsFile(elf_file, offset) => {
|
||||
let offset = offset.unwrap_or(0);
|
||||
if let Ok(elf_buffer) = read_bin_file(&elf_file) {
|
||||
if let Ok(elf) = goblin::elf::Elf::parse(&elf_buffer) {
|
||||
let strtab = elf.strtab;
|
||||
for sym in elf.syms.iter() {
|
||||
if let Some(Ok(name)) = strtab.get(sym.st_name) {
|
||||
self.symbols
|
||||
.insert(name.to_owned(), offset + (sym.st_value as u32));
|
||||
} else {
|
||||
warn!("failed to parse symbol name sym {:?}", sym);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("[error] Failed to parse elf file!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
println!("symbols not loaded!");
|
||||
}
|
||||
println!("[error] Can't read elf file!");
|
||||
return;
|
||||
};
|
||||
}
|
||||
_ => println!("Not Implemented",),
|
||||
}
|
||||
|
@ -493,6 +546,39 @@ impl Debugger {
|
|||
}
|
||||
}
|
||||
}
|
||||
"add-symbols-file" | "load-symbols" | "load-syms" => match args.len() {
|
||||
1 => {
|
||||
if let Value::Identifier(elf_file) = &args[0] {
|
||||
Ok(Command::AddSymbolsFile(PathBuf::from(elf_file), None))
|
||||
} else {
|
||||
Err(DebuggerError::InvalidArgument(String::from(
|
||||
"expected a filename",
|
||||
)))
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if let Value::Identifier(elf_file) = &args[0] {
|
||||
if let Value::Num(offset) = &args[1] {
|
||||
Ok(Command::AddSymbolsFile(
|
||||
PathBuf::from(elf_file),
|
||||
Some(*offset),
|
||||
))
|
||||
} else {
|
||||
Err(DebuggerError::InvalidArgument(String::from(
|
||||
"expected a number",
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(DebuggerError::InvalidArgument(String::from(
|
||||
"expected a filename",
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Err(DebuggerError::InvalidCommandFormat(format!(
|
||||
"usage: {} path/to/elf [offset]",
|
||||
command
|
||||
))),
|
||||
},
|
||||
"list-symbols" | "list-syms" | "symbols" | "syms" => match args.len() {
|
||||
0 => Ok(Command::ListSymbols(None)),
|
||||
1 => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{prelude::*, BufReader};
|
||||
|
||||
|
@ -39,6 +40,7 @@ pub struct Debugger {
|
|||
pub gba: GameBoyAdvance,
|
||||
running: bool,
|
||||
pub previous_command: Option<Command>,
|
||||
pub symbols: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl Debugger {
|
||||
|
@ -47,6 +49,7 @@ impl Debugger {
|
|||
gba: gba,
|
||||
running: false,
|
||||
previous_command: None,
|
||||
symbols: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -231,6 +231,10 @@ impl SysBus {
|
|||
self.internal_work_ram.mem = buffer;
|
||||
}
|
||||
|
||||
pub fn get_bios(&self) -> &[u8] {
|
||||
&self.bios.mem
|
||||
}
|
||||
|
||||
pub fn get_ewram(&self) -> &[u8] {
|
||||
&self.onboard_work_ram.mem
|
||||
}
|
||||
|
|
Reference in a new issue