diff --git a/Cargo.toml b/Cargo.toml index 054b9640..ee090412 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,9 @@ readme = "README.md" [dependencies] lazy_static = "1.4.0" object = "0.20" -gimli = "0.22.0" +gimli = "0.25.0" capstone = "0.7.0" -addr2line = "0.13.0" +addr2line = {git="https://github.com/FlyingCanoe/addr2line.git"} syntect = {version = "4.4.0", optional = true} # Dependencies specific to macOS & Linux diff --git a/examples/gui.rs b/examples/gui.rs index 86138d66..e396f1a3 100644 --- a/examples/gui.rs +++ b/examples/gui.rs @@ -296,13 +296,7 @@ mod example { let location = frame .location .as_ref() - .map(|loc| { - format!( - "{}:{}", - loc.file.unwrap_or(""), - loc.line.unwrap_or(0), - ) - }) + .map(|loc| format!("{}:{}", loc.file, loc.line)) .unwrap_or_default(); if first_frame { diff --git a/examples/repl.rs b/examples/repl.rs index 4631f697..3118162b 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -10,6 +10,7 @@ fn main() { #[cfg(target_os = "linux")] mod example { + use std::num::NonZeroU32; use std::{borrow::Cow, process::Command}; use std::{os::unix::ffi::OsStrExt, path::PathBuf}; @@ -337,7 +338,11 @@ mod example { context.remote = None; } ReplCommand::Kill(()) => println!("{:?}", context.remote()?.kill()?), - ReplCommand::Breakpoint(location) => set_breakpoint(context, &location)?, + ReplCommand::Breakpoint(location) => { + for addr in parse_breakpoint(context, &location)? { + set_breakpoint(context, addr)? + } + } ReplCommand::Stepi(()) => { println!("{:?}", context.remote()?.step()?); return print_source_for_top_of_stack_symbol(context, 3); @@ -388,36 +393,73 @@ mod example { Ok(()) } - fn set_breakpoint(context: &mut Context, location: &str) -> CrabResult<()> { + fn parse_breakpoint(context: &mut Context, location: &str) -> CrabResult> { context.load_debuginfo_if_necessary()?; - - if let Ok(addr) = { - usize::from_str_radix(&location, 10) - .map(|addr| addr as usize) - .map_err(|e| Box::new(e)) - .or_else(|_e| { - if location.starts_with("0x") { - let raw_num = location.trim_start_matches("0x"); - usize::from_str_radix(raw_num, 16) - .map(|addr| addr as usize) - .map_err(|_e| Box::new(format!("Invalid address format."))) - } else { - context - .debuginfo() - .get_symbol_address(&location) - .ok_or(Box::new(format!("No such symbol {}", location))) - } - }) - } { - context.mut_remote()?.set_breakpoint(addr)?; + if let Some(addr) = parse_address(location).or(parse_symbol(&location, context)) { + Ok(vec![addr]) } else { - Err(format!( - "Breakpoints must be set on a symbol or at a given address. For example `b main` or `b 0x0000555555559394` or even `b 93824992252820`" - ))? + if let Some(source_location) = parse_source_location(location) { + Ok(context + .debuginfo + .as_ref() + .unwrap() // this unwrap cannot fail due to the call to load_debuginfo_if_necessary + .find_location_addr(&source_location) + .unwrap() // todo: this unwrap could fail if the debugee contain invalid debug info. + .into_iter() + .map(|(_, addr)| addr as usize) + .collect()) + } else { + Err(format!( + "Breakpoints must be set on a symbol or at a given address. For example `b main` or `b 0x0000555555559394` or even `b 93824992252820`" + ))? + } } + } + + fn set_breakpoint(context: &mut Context, addr: usize) -> CrabResult<()> { + context.mut_remote()?.set_breakpoint(addr)?; Ok(()) } + fn parse_address(location: &str) -> Option { + if let Ok(addr) = usize::from_str_radix(&location, 10) { + return Some(addr); + } else { + if location.starts_with("0x") { + let raw_num = location.trim_start_matches("0x"); + if let Ok(addr) = usize::from_str_radix(raw_num, 16) { + return Some(addr); + } + } + } + None + } + + fn parse_symbol(location: &str, context: &mut Context) -> Option { + context.debuginfo().get_symbol_address(&location) + } + + fn parse_source_location(location: &str) -> Option { + use addr2line::Location; + + let mut iter = location.split(":"); + let file = iter.next(); + let line = iter + .next() + .map_or(None, parse_address) + .map_or(None, |num| NonZeroU32::new(num as u32)); + let column = iter + .next() + .map_or(None, parse_address) + .map_or(None, |num| NonZeroU32::new(num as u32)); + + if let (Some(file), Some(line)) = (file, line) { + Some(Location { file, line, column }) + } else { + None + } + } + fn show_backtrace(context: &mut Context, bt_type: &BacktraceType) -> CrabResult<()> { let call_stack: Vec<_> = get_call_stack(context, bt_type)?; for func in call_stack { @@ -437,13 +479,7 @@ mod example { let location = frame .location .as_ref() - .map(|loc| { - format!( - "{}:{}", - loc.file.unwrap_or(""), - loc.line.unwrap_or(0), - ) - }) + .map(|loc| format!("{}:{}", loc.file, loc.line)) .unwrap_or_default(); if first_frame { @@ -493,13 +529,7 @@ mod example { let location = frame .location .as_ref() - .map(|loc| { - format!( - "{}:{}", - loc.file.unwrap_or(""), - loc.line.unwrap_or(0), - ) - }) + .map(|loc| format!("{}:{}", loc.file, loc.line)) .unwrap_or_default(); if first_frame { @@ -637,9 +667,9 @@ mod example { .as_ref() .map(|loc| { ( - loc.file.unwrap_or(""), - loc.line.unwrap_or(0), - loc.column.unwrap_or(0), + loc.file, + loc.line.get(), + loc.column.map(NonZeroU32::get).unwrap_or(0), ) }) .unwrap_or_default(); diff --git a/src/symbol.rs b/src/symbol.rs index a9834fc5..f4235ff4 100644 --- a/src/symbol.rs +++ b/src/symbol.rs @@ -14,6 +14,7 @@ use std::{ borrow::Cow, collections::{BTreeMap, HashMap}, fs::File, + num::NonZeroU32, path::Path, }; pub use sym::Symbol; @@ -69,13 +70,7 @@ pub type Reader<'a> = gimli::EndianReader> pub struct ParsedDwarf<'a> { object: object::File<'a>, addr2line: addr2line::Context>, - vars: BTreeMap< - String, - ( - gimli::CompilationUnitHeader>, - gimli::Expression>, - ), - >, + vars: BTreeMap>, gimli::Expression>)>, symbols: Vec>, symbol_names: HashMap, } @@ -110,11 +105,9 @@ impl<'a> ParsedDwarf<'a> { None => Ok(gimli::EndianReader::new(RcCow::Borrowed(&[][..]), endian)), } }; - // we don't support supplementary object files for now - let sup_loader = |_| Ok(gimli::EndianReader::new(RcCow::Borrowed(&[][..]), endian)); // Create `EndianSlice`s for all of the sections. - let dwarf = gimli::Dwarf::load(loader, sup_loader)?; + let dwarf = gimli::Dwarf::load(loader)?; let addr2line = addr2line::Context::from_dwarf(dwarf)?; let dwarf = addr2line.dwarf(); @@ -235,6 +228,17 @@ impl<'a> ParsedDwarf<'a> { iter: self.addr2line.find_frames(addr as u64)?, }) } + + /// Find an address for each substatement in the `Location`. + /// This function returns a hashmap where a key is a column number of each statement + /// and a value is an address of an instruction from that statement. + /// If the `Location` specifies a column number, the hashmap will only contain one value. + pub fn find_location_addr( + &'a self, + location: &addr2line::Location, + ) -> CrabResult, u64>> { + Ok(self.addr2line.find_addresses(location)?) + } } mod inner { @@ -330,4 +334,15 @@ impl Dwarf { ) -> CrabResult { self.rent(|parsed| f(addr, parsed.get_addr_frames(addr)?)) } + + /// Find an address for each substatement in the `Location`. + /// This function returns a hashmap where a key is a column number of each statement + /// and a value is an address of an instruction from that statement. + /// If the `Location` specifies a column number, the hashmap will only contain one value. + pub fn find_location_addr( + &self, + location: &addr2line::Location, + ) -> CrabResult, u64>> { + self.rent(|parsed| parsed.find_location_addr(location)) + } } diff --git a/src/symbol/relocate.rs b/src/symbol/relocate.rs index f2e9eb05..45eca77a 100644 --- a/src/symbol/relocate.rs +++ b/src/symbol/relocate.rs @@ -199,4 +199,31 @@ impl RelocatedDwarf { } Ok(None) } + + /// Find an address for each substatement in the `Location`. + /// This function returns a hashmap where a key is a column number of each statement + /// and a value is an address of an instruction from that statement. + /// If the `Location` specifies a column number, the hashmap will only contain one value. + pub fn find_location_addr( + &self, + location: &addr2line::Location, + ) -> CrabResult, u64>> { + let mut addresses = HashMap::new(); + for entry in &self.0 { + let mut ignore_entry = false; + + if !ignore_entry { + addresses.extend(entry.dwarf.find_location_addr(location)?.into_iter().map( + |(column, addr)| { + if addr + entry.bias >= entry.address_range.0 + entry.address_range.1 { + ignore_entry = true; + } + + (column, addr + entry.bias) + }, + )) + } + } + Ok(addresses) + } } diff --git a/src/symbol/source.rs b/src/symbol/source.rs index 33e28e55..ee22a41c 100644 --- a/src/symbol/source.rs +++ b/src/symbol/source.rs @@ -2,6 +2,7 @@ use capstone::Capstone; use std::fs::File; use std::io::{prelude::*, BufReader}; +use std::num::NonZeroU32; use crate::CrabResult; @@ -62,12 +63,9 @@ impl super::Dwarf { .find_location(addr as u64)? .ok_or_else(|| "source location not found".to_string())?; Ok(( - location - .file - .ok_or_else(|| "Unknown file".to_string())? - .to_string(), - location.line.unwrap_or(0) as u64, - location.column.unwrap_or(0) as u64, + location.file.to_string(), + location.line.get() as u64, + location.column.map(NonZeroU32::get).unwrap_or(0) as u64, )) }) }