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

Improve relocation support #709

Merged
merged 6 commits into from
Apr 11, 2024
Merged
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
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
Loading