core: debuggger: load symbols from elf files

Former-commit-id: 3b20be6ecff9d540f7ba0c76d9762f87fac81998
Former-commit-id: 8b08fb90c5c163479c8318dd01f1f92fab475efc
This commit is contained in:
Michel Heily 2020-10-17 05:41:33 -07:00 committed by MishMish
parent 3fa858f969
commit 85db28dac6
4 changed files with 117 additions and 24 deletions

View file

@ -53,9 +53,9 @@ criterion = "0.3"
[features] [features]
default = ["arm7tdmi_dispatch_table"] default = ["arm7tdmi_dispatch_table"]
debugger = ["nom", "rustyline", "fuzzy-matcher"]
gdb = ["gdbstub"]
elf_support = ["goblin"] elf_support = ["goblin"]
debugger = ["nom", "rustyline", "fuzzy-matcher", "elf_support"]
gdb = ["gdbstub"]
# Uses lookup tables when executing instructions instead of `match` statements. # Uses lookup tables when executing instructions instead of `match` statements.
# Faster, but consumes more memory. # Faster, but consumes more memory.
arm7tdmi_dispatch_table = [] arm7tdmi_dispatch_table = []

View file

@ -1,4 +1,5 @@
use std::path::Path; use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::time; use std::time;
use crate::arm7tdmi::arm::ArmInstruction; use crate::arm7tdmi::arm::ArmInstruction;
@ -19,6 +20,8 @@ use fuzzy_matcher::FuzzyMatcher;
use hexdump; use hexdump;
use goblin;
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum DisassMode { pub enum DisassMode {
ModeArm, ModeArm,
@ -64,15 +67,43 @@ pub enum Command {
TraceToggle(TraceFlags), TraceToggle(TraceFlags),
SaveState(String), SaveState(String),
LoadState(String), LoadState(String),
AddSymbolsFile(PathBuf, Option<u32>),
ListSymbols(Option<String>), 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 { impl Debugger {
pub fn run_command(&mut self, command: Command) { pub fn run_command(&mut self, command: Command) {
use Command::*; use Command::*;
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
match command { match command {
Info => { 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!("{}", self.gba.cpu);
println!("IME={}", self.gba.sysbus.io.intc.interrupt_master_enable); println!("IME={}", self.gba.sysbus.io.intc.interrupt_master_enable);
println!("IE={:#?}", self.gba.sysbus.io.intc.interrupt_enable); println!("IE={:#?}", self.gba.sysbus.io.intc.interrupt_enable);
@ -87,13 +118,20 @@ impl Debugger {
self.gba.cpu.step(&mut self.gba.sysbus); self.gba.cpu.step(&mut self.gba.sysbus);
} }
if let Some(last_executed) = &self.gba.cpu.last_executed { 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!( print!(
"{}\t{}", "{}\t{}",
Colour::Black Colour::Black.bold().italic().on(Colour::White).paint(text),
.bold()
.italic()
.on(Colour::White)
.paint(format!("Executed at @0x{:08x}:", last_executed.get_pc(),)),
last_executed last_executed
); );
println!( println!(
@ -222,30 +260,45 @@ impl Debugger {
let save = read_bin_file(&Path::new(&load_path)) let save = read_bin_file(&Path::new(&load_path))
.expect("failed to read save state from file"); .expect("failed to read save state from file");
self.gba self.gba
.restore_state(&save) .restore_state(&save, Box::from(self.gba.sysbus.get_bios()))
.expect("failed to deserialize"); .expect("failed to deserialize");
} }
ListSymbols(Some(pattern)) => { ListSymbols(Some(pattern)) => {
if let Some(symbols) = self.gba.sysbus.cartridge.get_symbols() {
let matcher = SkimMatcherV2::default(); let matcher = SkimMatcherV2::default();
for (k, v) in symbols for (k, v) in self
.symbols
.iter() .iter()
.filter(|(k, _v)| matcher.fuzzy_match(k, &pattern).is_some()) .filter(|(k, _v)| matcher.fuzzy_match(k, &pattern).is_some())
{ {
println!("{}=0x{:08x}", k, v); println!("{}=0x{:08x}", k, v);
} }
} else {
println!("symbols not loaded!");
}
} }
ListSymbols(None) => { ListSymbols(None) => {
if let Some(symbols) = self.gba.sysbus.cartridge.get_symbols() { for (k, v) in self.symbols.iter() {
for (k, v) in symbols.iter() {
println!("{}=0x{:08x}", k, v); println!("{}=0x{:08x}", k, v);
} }
} else {
println!("symbols not loaded!");
} }
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!("[error] Can't read elf file!");
return;
};
} }
_ => println!("Not Implemented",), _ => 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() { "list-symbols" | "list-syms" | "symbols" | "syms" => match args.len() {
0 => Ok(Command::ListSymbols(None)), 0 => Ok(Command::ListSymbols(None)),
1 => { 1 => {

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{prelude::*, BufReader}; use std::io::{prelude::*, BufReader};
@ -39,6 +40,7 @@ pub struct Debugger {
pub gba: GameBoyAdvance, pub gba: GameBoyAdvance,
running: bool, running: bool,
pub previous_command: Option<Command>, pub previous_command: Option<Command>,
pub symbols: HashMap<String, u32>,
} }
impl Debugger { impl Debugger {
@ -47,6 +49,7 @@ impl Debugger {
gba: gba, gba: gba,
running: false, running: false,
previous_command: None, previous_command: None,
symbols: HashMap::new(),
} }
} }

View file

@ -231,6 +231,10 @@ impl SysBus {
self.internal_work_ram.mem = buffer; self.internal_work_ram.mem = buffer;
} }
pub fn get_bios(&self) -> &[u8] {
&self.bios.mem
}
pub fn get_ewram(&self) -> &[u8] { pub fn get_ewram(&self) -> &[u8] {
&self.onboard_work_ram.mem &self.onboard_work_ram.mem
} }