Skip to content

Commit

Permalink
Merge pull request #709 from philipc/reloc
Browse files Browse the repository at this point in the history
Improve relocation support
  • Loading branch information
philipc committed Apr 11, 2024
2 parents be18b8b + d8086a7 commit b1b9b30
Show file tree
Hide file tree
Showing 12 changed files with 905 additions and 261 deletions.
11 changes: 8 additions & 3 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ gimli = { path = "../..", default-features = false }
crossbeam = "0.8"
fallible-iterator = { version = "0.3.0", default-features = false, optional = true }
getopts = "0.2"
memmap2 = "0.7.1"
memmap2 = "0.9.4"
num_cpus = "1"
object = { version = "0.32.0", features = ["wasm"] }
object = { version = "0.35.0", features = ["wasm", "write"] }
rayon = "1.0"
regex = "1"
typed-arena = "2"
Expand All @@ -19,8 +19,9 @@ typed-arena = "2"
read = ["gimli/read"]
read-all = ["read", "std", "fallible-iterator"]
fallible-iterator = ["dep:fallible-iterator", "gimli/fallible-iterator"]
write = ["gimli/write"]
std = ["gimli/std"]
default = ["read", "std", "fallible-iterator"]
default = ["read-all", "write"]

[[bin]]
name = "simple"
Expand All @@ -30,6 +31,10 @@ required-features = ["read", "std"]
name = "simple_line"
required-features = ["read", "std"]

[[bin]]
name = "simple_write"
required-features = ["write"]

[[bin]]
name = "dwarfdump"
required-features = ["read", "std", "fallible-iterator"]
Expand Down
229 changes: 22 additions & 207 deletions crates/examples/src/bin/dwarfdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use fallible_iterator::FallibleIterator;
use gimli::{Section, UnitHeader, UnitOffset, UnitSectionOffset, UnitType, UnwindSection};
use object::{Object, ObjectSection, ObjectSymbol};
use object::{Object, ObjectSection};
use regex::bytes::Regex;
use std::borrow::Cow;
use std::cmp::min;
Expand Down Expand Up @@ -154,202 +154,36 @@ impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where
{
}

type RelocationMap = HashMap<usize, object::Relocation>;
#[derive(Debug, Default)]
struct RelocationMap(object::read::RelocationMap);

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;
}
// There are other things we could match but currently don't
#[allow(clippy::single_match)]
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
);
}
}
_ => {
impl RelocationMap {
fn add(&mut self, file: &object::File, section: &object::Section) {
for (offset, relocation) in section.relocations() {
if let Err(e) = self.0.add(file, offset, relocation) {
eprintln!(
"Unsupported relocation for section {} at offset 0x{:08x}",
"Relocation error for section {} at offset 0x{:08x}: {}",
section.name().unwrap(),
offset
offset,
e
);
}
}
}
}

/// 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) {
// There are other things we could match but currently don't
#[allow(clippy::single_match)]
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)
impl<'a> gimli::read::Relocate for &'a RelocationMap {
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
Ok(self.0.relocate(offset as u64, value))
}

#[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)
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
}
}

type Relocate<'a, R> = gimli::RelocateReader<R, &'a RelocationMap>;

impl<'a, R: Reader> Reader for Relocate<'a, R> {}

#[derive(Default)]
Expand Down Expand Up @@ -561,21 +395,6 @@ fn main() {
}
}

fn empty_file_section<Endian: gimli::Endianity>(
endian: Endian,
arena_relocations: &Arena<RelocationMap>,
) -> Relocate<'_, gimli::EndianSlice<'_, Endian>> {
let reader = gimli::EndianSlice::new(&[], endian);
let section = reader;
let relocations = RelocationMap::default();
let relocations = arena_relocations.alloc(relocations);
Relocate {
relocations,
section,
reader,
}
}

fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
id: gimli::SectionId,
file: &object::File<'input>,
Expand All @@ -597,22 +416,17 @@ fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
Some(ref section) => {
// DWO sections never have relocations, so don't bother.
if !is_dwo {
add_relocations(&mut relocations, file, section);
relocations.add(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);
let reader = gimli::EndianSlice::new(data_ref, endian);
let section = reader;
let section = gimli::EndianSlice::new(data_ref, endian);
let relocations = arena_relocations.alloc(relocations);
Ok(Relocate {
relocations,
section,
reader,
})
Ok(Relocate::new(section, relocations))
}

fn dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Result<()>
Expand Down Expand Up @@ -672,7 +486,8 @@ where

let w = &mut BufWriter::new(io::stdout());
if flags.dwp {
let empty = empty_file_section(endian, &arena_relocations);
let empty_relocations = arena_relocations.alloc(RelocationMap::default());
let empty = Relocate::new(gimli::EndianSlice::new(&[], endian), empty_relocations);
let dwp = gimli::DwarfPackage::load(&mut load_section, empty)?;
dump_dwp(w, &dwp, dwo_parent.unwrap(), dwo_parent_units, flags)?;
w.flush()?;
Expand Down
Loading

0 comments on commit b1b9b30

Please sign in to comment.