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]
|
[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 = []
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue