Skip to content

Commit

Permalink
dwarfdump: handle relocations
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc committed Oct 25, 2018
1 parent 6971af9 commit 96c8487
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ crossbeam = "0.4.1"
getopts = "0.2"
memmap = "0.7"
num_cpus = "1"
object = "0.10"
object = "0.11"
rayon = "1.0"
regex = "1"
test-assembler = "0.1.3"
Expand Down
146 changes: 138 additions & 8 deletions examples/dwarfdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern crate typed_arena;

use fallible_iterator::FallibleIterator;
use gimli::{CompilationUnitHeader, UnitOffset, UnwindSection};
use object::Object;
use object::{Object, ObjectSection};
use regex::bytes::Regex;
use std::borrow::{Borrow, Cow};
use std::cmp::min;
Expand Down Expand Up @@ -159,6 +159,122 @@ where
type SyncSendEndian = Endian;
}

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::Direct32 | object::RelocationKind::Direct64 => {
if let Some(symbol) = file.symbol_by_index(relocation.symbol()) {
let addend = symbol.address().wrapping_add(relocation.addend() as u64);
relocation.set_addend(addend as i64);
if relocations.insert(offset, relocation).is_some() {
println!(
"Multiple relocations for section {} at offset 0x{:08x}",
section.name().unwrap(),
offset
);
}
} else {
println!(
"Relocation with invalid symbol for section {} at offset 0x{:08x}",
section.name().unwrap(),
offset
);
}
}
_ => {
println!(
"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 RelocationHook<'a, R: gimli::Reader<Offset = usize>> {
relocations: &'a RelocationMap,
section: R,
reader: R,
}

impl<'a, R: gimli::Reader<Offset = usize>> RelocationHook<'a, R> {
fn relocate(&self, offset: usize, value: u64) -> u64 {
if let Some(relocation) = self.relocations.get(&offset) {
match relocation.kind() {
object::RelocationKind::Direct32 | object::RelocationKind::Direct64 => {
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::ReaderHook for RelocationHook<'a, R> {
type R = R;

#[inline]
fn reader(&self) -> &Self::R {
&self.reader
}

#[inline]
fn reader_mut(&mut self) -> &mut Self::R {
&mut self.reader
}

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_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))
}
}

impl<'a, R: Reader> Reader for gimli::HookedReader<RelocationHook<'a, R>> {
type SyncSendEndian = R::SyncSendEndian;
}

#[derive(Default)]
struct Flags {
eh_frame: bool,
Expand Down Expand Up @@ -314,22 +430,36 @@ fn dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Resu
where
Endian: gimli::Endianity + Send + Sync,
{
let arena = Arena::new();
let arena = (Arena::new(), Arena::new());

fn load_section<'a, 'file, 'input, S, Endian>(
arena: &'a Arena<Cow<'file, [u8]>>,
arena: &'a (Arena<Cow<'file, [u8]>>, Arena<RelocationMap>),
file: &'file object::File<'input>,
endian: Endian,
) -> S
where
S: gimli::Section<gimli::EndianSlice<'a, Endian>>,
S: gimli::Section<gimli::HookedReader<RelocationHook<'a, gimli::EndianSlice<'a, Endian>>>>,
Endian: gimli::Endianity + Send + Sync,
'file: 'input,
'a: 'file
'a: 'file,
{
let data = file.section_data_by_name(S::section_name()).unwrap_or(Cow::Borrowed(&[]));
let data_ref = (*arena.alloc(data)).borrow();
S::from(gimli::EndianSlice::new(data_ref, endian))
let mut relocations = RelocationMap::default();
let data = match file.section_by_name(S::section_name()) {
Some(ref section) => {
add_relocations(&mut relocations, file, section);
section.uncompressed_data()
}
None => Cow::Borrowed(&[][..]),
};
let data_ref = (*arena.0.alloc(data)).borrow();
let reader = gimli::EndianSlice::new(data_ref, endian);
let section = reader.clone();
let relocations = (*arena.1.alloc(relocations)).borrow();
S::from(gimli::HookedReader::new(RelocationHook {
relocations,
section,
reader,
}))
}

// Variables representing sections of the file. The type of each is inferred from its use in the
Expand Down

0 comments on commit 96c8487

Please sign in to comment.