2399 lines
78 KiB
Rust
2399 lines
78 KiB
Rust
// Allow clippy lints when building without clippy.
|
|
#![allow(unknown_lints)]
|
|
|
|
use fallible_iterator::FallibleIterator;
|
|
use gimli::{Section, UnitHeader, UnitOffset, UnitSectionOffset, UnitType, UnwindSection};
|
|
use object::{Object, ObjectSection, ObjectSymbol};
|
|
use regex::bytes::Regex;
|
|
use std::borrow::{Borrow, Cow};
|
|
use std::cmp::min;
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::fmt::{self, Debug};
|
|
use std::fs;
|
|
use std::io;
|
|
use std::io::{BufWriter, Write};
|
|
use std::iter::Iterator;
|
|
use std::mem;
|
|
use std::process;
|
|
use std::result;
|
|
use std::sync::{Condvar, Mutex};
|
|
use typed_arena::Arena;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Error {
|
|
GimliError(gimli::Error),
|
|
ObjectError(object::read::Error),
|
|
IoError,
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
|
|
Debug::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
fn writeln_error<W: Write, R: Reader>(
|
|
w: &mut W,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
err: Error,
|
|
msg: &str,
|
|
) -> io::Result<()> {
|
|
writeln!(
|
|
w,
|
|
"{}: {}",
|
|
msg,
|
|
match err {
|
|
Error::GimliError(err) => dwarf.format_error(err),
|
|
Error::ObjectError(err) =>
|
|
format!("{}:{:?}", "An object error occurred while reading", err),
|
|
Error::IoError => "An I/O error occurred while writing.".to_string(),
|
|
}
|
|
)
|
|
}
|
|
|
|
impl From<gimli::Error> for Error {
|
|
fn from(err: gimli::Error) -> Self {
|
|
Error::GimliError(err)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(_: io::Error) -> Self {
|
|
Error::IoError
|
|
}
|
|
}
|
|
|
|
impl From<object::read::Error> for Error {
|
|
fn from(err: object::read::Error) -> Self {
|
|
Error::ObjectError(err)
|
|
}
|
|
}
|
|
|
|
pub type Result<T> = result::Result<T, Error>;
|
|
|
|
fn parallel_output<W, II, F>(w: &mut W, max_workers: usize, iter: II, f: F) -> Result<()>
|
|
where
|
|
W: Write + Send,
|
|
F: Sync + Fn(II::Item, &mut Vec<u8>) -> Result<()>,
|
|
II: IntoIterator,
|
|
II::IntoIter: Send,
|
|
{
|
|
struct ParallelOutputState<I, W> {
|
|
iterator: I,
|
|
current_worker: usize,
|
|
result: Result<()>,
|
|
w: W,
|
|
}
|
|
|
|
let state = Mutex::new(ParallelOutputState {
|
|
iterator: iter.into_iter().fuse(),
|
|
current_worker: 0,
|
|
result: Ok(()),
|
|
w,
|
|
});
|
|
let workers = min(max_workers, num_cpus::get());
|
|
let mut condvars = Vec::new();
|
|
for _ in 0..workers {
|
|
condvars.push(Condvar::new());
|
|
}
|
|
{
|
|
let state_ref = &state;
|
|
let f_ref = &f;
|
|
let condvars_ref = &condvars;
|
|
crossbeam::scope(|scope| {
|
|
for i in 0..workers {
|
|
scope.spawn(move |_| {
|
|
let mut v = Vec::new();
|
|
let mut lock = state_ref.lock().unwrap();
|
|
while lock.current_worker != i {
|
|
lock = condvars_ref[i].wait(lock).unwrap();
|
|
}
|
|
loop {
|
|
let item = if lock.result.is_ok() {
|
|
lock.iterator.next()
|
|
} else {
|
|
None
|
|
};
|
|
lock.current_worker = (i + 1) % workers;
|
|
condvars_ref[lock.current_worker].notify_one();
|
|
mem::drop(lock);
|
|
|
|
let ret = if let Some(item) = item {
|
|
v.clear();
|
|
f_ref(item, &mut v)
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
lock = state_ref.lock().unwrap();
|
|
while lock.current_worker != i {
|
|
lock = condvars_ref[i].wait(lock).unwrap();
|
|
}
|
|
if lock.result.is_ok() {
|
|
let ret2 = lock.w.write_all(&v);
|
|
if ret.is_err() {
|
|
lock.result = ret;
|
|
} else {
|
|
lock.result = ret2.map_err(Error::from);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.unwrap();
|
|
}
|
|
state.into_inner().unwrap().result
|
|
}
|
|
|
|
trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
|
|
|
|
impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where
|
|
Endian: gimli::Endianity + Send + Sync
|
|
{
|
|
}
|
|
|
|
type RelocationMap = HashMap<usize, object::Relocation>;
|
|
|
|
fn add_relocations(
|
|
relocations: &mut RelocationMap,
|
|
file: &object::File,
|
|
section: &object::Section,
|
|
) {
|
|
for (offset64, mut relocation) in section.relocations() {
|
|
let offset = offset64 as usize;
|
|
if offset as u64 != offset64 {
|
|
continue;
|
|
}
|
|
let offset = offset as usize;
|
|
match relocation.kind() {
|
|
object::RelocationKind::Absolute => {
|
|
match relocation.target() {
|
|
object::RelocationTarget::Symbol(symbol_idx) => {
|
|
match file.symbol_by_index(symbol_idx) {
|
|
Ok(symbol) => {
|
|
let addend =
|
|
symbol.address().wrapping_add(relocation.addend() as u64);
|
|
relocation.set_addend(addend as i64);
|
|
}
|
|
Err(_) => {
|
|
eprintln!(
|
|
"Relocation with invalid symbol for section {} at offset 0x{:08x}",
|
|
section.name().unwrap(),
|
|
offset
|
|
);
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
if relocations.insert(offset, relocation).is_some() {
|
|
eprintln!(
|
|
"Multiple relocations for section {} at offset 0x{:08x}",
|
|
section.name().unwrap(),
|
|
offset
|
|
);
|
|
}
|
|
}
|
|
_ => {
|
|
eprintln!(
|
|
"Unsupported relocation for section {} at offset 0x{:08x}",
|
|
section.name().unwrap(),
|
|
offset
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Apply relocations to addresses and offsets during parsing,
|
|
/// instead of requiring the data to be fully relocated prior
|
|
/// to parsing.
|
|
///
|
|
/// Pros
|
|
/// - allows readonly buffers, we don't need to implement writing of values back to buffers
|
|
/// - potentially allows us to handle addresses and offsets differently
|
|
/// - potentially allows us to add metadata from the relocation (eg symbol names)
|
|
/// Cons
|
|
/// - maybe incomplete
|
|
#[derive(Debug, Clone)]
|
|
struct Relocate<'a, R: gimli::Reader<Offset = usize>> {
|
|
relocations: &'a RelocationMap,
|
|
section: R,
|
|
reader: R,
|
|
}
|
|
|
|
impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> {
|
|
fn relocate(&self, offset: usize, value: u64) -> u64 {
|
|
if let Some(relocation) = self.relocations.get(&offset) {
|
|
match relocation.kind() {
|
|
object::RelocationKind::Absolute => {
|
|
if relocation.has_implicit_addend() {
|
|
// Use the explicit addend too, because it may have the symbol value.
|
|
return value.wrapping_add(relocation.addend() as u64);
|
|
} else {
|
|
return relocation.addend() as u64;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
};
|
|
value
|
|
}
|
|
}
|
|
|
|
impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> {
|
|
type Endian = R::Endian;
|
|
type Offset = R::Offset;
|
|
|
|
fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> {
|
|
let offset = self.reader.offset_from(&self.section);
|
|
let value = self.reader.read_address(address_size)?;
|
|
Ok(self.relocate(offset, value))
|
|
}
|
|
|
|
fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> {
|
|
let offset = self.reader.offset_from(&self.section);
|
|
let value = self.reader.read_length(format)?;
|
|
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
|
|
}
|
|
|
|
fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> {
|
|
let offset = self.reader.offset_from(&self.section);
|
|
let value = self.reader.read_offset(format)?;
|
|
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
|
|
}
|
|
|
|
fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> {
|
|
let offset = self.reader.offset_from(&self.section);
|
|
let value = self.reader.read_sized_offset(size)?;
|
|
<usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
|
|
}
|
|
|
|
#[inline]
|
|
fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> {
|
|
let mut other = self.clone();
|
|
other.reader.truncate(len)?;
|
|
self.reader.skip(len)?;
|
|
Ok(other)
|
|
}
|
|
|
|
// All remaining methods simply delegate to `self.reader`.
|
|
|
|
#[inline]
|
|
fn endian(&self) -> Self::Endian {
|
|
self.reader.endian()
|
|
}
|
|
|
|
#[inline]
|
|
fn len(&self) -> Self::Offset {
|
|
self.reader.len()
|
|
}
|
|
|
|
#[inline]
|
|
fn empty(&mut self) {
|
|
self.reader.empty()
|
|
}
|
|
|
|
#[inline]
|
|
fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> {
|
|
self.reader.truncate(len)
|
|
}
|
|
|
|
#[inline]
|
|
fn offset_from(&self, base: &Self) -> Self::Offset {
|
|
self.reader.offset_from(&base.reader)
|
|
}
|
|
|
|
#[inline]
|
|
fn offset_id(&self) -> gimli::ReaderOffsetId {
|
|
self.reader.offset_id()
|
|
}
|
|
|
|
#[inline]
|
|
fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> {
|
|
self.reader.lookup_offset_id(id)
|
|
}
|
|
|
|
#[inline]
|
|
fn find(&self, byte: u8) -> gimli::Result<Self::Offset> {
|
|
self.reader.find(byte)
|
|
}
|
|
|
|
#[inline]
|
|
fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> {
|
|
self.reader.skip(len)
|
|
}
|
|
|
|
#[inline]
|
|
fn to_slice(&self) -> gimli::Result<Cow<[u8]>> {
|
|
self.reader.to_slice()
|
|
}
|
|
|
|
#[inline]
|
|
fn to_string(&self) -> gimli::Result<Cow<str>> {
|
|
self.reader.to_string()
|
|
}
|
|
|
|
#[inline]
|
|
fn to_string_lossy(&self) -> gimli::Result<Cow<str>> {
|
|
self.reader.to_string_lossy()
|
|
}
|
|
|
|
#[inline]
|
|
fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> {
|
|
self.reader.read_slice(buf)
|
|
}
|
|
}
|
|
|
|
impl<'a, R: Reader> Reader for Relocate<'a, R> {}
|
|
|
|
#[derive(Default)]
|
|
struct Flags<'a> {
|
|
eh_frame: bool,
|
|
goff: bool,
|
|
info: bool,
|
|
line: bool,
|
|
pubnames: bool,
|
|
pubtypes: bool,
|
|
aranges: bool,
|
|
dwo: bool,
|
|
dwp: bool,
|
|
dwo_parent: Option<object::File<'a>>,
|
|
sup: Option<object::File<'a>>,
|
|
raw: bool,
|
|
match_units: Option<Regex>,
|
|
}
|
|
|
|
fn print_usage(opts: &getopts::Options) -> ! {
|
|
let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
|
|
write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
|
|
process::exit(1);
|
|
}
|
|
|
|
fn main() {
|
|
let mut opts = getopts::Options::new();
|
|
opts.optflag(
|
|
"",
|
|
"eh-frame",
|
|
"print .eh-frame exception handling frame information",
|
|
);
|
|
opts.optflag("G", "", "show global die offsets");
|
|
opts.optflag("i", "", "print .debug_info and .debug_types sections");
|
|
opts.optflag("l", "", "print .debug_line section");
|
|
opts.optflag("p", "", "print .debug_pubnames section");
|
|
opts.optflag("r", "", "print .debug_aranges section");
|
|
opts.optflag("y", "", "print .debug_pubtypes section");
|
|
opts.optflag(
|
|
"",
|
|
"dwo",
|
|
"print the .dwo versions of the selected sections",
|
|
);
|
|
opts.optflag(
|
|
"",
|
|
"dwp",
|
|
"print the .dwp versions of the selected sections",
|
|
);
|
|
opts.optopt(
|
|
"",
|
|
"dwo-parent",
|
|
"use the specified file as the parent of the dwo or dwp (e.g. for .debug_addr)",
|
|
"library path",
|
|
);
|
|
opts.optflag("", "raw", "print raw data values");
|
|
opts.optopt(
|
|
"u",
|
|
"match-units",
|
|
"print compilation units whose output matches a regex",
|
|
"REGEX",
|
|
);
|
|
opts.optopt("", "sup", "path to supplementary object file", "PATH");
|
|
|
|
let matches = match opts.parse(env::args().skip(1)) {
|
|
Ok(m) => m,
|
|
Err(e) => {
|
|
writeln!(&mut io::stderr(), "{:?}\n", e).ok();
|
|
print_usage(&opts);
|
|
}
|
|
};
|
|
if matches.free.is_empty() {
|
|
print_usage(&opts);
|
|
}
|
|
|
|
let mut all = true;
|
|
let mut flags = Flags::default();
|
|
if matches.opt_present("eh-frame") {
|
|
flags.eh_frame = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("G") {
|
|
flags.goff = true;
|
|
}
|
|
if matches.opt_present("i") {
|
|
flags.info = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("l") {
|
|
flags.line = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("p") {
|
|
flags.pubnames = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("y") {
|
|
flags.pubtypes = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("r") {
|
|
flags.aranges = true;
|
|
all = false;
|
|
}
|
|
if matches.opt_present("dwo") {
|
|
flags.dwo = true;
|
|
}
|
|
if matches.opt_present("dwp") {
|
|
flags.dwp = true;
|
|
}
|
|
if matches.opt_present("raw") {
|
|
flags.raw = true;
|
|
}
|
|
if all {
|
|
// .eh_frame is excluded even when printing all information.
|
|
// cosmetic flags like -G must be set explicitly too.
|
|
flags.info = true;
|
|
flags.line = true;
|
|
flags.pubnames = true;
|
|
flags.pubtypes = true;
|
|
flags.aranges = true;
|
|
}
|
|
flags.match_units = if let Some(r) = matches.opt_str("u") {
|
|
match Regex::new(&r) {
|
|
Ok(r) => Some(r),
|
|
Err(e) => {
|
|
eprintln!("Invalid regular expression {}: {}", r, e);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let arena_mmap = Arena::new();
|
|
let load_file = |path| {
|
|
let file = match fs::File::open(&path) {
|
|
Ok(file) => file,
|
|
Err(err) => {
|
|
eprintln!("Failed to open file '{}': {}", path, err);
|
|
process::exit(1);
|
|
}
|
|
};
|
|
let mmap = match unsafe { memmap::Mmap::map(&file) } {
|
|
Ok(mmap) => mmap,
|
|
Err(err) => {
|
|
eprintln!("Failed to map file '{}': {}", path, err);
|
|
process::exit(1);
|
|
}
|
|
};
|
|
let mmap_ref = (*arena_mmap.alloc(mmap)).borrow();
|
|
match object::File::parse(&**mmap_ref) {
|
|
Ok(file) => Some(file),
|
|
Err(err) => {
|
|
eprintln!("Failed to parse file '{}': {}", path, err);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
flags.sup = matches.opt_str("sup").and_then(load_file);
|
|
flags.dwo_parent = matches.opt_str("dwo-parent").and_then(load_file);
|
|
if flags.dwo_parent.is_some() && !flags.dwo && !flags.dwp {
|
|
eprintln!("--dwo-parent also requires --dwo or --dwp");
|
|
process::exit(1);
|
|
}
|
|
if flags.dwo_parent.is_none() && flags.dwp {
|
|
eprintln!("--dwp also requires --dwo-parent");
|
|
process::exit(1);
|
|
}
|
|
|
|
for file_path in &matches.free {
|
|
if matches.free.len() != 1 {
|
|
println!("{}", file_path);
|
|
println!();
|
|
}
|
|
|
|
let file = match fs::File::open(&file_path) {
|
|
Ok(file) => file,
|
|
Err(err) => {
|
|
eprintln!("Failed to open file '{}': {}", file_path, err);
|
|
continue;
|
|
}
|
|
};
|
|
let file = match unsafe { memmap::Mmap::map(&file) } {
|
|
Ok(mmap) => mmap,
|
|
Err(err) => {
|
|
eprintln!("Failed to map file '{}': {}", file_path, err);
|
|
continue;
|
|
}
|
|
};
|
|
let file = match object::File::parse(&*file) {
|
|
Ok(file) => file,
|
|
Err(err) => {
|
|
eprintln!("Failed to parse file '{}': {}", file_path, err);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let endian = if file.is_little_endian() {
|
|
gimli::RunTimeEndian::Little
|
|
} else {
|
|
gimli::RunTimeEndian::Big
|
|
};
|
|
let ret = dump_file(&file, endian, &flags);
|
|
match ret {
|
|
Ok(_) => (),
|
|
Err(err) => eprintln!("Failed to dump '{}': {}", file_path, err,),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn empty_file_section<'input, 'arena, Endian: gimli::Endianity>(
|
|
endian: Endian,
|
|
arena_relocations: &'arena Arena<RelocationMap>,
|
|
) -> Relocate<'arena, gimli::EndianSlice<'arena, Endian>> {
|
|
let reader = gimli::EndianSlice::new(&[], endian);
|
|
let section = reader;
|
|
let relocations = RelocationMap::default();
|
|
let relocations = (*arena_relocations.alloc(relocations)).borrow();
|
|
Relocate {
|
|
relocations,
|
|
section,
|
|
reader,
|
|
}
|
|
}
|
|
|
|
fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
|
|
id: gimli::SectionId,
|
|
file: &object::File<'input>,
|
|
endian: Endian,
|
|
is_dwo: bool,
|
|
arena_data: &'arena Arena<Cow<'input, [u8]>>,
|
|
arena_relocations: &'arena Arena<RelocationMap>,
|
|
) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
|
|
let mut relocations = RelocationMap::default();
|
|
let name = if is_dwo {
|
|
id.dwo_name()
|
|
} else {
|
|
Some(id.name())
|
|
};
|
|
|
|
let data = match name.and_then(|name| file.section_by_name(&name)) {
|
|
Some(ref section) => {
|
|
// DWO sections never have relocations, so don't bother.
|
|
if !is_dwo {
|
|
add_relocations(&mut relocations, file, section);
|
|
}
|
|
section.uncompressed_data()?
|
|
}
|
|
// Use a non-zero capacity so that `ReaderOffsetId`s are unique.
|
|
None => Cow::Owned(Vec::with_capacity(1)),
|
|
};
|
|
let data_ref = (*arena_data.alloc(data)).borrow();
|
|
let reader = gimli::EndianSlice::new(data_ref, endian);
|
|
let section = reader;
|
|
let relocations = (*arena_relocations.alloc(relocations)).borrow();
|
|
Ok(Relocate {
|
|
relocations,
|
|
section,
|
|
reader,
|
|
})
|
|
}
|
|
|
|
fn dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Result<()>
|
|
where
|
|
Endian: gimli::Endianity + Send + Sync,
|
|
{
|
|
let arena_data = Arena::new();
|
|
let arena_relocations = Arena::new();
|
|
|
|
let dwo_parent = if let Some(dwo_parent_file) = flags.dwo_parent.as_ref() {
|
|
let mut load_dwo_parent_section = |id: gimli::SectionId| -> Result<_> {
|
|
load_file_section(
|
|
id,
|
|
dwo_parent_file,
|
|
endian,
|
|
false,
|
|
&arena_data,
|
|
&arena_relocations,
|
|
)
|
|
};
|
|
Some(gimli::Dwarf::load(&mut load_dwo_parent_section)?)
|
|
} else {
|
|
None
|
|
};
|
|
let dwo_parent = dwo_parent.as_ref();
|
|
|
|
let dwo_parent_units = if let Some(dwo_parent) = dwo_parent {
|
|
Some(
|
|
match dwo_parent
|
|
.units()
|
|
.map(|unit_header| dwo_parent.unit(unit_header))
|
|
.filter_map(|unit| Ok(unit.dwo_id.map(|dwo_id| (dwo_id, unit))))
|
|
.collect()
|
|
{
|
|
Ok(units) => units,
|
|
Err(err) => {
|
|
eprintln!("Failed to process --dwo-parent units: {}", err);
|
|
return Ok(());
|
|
}
|
|
},
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
let dwo_parent_units = dwo_parent_units.as_ref();
|
|
|
|
let mut load_section = |id: gimli::SectionId| -> Result<_> {
|
|
load_file_section(
|
|
id,
|
|
file,
|
|
endian,
|
|
flags.dwo || flags.dwp,
|
|
&arena_data,
|
|
&arena_relocations,
|
|
)
|
|
};
|
|
|
|
let w = &mut BufWriter::new(io::stdout());
|
|
if flags.dwp {
|
|
let empty = empty_file_section(endian, &arena_relocations);
|
|
let dwp = gimli::DwarfPackage::load(&mut load_section, empty)?;
|
|
dump_dwp(w, &dwp, dwo_parent.unwrap(), dwo_parent_units, flags)?;
|
|
w.flush()?;
|
|
return Ok(());
|
|
}
|
|
|
|
let mut dwarf = gimli::Dwarf::load(&mut load_section)?;
|
|
if flags.dwo {
|
|
dwarf.file_type = gimli::DwarfFileType::Dwo;
|
|
if let Some(dwo_parent) = dwo_parent {
|
|
dwarf.debug_addr = dwo_parent.debug_addr.clone();
|
|
dwarf
|
|
.ranges
|
|
.set_debug_ranges(dwo_parent.ranges.debug_ranges().clone());
|
|
}
|
|
}
|
|
|
|
if let Some(sup_file) = flags.sup.as_ref() {
|
|
let mut load_sup_section = |id: gimli::SectionId| -> Result<_> {
|
|
// Note: we really only need the `.debug_str` section,
|
|
// but for now we load them all.
|
|
load_file_section(id, sup_file, endian, false, &arena_data, &arena_relocations)
|
|
};
|
|
dwarf.load_sup(&mut load_sup_section)?;
|
|
}
|
|
|
|
if flags.eh_frame {
|
|
let eh_frame = gimli::EhFrame::load(&mut load_section).unwrap();
|
|
dump_eh_frame(w, file, eh_frame)?;
|
|
}
|
|
if flags.info {
|
|
dump_info(w, &dwarf, dwo_parent_units, flags)?;
|
|
dump_types(w, &dwarf, dwo_parent_units, flags)?;
|
|
}
|
|
if flags.line {
|
|
dump_line(w, &dwarf)?;
|
|
}
|
|
if flags.pubnames {
|
|
let debug_pubnames = &gimli::Section::load(&mut load_section).unwrap();
|
|
dump_pubnames(w, debug_pubnames, &dwarf.debug_info)?;
|
|
}
|
|
if flags.aranges {
|
|
let debug_aranges = &gimli::Section::load(&mut load_section).unwrap();
|
|
dump_aranges(w, debug_aranges)?;
|
|
}
|
|
if flags.pubtypes {
|
|
let debug_pubtypes = &gimli::Section::load(&mut load_section).unwrap();
|
|
dump_pubtypes(w, debug_pubtypes, &dwarf.debug_info)?;
|
|
}
|
|
w.flush()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_eh_frame<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
file: &object::File,
|
|
mut eh_frame: gimli::EhFrame<R>,
|
|
) -> Result<()> {
|
|
// TODO: this might be better based on the file format.
|
|
let address_size = file
|
|
.architecture()
|
|
.address_size()
|
|
.map(|w| w.bytes())
|
|
.unwrap_or(mem::size_of::<usize>() as u8);
|
|
eh_frame.set_address_size(address_size);
|
|
|
|
fn register_name_none(_: gimli::Register) -> Option<&'static str> {
|
|
None
|
|
}
|
|
let arch_register_name = match file.architecture() {
|
|
object::Architecture::Arm | object::Architecture::Aarch64 => gimli::Arm::register_name,
|
|
object::Architecture::I386 => gimli::X86::register_name,
|
|
object::Architecture::X86_64 => gimli::X86_64::register_name,
|
|
_ => register_name_none,
|
|
};
|
|
let register_name = &|register| match arch_register_name(register) {
|
|
Some(name) => Cow::Borrowed(name),
|
|
None => Cow::Owned(format!("{}", register.0)),
|
|
};
|
|
|
|
let mut bases = gimli::BaseAddresses::default();
|
|
if let Some(section) = file.section_by_name(".eh_frame_hdr") {
|
|
bases = bases.set_eh_frame_hdr(section.address());
|
|
}
|
|
if let Some(section) = file.section_by_name(".eh_frame") {
|
|
bases = bases.set_eh_frame(section.address());
|
|
}
|
|
if let Some(section) = file.section_by_name(".text") {
|
|
bases = bases.set_text(section.address());
|
|
}
|
|
if let Some(section) = file.section_by_name(".got") {
|
|
bases = bases.set_got(section.address());
|
|
}
|
|
|
|
// TODO: Print "__eh_frame" here on macOS, and more generally use the
|
|
// section that we're actually looking at, which is what the canonical
|
|
// dwarfdump does.
|
|
writeln!(
|
|
w,
|
|
"Exception handling frame information for section .eh_frame"
|
|
)?;
|
|
|
|
let mut cies = HashMap::new();
|
|
|
|
let mut entries = eh_frame.entries(&bases);
|
|
loop {
|
|
match entries.next()? {
|
|
None => return Ok(()),
|
|
Some(gimli::CieOrFde::Cie(cie)) => {
|
|
writeln!(w)?;
|
|
writeln!(w, "{:#010x}: CIE", cie.offset())?;
|
|
writeln!(w, " length: {:#010x}", cie.entry_len())?;
|
|
// TODO: CIE_id
|
|
writeln!(w, " version: {:#04x}", cie.version())?;
|
|
// TODO: augmentation
|
|
writeln!(w, " code_align: {}", cie.code_alignment_factor())?;
|
|
writeln!(w, " data_align: {}", cie.data_alignment_factor())?;
|
|
writeln!(w, " ra_register: {:#x}", cie.return_address_register().0)?;
|
|
if let Some(encoding) = cie.lsda_encoding() {
|
|
writeln!(w, " lsda_encoding: {:#02x}", encoding.0)?;
|
|
}
|
|
if let Some((encoding, personality)) = cie.personality_with_encoding() {
|
|
write!(w, " personality: {:#02x} ", encoding.0)?;
|
|
dump_pointer(w, personality)?;
|
|
writeln!(w)?;
|
|
}
|
|
if let Some(encoding) = cie.fde_address_encoding() {
|
|
writeln!(w, " fde_encoding: {:#02x}", encoding.0)?;
|
|
}
|
|
let instructions = cie.instructions(&eh_frame, &bases);
|
|
dump_cfi_instructions(w, instructions, true, register_name)?;
|
|
writeln!(w)?;
|
|
}
|
|
Some(gimli::CieOrFde::Fde(partial)) => {
|
|
let mut offset = None;
|
|
let fde = partial.parse(|_, bases, o| {
|
|
offset = Some(o);
|
|
cies.entry(o)
|
|
.or_insert_with(|| eh_frame.cie_from_offset(bases, o))
|
|
.clone()
|
|
})?;
|
|
|
|
writeln!(w)?;
|
|
writeln!(w, "{:#010x}: FDE", fde.offset())?;
|
|
writeln!(w, " length: {:#010x}", fde.entry_len())?;
|
|
writeln!(w, " CIE_pointer: {:#010x}", offset.unwrap().0)?;
|
|
// TODO: symbolicate the start address like the canonical dwarfdump does.
|
|
writeln!(w, " start_addr: {:#018x}", fde.initial_address())?;
|
|
writeln!(
|
|
w,
|
|
" range_size: {:#018x} (end_addr = {:#018x})",
|
|
fde.len(),
|
|
fde.initial_address() + fde.len()
|
|
)?;
|
|
if let Some(lsda) = fde.lsda() {
|
|
write!(w, " lsda: ")?;
|
|
dump_pointer(w, lsda)?;
|
|
writeln!(w)?;
|
|
}
|
|
let instructions = fde.instructions(&eh_frame, &bases);
|
|
dump_cfi_instructions(w, instructions, false, register_name)?;
|
|
writeln!(w)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn dump_pointer<W: Write>(w: &mut W, p: gimli::Pointer) -> Result<()> {
|
|
match p {
|
|
gimli::Pointer::Direct(p) => {
|
|
write!(w, "{:#018x}", p)?;
|
|
}
|
|
gimli::Pointer::Indirect(p) => {
|
|
write!(w, "({:#018x})", p)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::unneeded_field_pattern)]
|
|
fn dump_cfi_instructions<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
mut insns: gimli::CallFrameInstructionIter<R>,
|
|
is_initial: bool,
|
|
register_name: &dyn Fn(gimli::Register) -> Cow<'static, str>,
|
|
) -> Result<()> {
|
|
use gimli::CallFrameInstruction::*;
|
|
|
|
// TODO: we need to actually evaluate these instructions as we iterate them
|
|
// so we can print the initialized state for CIEs, and each unwind row's
|
|
// registers for FDEs.
|
|
//
|
|
// TODO: We should print DWARF expressions for the CFI instructions that
|
|
// embed DWARF expressions within themselves.
|
|
|
|
if !is_initial {
|
|
writeln!(w, " Instructions:")?;
|
|
}
|
|
|
|
loop {
|
|
match insns.next() {
|
|
Err(e) => {
|
|
writeln!(w, "Failed to decode CFI instruction: {}", e)?;
|
|
return Ok(());
|
|
}
|
|
Ok(None) => {
|
|
if is_initial {
|
|
writeln!(w, " Instructions: Init State:")?;
|
|
}
|
|
return Ok(());
|
|
}
|
|
Ok(Some(op)) => match op {
|
|
SetLoc { address } => {
|
|
writeln!(w, " DW_CFA_set_loc ({:#x})", address)?;
|
|
}
|
|
AdvanceLoc { delta } => {
|
|
writeln!(w, " DW_CFA_advance_loc ({})", delta)?;
|
|
}
|
|
DefCfa { register, offset } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_def_cfa ({}, {})",
|
|
register_name(register),
|
|
offset
|
|
)?;
|
|
}
|
|
DefCfaSf {
|
|
register,
|
|
factored_offset,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_def_cfa_sf ({}, {})",
|
|
register_name(register),
|
|
factored_offset
|
|
)?;
|
|
}
|
|
DefCfaRegister { register } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_def_cfa_register ({})",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
DefCfaOffset { offset } => {
|
|
writeln!(w, " DW_CFA_def_cfa_offset ({})", offset)?;
|
|
}
|
|
DefCfaOffsetSf { factored_offset } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_def_cfa_offset_sf ({})",
|
|
factored_offset
|
|
)?;
|
|
}
|
|
DefCfaExpression { expression: _ } => {
|
|
writeln!(w, " DW_CFA_def_cfa_expression (...)")?;
|
|
}
|
|
Undefined { register } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_undefined ({})",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
SameValue { register } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_same_value ({})",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
Offset {
|
|
register,
|
|
factored_offset,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_offset ({}, {})",
|
|
register_name(register),
|
|
factored_offset
|
|
)?;
|
|
}
|
|
OffsetExtendedSf {
|
|
register,
|
|
factored_offset,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_offset_extended_sf ({}, {})",
|
|
register_name(register),
|
|
factored_offset
|
|
)?;
|
|
}
|
|
ValOffset {
|
|
register,
|
|
factored_offset,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_val_offset ({}, {})",
|
|
register_name(register),
|
|
factored_offset
|
|
)?;
|
|
}
|
|
ValOffsetSf {
|
|
register,
|
|
factored_offset,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_val_offset_sf ({}, {})",
|
|
register_name(register),
|
|
factored_offset
|
|
)?;
|
|
}
|
|
Register {
|
|
dest_register,
|
|
src_register,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_register ({}, {})",
|
|
register_name(dest_register),
|
|
register_name(src_register)
|
|
)?;
|
|
}
|
|
Expression {
|
|
register,
|
|
expression: _,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_expression ({}, ...)",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
ValExpression {
|
|
register,
|
|
expression: _,
|
|
} => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_val_expression ({}, ...)",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
Restore { register } => {
|
|
writeln!(
|
|
w,
|
|
" DW_CFA_restore ({})",
|
|
register_name(register)
|
|
)?;
|
|
}
|
|
RememberState => {
|
|
writeln!(w, " DW_CFA_remember_state")?;
|
|
}
|
|
RestoreState => {
|
|
writeln!(w, " DW_CFA_restore_state")?;
|
|
}
|
|
ArgsSize { size } => {
|
|
writeln!(w, " DW_CFA_GNU_args_size ({})", size)?;
|
|
}
|
|
Nop => {
|
|
writeln!(w, " DW_CFA_nop")?;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn dump_dwp<R: Reader, W: Write + Send>(
|
|
w: &mut W,
|
|
dwp: &gimli::DwarfPackage<R>,
|
|
dwo_parent: &gimli::Dwarf<R>,
|
|
dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
|
|
flags: &Flags,
|
|
) -> Result<()>
|
|
where
|
|
R::Endian: Send + Sync,
|
|
{
|
|
if dwp.cu_index.unit_count() != 0 {
|
|
writeln!(
|
|
w,
|
|
"\n.debug_cu_index: version = {}, sections = {}, units = {}, slots = {}",
|
|
dwp.cu_index.version(),
|
|
dwp.cu_index.section_count(),
|
|
dwp.cu_index.unit_count(),
|
|
dwp.cu_index.slot_count(),
|
|
)?;
|
|
for i in 1..=dwp.cu_index.unit_count() {
|
|
writeln!(w, "\nCU index {}", i)?;
|
|
dump_dwp_sections(
|
|
w,
|
|
&dwp,
|
|
dwo_parent,
|
|
dwo_parent_units,
|
|
flags,
|
|
dwp.cu_index.sections(i)?,
|
|
)?;
|
|
}
|
|
}
|
|
|
|
if dwp.tu_index.unit_count() != 0 {
|
|
writeln!(
|
|
w,
|
|
"\n.debug_tu_index: version = {}, sections = {}, units = {}, slots = {}",
|
|
dwp.tu_index.version(),
|
|
dwp.tu_index.section_count(),
|
|
dwp.tu_index.unit_count(),
|
|
dwp.tu_index.slot_count(),
|
|
)?;
|
|
for i in 1..=dwp.tu_index.unit_count() {
|
|
writeln!(w, "\nTU index {}", i)?;
|
|
dump_dwp_sections(
|
|
w,
|
|
&dwp,
|
|
dwo_parent,
|
|
dwo_parent_units,
|
|
flags,
|
|
dwp.tu_index.sections(i)?,
|
|
)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_dwp_sections<R: Reader, W: Write + Send>(
|
|
w: &mut W,
|
|
dwp: &gimli::DwarfPackage<R>,
|
|
dwo_parent: &gimli::Dwarf<R>,
|
|
dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
|
|
flags: &Flags,
|
|
sections: gimli::UnitIndexSectionIterator<R>,
|
|
) -> Result<()>
|
|
where
|
|
R::Endian: Send + Sync,
|
|
{
|
|
for section in sections.clone() {
|
|
writeln!(
|
|
w,
|
|
" {}: offset = 0x{:x}, size = 0x{:x}",
|
|
section.section.dwo_name().unwrap(),
|
|
section.offset,
|
|
section.size
|
|
)?;
|
|
}
|
|
let dwarf = dwp.sections(sections, dwo_parent)?;
|
|
if flags.info {
|
|
dump_info(w, &dwarf, dwo_parent_units, flags)?;
|
|
dump_types(w, &dwarf, dwo_parent_units, flags)?;
|
|
}
|
|
if flags.line {
|
|
dump_line(w, &dwarf)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_info<R: Reader, W: Write + Send>(
|
|
w: &mut W,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
|
|
flags: &Flags,
|
|
) -> Result<()>
|
|
where
|
|
R::Endian: Send + Sync,
|
|
{
|
|
writeln!(w, "\n.debug_info")?;
|
|
|
|
let units = match dwarf.units().collect::<Vec<_>>() {
|
|
Ok(units) => units,
|
|
Err(err) => {
|
|
writeln_error(
|
|
w,
|
|
dwarf,
|
|
Error::GimliError(err),
|
|
"Failed to read unit headers",
|
|
)?;
|
|
return Ok(());
|
|
}
|
|
};
|
|
let process_unit = |header: UnitHeader<R>, buf: &mut Vec<u8>| -> Result<()> {
|
|
dump_unit(buf, header, dwarf, dwo_parent_units, flags)?;
|
|
if !flags
|
|
.match_units
|
|
.as_ref()
|
|
.map(|r| r.is_match(&buf))
|
|
.unwrap_or(true)
|
|
{
|
|
buf.clear();
|
|
}
|
|
Ok(())
|
|
};
|
|
// Don't use more than 16 cores even if available. No point in soaking hundreds
|
|
// of cores if you happen to have them.
|
|
parallel_output(w, 16, units, process_unit)
|
|
}
|
|
|
|
fn dump_types<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
|
|
flags: &Flags,
|
|
) -> Result<()> {
|
|
writeln!(w, "\n.debug_types")?;
|
|
|
|
let mut iter = dwarf.type_units();
|
|
while let Some(header) = iter.next()? {
|
|
dump_unit(w, header, dwarf, dwo_parent_units, flags)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_unit<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
header: UnitHeader<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
|
|
flags: &Flags,
|
|
) -> Result<()> {
|
|
write!(w, "\nUNIT<")?;
|
|
match header.offset() {
|
|
UnitSectionOffset::DebugInfoOffset(o) => {
|
|
write!(w, ".debug_info+0x{:08x}", o.0)?;
|
|
}
|
|
UnitSectionOffset::DebugTypesOffset(o) => {
|
|
write!(w, ".debug_types+0x{:08x}", o.0)?;
|
|
}
|
|
}
|
|
writeln!(w, ">: length = 0x{:x}, format = {:?}, version = {}, address_size = {}, abbrev_offset = 0x{:x}",
|
|
header.unit_length(),
|
|
header.format(),
|
|
header.version(),
|
|
header.address_size(),
|
|
header.debug_abbrev_offset().0,
|
|
)?;
|
|
|
|
match header.type_() {
|
|
UnitType::Compilation | UnitType::Partial => (),
|
|
UnitType::Type {
|
|
type_signature,
|
|
type_offset,
|
|
}
|
|
| UnitType::SplitType {
|
|
type_signature,
|
|
type_offset,
|
|
} => {
|
|
write!(w, " signature = ")?;
|
|
dump_type_signature(w, type_signature)?;
|
|
writeln!(w)?;
|
|
writeln!(w, " type_offset = 0x{:x}", type_offset.0,)?;
|
|
}
|
|
UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => {
|
|
write!(w, " dwo_id = ")?;
|
|
writeln!(w, "0x{:016x}", dwo_id.0)?;
|
|
}
|
|
}
|
|
|
|
let mut unit = match dwarf.unit(header) {
|
|
Ok(unit) => unit,
|
|
Err(err) => {
|
|
writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?;
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
if let Some(dwo_parent_units) = dwo_parent_units {
|
|
if let Some(dwo_id) = unit.dwo_id {
|
|
if let Some(parent_unit) = dwo_parent_units.get(&dwo_id) {
|
|
unit.copy_relocated_attributes(parent_unit);
|
|
}
|
|
}
|
|
}
|
|
|
|
let entries_result = dump_entries(w, unit, dwarf, flags);
|
|
if let Err(err) = entries_result {
|
|
writeln_error(w, dwarf, err, "Failed to dump entries")?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn spaces(buf: &mut String, len: usize) -> &str {
|
|
while buf.len() < len {
|
|
buf.push(' ');
|
|
}
|
|
&buf[..len]
|
|
}
|
|
|
|
// " GOFF=0x{:08x}" adds exactly 16 spaces.
|
|
const GOFF_SPACES: usize = 16;
|
|
|
|
fn write_offset<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
unit: &gimli::Unit<R>,
|
|
offset: gimli::UnitOffset<R::Offset>,
|
|
flags: &Flags,
|
|
) -> Result<()> {
|
|
write!(w, "<0x{:08x}", offset.0)?;
|
|
if flags.goff {
|
|
let goff = match offset.to_unit_section_offset(unit) {
|
|
UnitSectionOffset::DebugInfoOffset(o) => o.0,
|
|
UnitSectionOffset::DebugTypesOffset(o) => o.0,
|
|
};
|
|
write!(w, " GOFF=0x{:08x}", goff)?;
|
|
}
|
|
write!(w, ">")?;
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_entries<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
unit: gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
flags: &Flags,
|
|
) -> Result<()> {
|
|
let mut spaces_buf = String::new();
|
|
|
|
let mut entries = unit.entries_raw(None)?;
|
|
while !entries.is_empty() {
|
|
let offset = entries.next_offset();
|
|
let depth = entries.next_depth();
|
|
let abbrev = entries.read_abbreviation()?;
|
|
|
|
let mut indent = if depth >= 0 {
|
|
depth as usize * 2 + 2
|
|
} else {
|
|
2
|
|
};
|
|
write!(w, "<{}{}>", if depth < 10 { " " } else { "" }, depth)?;
|
|
write_offset(w, &unit, offset, flags)?;
|
|
writeln!(
|
|
w,
|
|
"{}{}",
|
|
spaces(&mut spaces_buf, indent),
|
|
abbrev.map(|x| x.tag()).unwrap_or(gimli::DW_TAG_null)
|
|
)?;
|
|
|
|
indent += 18;
|
|
if flags.goff {
|
|
indent += GOFF_SPACES;
|
|
}
|
|
|
|
for spec in abbrev.map(|x| x.attributes()).unwrap_or(&[]) {
|
|
let attr = entries.read_attribute(*spec)?;
|
|
w.write_all(spaces(&mut spaces_buf, indent).as_bytes())?;
|
|
if let Some(n) = attr.name().static_string() {
|
|
let right_padding = 27 - std::cmp::min(27, n.len());
|
|
write!(w, "{}{} ", n, spaces(&mut spaces_buf, right_padding))?;
|
|
} else {
|
|
write!(w, "{:27} ", attr.name())?;
|
|
}
|
|
if flags.raw {
|
|
writeln!(w, "{:?}", attr.raw_value())?;
|
|
} else {
|
|
match dump_attr_value(w, &attr, &unit, dwarf) {
|
|
Ok(_) => (),
|
|
Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_attr_value<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
attr: &gimli::Attribute<R>,
|
|
unit: &gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
) -> Result<()> {
|
|
let value = attr.value();
|
|
match value {
|
|
gimli::AttributeValue::Addr(address) => {
|
|
writeln!(w, "0x{:08x}", address)?;
|
|
}
|
|
gimli::AttributeValue::Block(data) => {
|
|
for byte in data.to_slice()?.iter() {
|
|
write!(w, "{:02x}", byte)?;
|
|
}
|
|
writeln!(w)?;
|
|
}
|
|
gimli::AttributeValue::Data1(_)
|
|
| gimli::AttributeValue::Data2(_)
|
|
| gimli::AttributeValue::Data4(_)
|
|
| gimli::AttributeValue::Data8(_) => {
|
|
if let (Some(udata), Some(sdata)) = (attr.udata_value(), attr.sdata_value()) {
|
|
if sdata >= 0 {
|
|
writeln!(w, "{}", udata)?;
|
|
} else {
|
|
writeln!(w, "{} ({})", udata, sdata)?;
|
|
}
|
|
} else {
|
|
writeln!(w, "{:?}", value)?;
|
|
}
|
|
}
|
|
gimli::AttributeValue::Sdata(data) => {
|
|
match attr.name() {
|
|
gimli::DW_AT_data_member_location => {
|
|
writeln!(w, "{}", data)?;
|
|
}
|
|
_ => {
|
|
if data >= 0 {
|
|
writeln!(w, "0x{:08x}", data)?;
|
|
} else {
|
|
writeln!(w, "0x{:08x} ({})", data, data)?;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
gimli::AttributeValue::Udata(data) => {
|
|
match attr.name() {
|
|
gimli::DW_AT_high_pc => {
|
|
writeln!(w, "<offset-from-lowpc>{}", data)?;
|
|
}
|
|
gimli::DW_AT_data_member_location => {
|
|
if let Some(sdata) = attr.sdata_value() {
|
|
// This is a DW_FORM_data* value.
|
|
// libdwarf-dwarfdump displays this as signed too.
|
|
if sdata >= 0 {
|
|
writeln!(w, "{}", data)?;
|
|
} else {
|
|
writeln!(w, "{} ({})", data, sdata)?;
|
|
}
|
|
} else {
|
|
writeln!(w, "{}", data)?;
|
|
}
|
|
}
|
|
gimli::DW_AT_lower_bound | gimli::DW_AT_upper_bound => {
|
|
writeln!(w, "{}", data)?;
|
|
}
|
|
_ => {
|
|
writeln!(w, "0x{:08x}", data)?;
|
|
}
|
|
};
|
|
}
|
|
gimli::AttributeValue::Exprloc(ref data) => {
|
|
if let gimli::AttributeValue::Exprloc(_) = attr.raw_value() {
|
|
write!(w, "len 0x{:04x}: ", data.0.len())?;
|
|
for byte in data.0.to_slice()?.iter() {
|
|
write!(w, "{:02x}", byte)?;
|
|
}
|
|
write!(w, ": ")?;
|
|
}
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::AttributeValue::Flag(true) => {
|
|
writeln!(w, "yes")?;
|
|
}
|
|
gimli::AttributeValue::Flag(false) => {
|
|
writeln!(w, "no")?;
|
|
}
|
|
gimli::AttributeValue::SecOffset(offset) => {
|
|
writeln!(w, "0x{:08x}", offset)?;
|
|
}
|
|
gimli::AttributeValue::DebugAddrBase(base) => {
|
|
writeln!(w, "<.debug_addr+0x{:08x}>", base.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugAddrIndex(index) => {
|
|
write!(w, "(indirect address, index {:#x}): ", index.0)?;
|
|
let address = dwarf.address(unit, index)?;
|
|
writeln!(w, "0x{:08x}", address)?;
|
|
}
|
|
gimli::AttributeValue::UnitRef(offset) => {
|
|
write!(w, "0x{:08x}", offset.0)?;
|
|
match offset.to_unit_section_offset(unit) {
|
|
UnitSectionOffset::DebugInfoOffset(goff) => {
|
|
write!(w, "<.debug_info+0x{:08x}>", goff.0)?;
|
|
}
|
|
UnitSectionOffset::DebugTypesOffset(goff) => {
|
|
write!(w, "<.debug_types+0x{:08x}>", goff.0)?;
|
|
}
|
|
}
|
|
writeln!(w)?;
|
|
}
|
|
gimli::AttributeValue::DebugInfoRef(offset) => {
|
|
writeln!(w, "<.debug_info+0x{:08x}>", offset.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugInfoRefSup(offset) => {
|
|
writeln!(w, "<.debug_info(sup)+0x{:08x}>", offset.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugLineRef(offset) => {
|
|
writeln!(w, "<.debug_line+0x{:08x}>", offset.0)?;
|
|
}
|
|
gimli::AttributeValue::LocationListsRef(offset) => {
|
|
dump_loc_list(w, offset, unit, dwarf)?;
|
|
}
|
|
gimli::AttributeValue::DebugLocListsBase(base) => {
|
|
writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugLocListsIndex(index) => {
|
|
write!(w, "(indirect location list, index {:#x}): ", index.0)?;
|
|
let offset = dwarf.locations_offset(unit, index)?;
|
|
dump_loc_list(w, offset, unit, dwarf)?;
|
|
}
|
|
gimli::AttributeValue::DebugMacinfoRef(offset) => {
|
|
writeln!(w, "<.debug_macinfo+0x{:08x}>", offset.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugMacroRef(offset) => {
|
|
writeln!(w, "<.debug_macro+0x{:08x}>", offset.0)?;
|
|
}
|
|
gimli::AttributeValue::RangeListsRef(offset) => {
|
|
let offset = dwarf.ranges_offset_from_raw(unit, offset);
|
|
dump_range_list(w, offset, unit, dwarf)?;
|
|
}
|
|
gimli::AttributeValue::DebugRngListsBase(base) => {
|
|
writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugRngListsIndex(index) => {
|
|
write!(w, "(indirect range list, index {:#x}): ", index.0)?;
|
|
let offset = dwarf.ranges_offset(unit, index)?;
|
|
dump_range_list(w, offset, unit, dwarf)?;
|
|
}
|
|
gimli::AttributeValue::DebugTypesRef(signature) => {
|
|
dump_type_signature(w, signature)?;
|
|
writeln!(w, " <type signature>")?;
|
|
}
|
|
gimli::AttributeValue::DebugStrRef(offset) => {
|
|
if let Ok(s) = dwarf.debug_str.get_str(offset) {
|
|
writeln!(w, "{}", s.to_string_lossy()?)?;
|
|
} else {
|
|
writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
|
|
}
|
|
}
|
|
gimli::AttributeValue::DebugStrRefSup(offset) => {
|
|
if let Some(s) = dwarf
|
|
.sup()
|
|
.and_then(|sup| sup.debug_str.get_str(offset).ok())
|
|
{
|
|
writeln!(w, "{}", s.to_string_lossy()?)?;
|
|
} else {
|
|
writeln!(w, "<.debug_str(sup)+0x{:08x}>", offset.0)?;
|
|
}
|
|
}
|
|
gimli::AttributeValue::DebugStrOffsetsBase(base) => {
|
|
writeln!(w, "<.debug_str_offsets+0x{:08x}>", base.0)?;
|
|
}
|
|
gimli::AttributeValue::DebugStrOffsetsIndex(index) => {
|
|
write!(w, "(indirect string, index {:#x}): ", index.0)?;
|
|
let offset = dwarf.debug_str_offsets.get_str_offset(
|
|
unit.encoding().format,
|
|
unit.str_offsets_base,
|
|
index,
|
|
)?;
|
|
if let Ok(s) = dwarf.debug_str.get_str(offset) {
|
|
writeln!(w, "{}", s.to_string_lossy()?)?;
|
|
} else {
|
|
writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
|
|
}
|
|
}
|
|
gimli::AttributeValue::DebugLineStrRef(offset) => {
|
|
if let Ok(s) = dwarf.debug_line_str.get_str(offset) {
|
|
writeln!(w, "{}", s.to_string_lossy()?)?;
|
|
} else {
|
|
writeln!(w, "<.debug_line_str=0x{:08x}>", offset.0)?;
|
|
}
|
|
}
|
|
gimli::AttributeValue::String(s) => {
|
|
writeln!(w, "{}", s.to_string_lossy()?)?;
|
|
}
|
|
gimli::AttributeValue::Encoding(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::DecimalSign(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Endianity(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Accessibility(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Visibility(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Virtuality(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Language(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::AddressClass(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::IdentifierCase(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::CallingConvention(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Inline(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::Ordering(value) => {
|
|
writeln!(w, "{}", value)?;
|
|
}
|
|
gimli::AttributeValue::FileIndex(value) => {
|
|
write!(w, "0x{:08x}", value)?;
|
|
dump_file_index(w, value, unit, dwarf)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::AttributeValue::DwoId(value) => {
|
|
writeln!(w, "0x{:016x}", value.0)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_type_signature<W: Write>(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()> {
|
|
write!(w, "0x{:016x}", signature.0)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_file_index<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
file: u64,
|
|
unit: &gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
) -> Result<()> {
|
|
if file == 0 {
|
|
return Ok(());
|
|
}
|
|
let header = match unit.line_program {
|
|
Some(ref program) => program.header(),
|
|
None => return Ok(()),
|
|
};
|
|
let file = match header.file(file) {
|
|
Some(header) => header,
|
|
None => {
|
|
writeln!(w, "Unable to get header for file {}", file)?;
|
|
return Ok(());
|
|
}
|
|
};
|
|
write!(w, " ")?;
|
|
if let Some(directory) = file.directory(header) {
|
|
let directory = dwarf.attr_string(unit, directory)?;
|
|
let directory = directory.to_string_lossy()?;
|
|
if !directory.starts_with('/') {
|
|
if let Some(ref comp_dir) = unit.comp_dir {
|
|
write!(w, "{}/", comp_dir.to_string_lossy()?,)?;
|
|
}
|
|
}
|
|
write!(w, "{}/", directory)?;
|
|
}
|
|
write!(
|
|
w,
|
|
"{}",
|
|
dwarf
|
|
.attr_string(unit, file.path_name())?
|
|
.to_string_lossy()?
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_exprloc<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
encoding: gimli::Encoding,
|
|
data: &gimli::Expression<R>,
|
|
) -> Result<()> {
|
|
let mut pc = data.0.clone();
|
|
let mut space = false;
|
|
while pc.len() != 0 {
|
|
let pc_clone = pc.clone();
|
|
match gimli::Operation::parse(&mut pc, encoding) {
|
|
Ok(op) => {
|
|
if space {
|
|
write!(w, " ")?;
|
|
} else {
|
|
space = true;
|
|
}
|
|
dump_op(w, encoding, pc_clone, op)?;
|
|
}
|
|
Err(gimli::Error::InvalidExpression(op)) => {
|
|
writeln!(w, "WARNING: unsupported operation 0x{:02x}", op.0)?;
|
|
return Ok(());
|
|
}
|
|
Err(gimli::Error::UnsupportedRegister(register)) => {
|
|
writeln!(w, "WARNING: unsupported register {}", register)?;
|
|
return Ok(());
|
|
}
|
|
Err(gimli::Error::UnexpectedEof(_)) => {
|
|
writeln!(w, "WARNING: truncated or malformed expression")?;
|
|
return Ok(());
|
|
}
|
|
Err(e) => {
|
|
writeln!(w, "WARNING: unexpected operation parse error: {}", e)?;
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_op<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
encoding: gimli::Encoding,
|
|
mut pc: R,
|
|
op: gimli::Operation<R>,
|
|
) -> Result<()> {
|
|
let dwop = gimli::DwOp(pc.read_u8()?);
|
|
write!(w, "{}", dwop)?;
|
|
match op {
|
|
gimli::Operation::Deref {
|
|
base_type, size, ..
|
|
} => {
|
|
if dwop == gimli::DW_OP_deref_size || dwop == gimli::DW_OP_xderef_size {
|
|
write!(w, " {}", size)?;
|
|
}
|
|
if base_type != UnitOffset(0) {
|
|
write!(w, " type 0x{:08x}", base_type.0)?;
|
|
}
|
|
}
|
|
gimli::Operation::Pick { index } => {
|
|
if dwop == gimli::DW_OP_pick {
|
|
write!(w, " {}", index)?;
|
|
}
|
|
}
|
|
gimli::Operation::PlusConstant { value } => {
|
|
write!(w, " {}", value as i64)?;
|
|
}
|
|
gimli::Operation::Bra { target } => {
|
|
write!(w, " {}", target)?;
|
|
}
|
|
gimli::Operation::Skip { target } => {
|
|
write!(w, " {}", target)?;
|
|
}
|
|
gimli::Operation::SignedConstant { value } => match dwop {
|
|
gimli::DW_OP_const1s
|
|
| gimli::DW_OP_const2s
|
|
| gimli::DW_OP_const4s
|
|
| gimli::DW_OP_const8s
|
|
| gimli::DW_OP_consts => {
|
|
write!(w, " {}", value)?;
|
|
}
|
|
_ => {}
|
|
},
|
|
gimli::Operation::UnsignedConstant { value } => match dwop {
|
|
gimli::DW_OP_const1u
|
|
| gimli::DW_OP_const2u
|
|
| gimli::DW_OP_const4u
|
|
| gimli::DW_OP_const8u
|
|
| gimli::DW_OP_constu => {
|
|
write!(w, " {}", value)?;
|
|
}
|
|
_ => {
|
|
// These have the value encoded in the operation, eg DW_OP_lit0.
|
|
}
|
|
},
|
|
gimli::Operation::Register { register } => {
|
|
if dwop == gimli::DW_OP_regx {
|
|
write!(w, " {}", register.0)?;
|
|
}
|
|
}
|
|
gimli::Operation::RegisterOffset {
|
|
register,
|
|
offset,
|
|
base_type,
|
|
} => {
|
|
if dwop >= gimli::DW_OP_breg0 && dwop <= gimli::DW_OP_breg31 {
|
|
write!(w, "{:+}", offset)?;
|
|
} else {
|
|
write!(w, " {}", register.0)?;
|
|
if offset != 0 {
|
|
write!(w, "{:+}", offset)?;
|
|
}
|
|
if base_type != UnitOffset(0) {
|
|
write!(w, " type 0x{:08x}", base_type.0)?;
|
|
}
|
|
}
|
|
}
|
|
gimli::Operation::FrameOffset { offset } => {
|
|
write!(w, " {}", offset)?;
|
|
}
|
|
gimli::Operation::Call { offset } => match offset {
|
|
gimli::DieReference::UnitRef(gimli::UnitOffset(offset)) => {
|
|
write!(w, " 0x{:08x}", offset)?;
|
|
}
|
|
gimli::DieReference::DebugInfoRef(gimli::DebugInfoOffset(offset)) => {
|
|
write!(w, " 0x{:08x}", offset)?;
|
|
}
|
|
},
|
|
gimli::Operation::Piece {
|
|
size_in_bits,
|
|
bit_offset: None,
|
|
} => {
|
|
write!(w, " {}", size_in_bits / 8)?;
|
|
}
|
|
gimli::Operation::Piece {
|
|
size_in_bits,
|
|
bit_offset: Some(bit_offset),
|
|
} => {
|
|
write!(w, " 0x{:08x} offset 0x{:08x}", size_in_bits, bit_offset)?;
|
|
}
|
|
gimli::Operation::ImplicitValue { data } => {
|
|
let data = data.to_slice()?;
|
|
write!(w, " 0x{:08x} contents 0x", data.len())?;
|
|
for byte in data.iter() {
|
|
write!(w, "{:02x}", byte)?;
|
|
}
|
|
}
|
|
gimli::Operation::ImplicitPointer { value, byte_offset } => {
|
|
write!(w, " 0x{:08x} {}", value.0, byte_offset)?;
|
|
}
|
|
gimli::Operation::EntryValue { expression } => {
|
|
write!(w, "(")?;
|
|
dump_exprloc(w, encoding, &gimli::Expression(expression))?;
|
|
write!(w, ")")?;
|
|
}
|
|
gimli::Operation::ParameterRef { offset } => {
|
|
write!(w, " 0x{:08x}", offset.0)?;
|
|
}
|
|
gimli::Operation::Address { address } => {
|
|
write!(w, " 0x{:08x}", address)?;
|
|
}
|
|
gimli::Operation::AddressIndex { index } => {
|
|
write!(w, " 0x{:08x}", index.0)?;
|
|
}
|
|
gimli::Operation::ConstantIndex { index } => {
|
|
write!(w, " 0x{:08x}", index.0)?;
|
|
}
|
|
gimli::Operation::TypedLiteral { base_type, value } => {
|
|
write!(w, " type 0x{:08x} contents 0x", base_type.0)?;
|
|
for byte in value.to_slice()?.iter() {
|
|
write!(w, "{:02x}", byte)?;
|
|
}
|
|
}
|
|
gimli::Operation::Convert { base_type } => {
|
|
write!(w, " type 0x{:08x}", base_type.0)?;
|
|
}
|
|
gimli::Operation::Reinterpret { base_type } => {
|
|
write!(w, " type 0x{:08x}", base_type.0)?;
|
|
}
|
|
gimli::Operation::WasmLocal { index }
|
|
| gimli::Operation::WasmGlobal { index }
|
|
| gimli::Operation::WasmStack { index } => {
|
|
let wasmop = pc.read_u8()?;
|
|
write!(w, " 0x{:x} 0x{:x}", wasmop, index)?;
|
|
}
|
|
gimli::Operation::Drop
|
|
| gimli::Operation::Swap
|
|
| gimli::Operation::Rot
|
|
| gimli::Operation::Abs
|
|
| gimli::Operation::And
|
|
| gimli::Operation::Div
|
|
| gimli::Operation::Minus
|
|
| gimli::Operation::Mod
|
|
| gimli::Operation::Mul
|
|
| gimli::Operation::Neg
|
|
| gimli::Operation::Not
|
|
| gimli::Operation::Or
|
|
| gimli::Operation::Plus
|
|
| gimli::Operation::Shl
|
|
| gimli::Operation::Shr
|
|
| gimli::Operation::Shra
|
|
| gimli::Operation::Xor
|
|
| gimli::Operation::Eq
|
|
| gimli::Operation::Ge
|
|
| gimli::Operation::Gt
|
|
| gimli::Operation::Le
|
|
| gimli::Operation::Lt
|
|
| gimli::Operation::Ne
|
|
| gimli::Operation::Nop
|
|
| gimli::Operation::PushObjectAddress
|
|
| gimli::Operation::TLS
|
|
| gimli::Operation::CallFrameCFA
|
|
| gimli::Operation::StackValue => {}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_loc_list<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
offset: gimli::LocationListsOffset<R::Offset>,
|
|
unit: &gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
) -> Result<()> {
|
|
let raw_locations = dwarf.raw_locations(unit, offset)?;
|
|
let raw_locations: Vec<_> = raw_locations.collect()?;
|
|
let mut locations = dwarf.locations(unit, offset)?;
|
|
writeln!(
|
|
w,
|
|
"<loclist at {}+0x{:08x} with {} entries>",
|
|
if unit.encoding().version < 5 {
|
|
".debug_loc"
|
|
} else {
|
|
".debug_loclists"
|
|
},
|
|
offset.0,
|
|
raw_locations.len()
|
|
)?;
|
|
for (i, raw) in raw_locations.iter().enumerate() {
|
|
write!(w, "\t\t\t[{:2}]", i)?;
|
|
match *raw {
|
|
gimli::RawLocListEntry::BaseAddress { addr } => {
|
|
writeln!(w, "<new base address 0x{:08x}>", addr)?;
|
|
}
|
|
gimli::RawLocListEntry::BaseAddressx { addr } => {
|
|
let addr_val = dwarf.address(unit, addr)?;
|
|
writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
|
|
}
|
|
gimli::RawLocListEntry::StartxEndx {
|
|
begin,
|
|
end,
|
|
ref data,
|
|
} => {
|
|
let begin_val = dwarf.address(unit, begin)?;
|
|
let end_val = dwarf.address(unit, end)?;
|
|
let location = locations.next()?.unwrap();
|
|
write!(
|
|
w,
|
|
"<startx-endx \
|
|
low-off: [{}]0x{:08x} addr 0x{:08x} \
|
|
high-off: [{}]0x{:08x} addr 0x{:08x}>",
|
|
begin.0, begin_val, location.range.begin, end.0, end_val, location.range.end
|
|
)?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::RawLocListEntry::StartxLength {
|
|
begin,
|
|
length,
|
|
ref data,
|
|
} => {
|
|
let begin_val = dwarf.address(unit, begin)?;
|
|
let location = locations.next()?.unwrap();
|
|
write!(
|
|
w,
|
|
"<start-length \
|
|
low-off: [{}]0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin.0, begin_val, location.range.begin, length, location.range.end
|
|
)?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::RawLocListEntry::AddressOrOffsetPair {
|
|
begin,
|
|
end,
|
|
ref data,
|
|
}
|
|
| gimli::RawLocListEntry::OffsetPair {
|
|
begin,
|
|
end,
|
|
ref data,
|
|
} => {
|
|
let location = locations.next()?.unwrap();
|
|
write!(
|
|
w,
|
|
"<offset pair \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, location.range.begin, end, location.range.end
|
|
)?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::RawLocListEntry::DefaultLocation { ref data } => {
|
|
write!(w, "<default location>")?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::RawLocListEntry::StartEnd {
|
|
begin,
|
|
end,
|
|
ref data,
|
|
} => {
|
|
let location = locations.next()?.unwrap();
|
|
write!(
|
|
w,
|
|
"<start-end \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, location.range.begin, end, location.range.end
|
|
)?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
gimli::RawLocListEntry::StartLength {
|
|
begin,
|
|
length,
|
|
ref data,
|
|
} => {
|
|
let location = locations.next()?.unwrap();
|
|
write!(
|
|
w,
|
|
"<start-length \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, location.range.begin, length, location.range.end
|
|
)?;
|
|
dump_exprloc(w, unit.encoding(), data)?;
|
|
writeln!(w)?;
|
|
}
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_range_list<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
offset: gimli::RangeListsOffset<R::Offset>,
|
|
unit: &gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
) -> Result<()> {
|
|
let raw_ranges = dwarf.raw_ranges(unit, offset)?;
|
|
let raw_ranges: Vec<_> = raw_ranges.collect()?;
|
|
let mut ranges = dwarf.ranges(unit, offset)?;
|
|
writeln!(
|
|
w,
|
|
"<rnglist at {}+0x{:08x} with {} entries>",
|
|
if unit.encoding().version < 5 {
|
|
".debug_ranges"
|
|
} else {
|
|
".debug_rnglists"
|
|
},
|
|
offset.0,
|
|
raw_ranges.len()
|
|
)?;
|
|
for (i, raw) in raw_ranges.iter().enumerate() {
|
|
write!(w, "\t\t\t[{:2}] ", i)?;
|
|
match *raw {
|
|
gimli::RawRngListEntry::AddressOrOffsetPair { begin, end } => {
|
|
let range = ranges.next()?.unwrap();
|
|
writeln!(
|
|
w,
|
|
"<address pair \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, range.begin, end, range.end
|
|
)?;
|
|
}
|
|
gimli::RawRngListEntry::BaseAddress { addr } => {
|
|
writeln!(w, "<new base address 0x{:08x}>", addr)?;
|
|
}
|
|
gimli::RawRngListEntry::BaseAddressx { addr } => {
|
|
let addr_val = dwarf.address(unit, addr)?;
|
|
writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
|
|
}
|
|
gimli::RawRngListEntry::StartxEndx { begin, end } => {
|
|
let begin_val = dwarf.address(unit, begin)?;
|
|
let end_val = dwarf.address(unit, end)?;
|
|
let range = if begin_val == end_val {
|
|
gimli::Range {
|
|
begin: begin_val,
|
|
end: end_val,
|
|
}
|
|
} else {
|
|
ranges.next()?.unwrap()
|
|
};
|
|
writeln!(
|
|
w,
|
|
"<startx-endx \
|
|
low-off: [{}]0x{:08x} addr 0x{:08x} \
|
|
high-off: [{}]0x{:08x} addr 0x{:08x}>",
|
|
begin.0, begin_val, range.begin, end.0, end_val, range.end
|
|
)?;
|
|
}
|
|
gimli::RawRngListEntry::StartxLength { begin, length } => {
|
|
let begin_val = dwarf.address(unit, begin)?;
|
|
let range = ranges.next()?.unwrap();
|
|
writeln!(
|
|
w,
|
|
"<startx-length \
|
|
low-off: [{}]0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin.0, begin_val, range.begin, length, range.end
|
|
)?;
|
|
}
|
|
gimli::RawRngListEntry::OffsetPair { begin, end } => {
|
|
let range = ranges.next()?.unwrap();
|
|
writeln!(
|
|
w,
|
|
"<offset pair \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, range.begin, end, range.end
|
|
)?;
|
|
}
|
|
gimli::RawRngListEntry::StartEnd { begin, end } => {
|
|
let range = if begin == end {
|
|
gimli::Range { begin, end }
|
|
} else {
|
|
ranges.next()?.unwrap()
|
|
};
|
|
writeln!(
|
|
w,
|
|
"<start-end \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, range.begin, end, range.end
|
|
)?;
|
|
}
|
|
gimli::RawRngListEntry::StartLength { begin, length } => {
|
|
let range = ranges.next()?.unwrap();
|
|
writeln!(
|
|
w,
|
|
"<start-length \
|
|
low-off: 0x{:08x} addr 0x{:08x} \
|
|
high-off: 0x{:08x} addr 0x{:08x}>",
|
|
begin, range.begin, length, range.end
|
|
)?;
|
|
}
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_line<R: Reader, W: Write>(w: &mut W, dwarf: &gimli::Dwarf<R>) -> Result<()> {
|
|
let mut iter = dwarf.units();
|
|
while let Some(header) = iter.next()? {
|
|
writeln!(
|
|
w,
|
|
"\n.debug_line: line number info for unit at .debug_info offset 0x{:08x}",
|
|
header.offset().as_debug_info_offset().unwrap().0
|
|
)?;
|
|
let unit = match dwarf.unit(header) {
|
|
Ok(unit) => unit,
|
|
Err(err) => {
|
|
writeln_error(
|
|
w,
|
|
dwarf,
|
|
err.into(),
|
|
"Failed to parse unit root entry for dump_line",
|
|
)?;
|
|
continue;
|
|
}
|
|
};
|
|
match dump_line_program(w, &unit, dwarf) {
|
|
Ok(_) => (),
|
|
Err(Error::IoError) => return Err(Error::IoError),
|
|
Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?,
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_line_program<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
unit: &gimli::Unit<R>,
|
|
dwarf: &gimli::Dwarf<R>,
|
|
) -> Result<()> {
|
|
if let Some(program) = unit.line_program.clone() {
|
|
{
|
|
let header = program.header();
|
|
writeln!(w)?;
|
|
writeln!(
|
|
w,
|
|
"Offset: 0x{:x}",
|
|
header.offset().0
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Length: {}",
|
|
header.unit_length()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"DWARF version: {}",
|
|
header.version()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Address size: {}",
|
|
header.address_size()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Prologue length: {}",
|
|
header.header_length()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Minimum instruction length: {}",
|
|
header.minimum_instruction_length()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Maximum operations per instruction: {}",
|
|
header.maximum_operations_per_instruction()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Default is_stmt: {}",
|
|
header.default_is_stmt()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Line base: {}",
|
|
header.line_base()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Line range: {}",
|
|
header.line_range()
|
|
)?;
|
|
writeln!(
|
|
w,
|
|
"Opcode base: {}",
|
|
header.opcode_base()
|
|
)?;
|
|
|
|
writeln!(w)?;
|
|
writeln!(w, "Opcodes:")?;
|
|
for (i, length) in header
|
|
.standard_opcode_lengths()
|
|
.to_slice()?
|
|
.iter()
|
|
.enumerate()
|
|
{
|
|
writeln!(w, " Opcode {} has {} args", i + 1, length)?;
|
|
}
|
|
|
|
let base = if header.version() >= 5 { 0 } else { 1 };
|
|
writeln!(w)?;
|
|
writeln!(w, "The Directory Table:")?;
|
|
for (i, dir) in header.include_directories().iter().enumerate() {
|
|
writeln!(
|
|
w,
|
|
" {} {}",
|
|
base + i,
|
|
dwarf.attr_string(unit, dir.clone())?.to_string_lossy()?
|
|
)?;
|
|
}
|
|
|
|
writeln!(w)?;
|
|
writeln!(w, "The File Name Table")?;
|
|
write!(w, " Entry\tDir\tTime\tSize")?;
|
|
if header.file_has_md5() {
|
|
write!(w, "\tMD5\t\t\t\t")?;
|
|
}
|
|
writeln!(w, "\tName")?;
|
|
for (i, file) in header.file_names().iter().enumerate() {
|
|
write!(
|
|
w,
|
|
" {}\t{}\t{}\t{}",
|
|
base + i,
|
|
file.directory_index(),
|
|
file.timestamp(),
|
|
file.size(),
|
|
)?;
|
|
if header.file_has_md5() {
|
|
let md5 = file.md5();
|
|
write!(w, "\t")?;
|
|
for i in 0..16 {
|
|
write!(w, "{:02X}", md5[i])?;
|
|
}
|
|
}
|
|
writeln!(
|
|
w,
|
|
"\t{}",
|
|
dwarf
|
|
.attr_string(unit, file.path_name())?
|
|
.to_string_lossy()?
|
|
)?;
|
|
}
|
|
|
|
writeln!(w)?;
|
|
writeln!(w, "Line Number Instructions:")?;
|
|
let mut instructions = header.instructions();
|
|
while let Some(instruction) = instructions.next_instruction(header)? {
|
|
writeln!(w, " {}", instruction)?;
|
|
}
|
|
|
|
writeln!(w)?;
|
|
writeln!(w, "Line Number Rows:")?;
|
|
writeln!(w, "<pc> [lno,col]")?;
|
|
}
|
|
let mut rows = program.rows();
|
|
let mut file_index = 0;
|
|
while let Some((header, row)) = rows.next_row()? {
|
|
let line = match row.line() {
|
|
Some(line) => line.get(),
|
|
None => 0,
|
|
};
|
|
let column = match row.column() {
|
|
gimli::ColumnType::Column(column) => column.get(),
|
|
gimli::ColumnType::LeftEdge => 0,
|
|
};
|
|
write!(w, "0x{:08x} [{:4},{:2}]", row.address(), line, column)?;
|
|
if row.is_stmt() {
|
|
write!(w, " NS")?;
|
|
}
|
|
if row.basic_block() {
|
|
write!(w, " BB")?;
|
|
}
|
|
if row.end_sequence() {
|
|
write!(w, " ET")?;
|
|
}
|
|
if row.prologue_end() {
|
|
write!(w, " PE")?;
|
|
}
|
|
if row.epilogue_begin() {
|
|
write!(w, " EB")?;
|
|
}
|
|
if row.isa() != 0 {
|
|
write!(w, " IS={}", row.isa())?;
|
|
}
|
|
if row.discriminator() != 0 {
|
|
write!(w, " DI={}", row.discriminator())?;
|
|
}
|
|
if file_index != row.file_index() {
|
|
file_index = row.file_index();
|
|
if let Some(file) = row.file(header) {
|
|
if let Some(directory) = file.directory(header) {
|
|
write!(
|
|
w,
|
|
" uri: \"{}/{}\"",
|
|
dwarf.attr_string(unit, directory)?.to_string_lossy()?,
|
|
dwarf
|
|
.attr_string(unit, file.path_name())?
|
|
.to_string_lossy()?
|
|
)?;
|
|
} else {
|
|
write!(
|
|
w,
|
|
" uri: \"{}\"",
|
|
dwarf
|
|
.attr_string(unit, file.path_name())?
|
|
.to_string_lossy()?
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
writeln!(w)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_pubnames<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
debug_pubnames: &gimli::DebugPubNames<R>,
|
|
debug_info: &gimli::DebugInfo<R>,
|
|
) -> Result<()> {
|
|
writeln!(w, "\n.debug_pubnames")?;
|
|
|
|
let mut cu_offset;
|
|
let mut cu_die_offset = gimli::DebugInfoOffset(0);
|
|
let mut prev_cu_offset = None;
|
|
let mut pubnames = debug_pubnames.items();
|
|
while let Some(pubname) = pubnames.next()? {
|
|
cu_offset = pubname.unit_header_offset();
|
|
if Some(cu_offset) != prev_cu_offset {
|
|
let cu = debug_info.header_from_offset(cu_offset)?;
|
|
cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
|
|
prev_cu_offset = Some(cu_offset);
|
|
}
|
|
let die_in_cu = pubname.die_offset();
|
|
let die_in_sect = cu_offset.0 + die_in_cu.0;
|
|
writeln!(w,
|
|
"global die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
|
|
die_in_sect,
|
|
cu_die_offset.0,
|
|
die_in_cu.0,
|
|
cu_offset.0,
|
|
pubname.name().to_string_lossy()?
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_pubtypes<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
debug_pubtypes: &gimli::DebugPubTypes<R>,
|
|
debug_info: &gimli::DebugInfo<R>,
|
|
) -> Result<()> {
|
|
writeln!(w, "\n.debug_pubtypes")?;
|
|
|
|
let mut cu_offset;
|
|
let mut cu_die_offset = gimli::DebugInfoOffset(0);
|
|
let mut prev_cu_offset = None;
|
|
let mut pubtypes = debug_pubtypes.items();
|
|
while let Some(pubtype) = pubtypes.next()? {
|
|
cu_offset = pubtype.unit_header_offset();
|
|
if Some(cu_offset) != prev_cu_offset {
|
|
let cu = debug_info.header_from_offset(cu_offset)?;
|
|
cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
|
|
prev_cu_offset = Some(cu_offset);
|
|
}
|
|
let die_in_cu = pubtype.die_offset();
|
|
let die_in_sect = cu_offset.0 + die_in_cu.0;
|
|
writeln!(w,
|
|
"pubtype die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
|
|
die_in_sect,
|
|
cu_die_offset.0,
|
|
die_in_cu.0,
|
|
cu_offset.0,
|
|
pubtype.name().to_string_lossy()?
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_aranges<R: Reader, W: Write>(
|
|
w: &mut W,
|
|
debug_aranges: &gimli::DebugAranges<R>,
|
|
) -> Result<()> {
|
|
writeln!(w, "\n.debug_aranges")?;
|
|
|
|
let mut headers = debug_aranges.headers();
|
|
while let Some(header) = headers.next()? {
|
|
writeln!(
|
|
w,
|
|
"Address Range Header: length = 0x{:08x}, version = 0x{:04x}, cu_offset = 0x{:08x}, addr_size = 0x{:02x}, seg_size = 0x{:02x}",
|
|
header.length(),
|
|
header.encoding().version,
|
|
header.debug_info_offset().0,
|
|
header.encoding().address_size,
|
|
header.segment_size(),
|
|
)?;
|
|
let mut aranges = header.entries();
|
|
while let Some(arange) = aranges.next()? {
|
|
let range = arange.range();
|
|
if let Some(segment) = arange.segment() {
|
|
writeln!(
|
|
w,
|
|
"[0x{:016x}, 0x{:016x}) segment 0x{:x}",
|
|
range.begin, range.end, segment
|
|
)?;
|
|
} else {
|
|
writeln!(w, "[0x{:016x}, 0x{:016x})", range.begin, range.end)?;
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|