Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add line to address functinality #133

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions examples/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,7 @@ mod example {
let location = frame
.location
.as_ref()
.map(|loc| {
format!(
"{}:{}",
loc.file.unwrap_or("<unknown file>"),
loc.line.unwrap_or(0),
)
})
.map(|loc| format!("{}:{}", loc.file, loc.line))
.unwrap_or_default();

if first_frame {
Expand Down
114 changes: 72 additions & 42 deletions examples/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<Vec<usize>> {
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<usize> {
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<usize> {
context.debuginfo().get_symbol_address(&location)
}

fn parse_source_location(location: &str) -> Option<addr2line::Location> {
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 {
Expand All @@ -437,13 +479,7 @@ mod example {
let location = frame
.location
.as_ref()
.map(|loc| {
format!(
"{}:{}",
loc.file.unwrap_or("<unknown file>"),
loc.line.unwrap_or(0),
)
})
.map(|loc| format!("{}:{}", loc.file, loc.line))
.unwrap_or_default();

if first_frame {
Expand Down Expand Up @@ -493,13 +529,7 @@ mod example {
let location = frame
.location
.as_ref()
.map(|loc| {
format!(
"{}:{}",
loc.file.unwrap_or("<unknown file>"),
loc.line.unwrap_or(0),
)
})
.map(|loc| format!("{}:{}", loc.file, loc.line))
.unwrap_or_default();

if first_frame {
Expand Down Expand Up @@ -637,9 +667,9 @@ mod example {
.as_ref()
.map(|loc| {
(
loc.file.unwrap_or("<unknown file>"),
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();
Expand Down
35 changes: 25 additions & 10 deletions src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::{
borrow::Cow,
collections::{BTreeMap, HashMap},
fs::File,
num::NonZeroU32,
path::Path,
};
pub use sym::Symbol;
Expand Down Expand Up @@ -69,13 +70,7 @@ pub type Reader<'a> = gimli::EndianReader<gimli::RunTimeEndian, RcCow<'a, [u8]>>
pub struct ParsedDwarf<'a> {
object: object::File<'a>,
addr2line: addr2line::Context<Reader<'a>>,
vars: BTreeMap<
String,
(
gimli::CompilationUnitHeader<Reader<'a>>,
gimli::Expression<Reader<'a>>,
),
>,
vars: BTreeMap<String, (gimli::UnitHeader<Reader<'a>>, gimli::Expression<Reader<'a>>)>,
symbols: Vec<Symbol<'a>>,
symbol_names: HashMap<String, usize>,
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<HashMap<Option<NonZeroU32>, u64>> {
Ok(self.addr2line.find_addresses(location)?)
}
}

mod inner {
Expand Down Expand Up @@ -330,4 +334,15 @@ impl Dwarf {
) -> CrabResult<T> {
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<HashMap<Option<NonZeroU32>, u64>> {
self.rent(|parsed| parsed.find_location_addr(location))
}
}
27 changes: 27 additions & 0 deletions src/symbol/relocate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HashMap<Option<NonZeroU32>, 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)
}
}
10 changes: 4 additions & 6 deletions src/symbol/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use capstone::Capstone;

use std::fs::File;
use std::io::{prelude::*, BufReader};
use std::num::NonZeroU32;

use crate::CrabResult;

Expand Down Expand Up @@ -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,
))
})
}
Expand Down