diff --git a/src/read/any.rs b/src/read/any.rs index 7da02e94..ae54ba2c 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -19,9 +19,9 @@ use crate::read::xcoff; use crate::read::{ self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectKind, ObjectMap, - ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, Result, - SectionFlags, SectionIndex, SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, - SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, + ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, + RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SubArchitecture, + SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, }; /// Evaluate an expression on the contents of a file format enum. @@ -787,6 +787,10 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for Section<'data, 'f } } + fn relocation_map(&self) -> Result { + with_inner!(self.inner, SectionInternal, |x| x.relocation_map()) + } + fn flags(&self) -> SectionFlags { with_inner!(self.inner, SectionInternal, |x| x.flags()) } diff --git a/src/read/coff/section.rs b/src/read/coff/section.rs index 7ecf41ae..afaec12d 100644 --- a/src/read/coff/section.rs +++ b/src/read/coff/section.rs @@ -6,7 +6,7 @@ use crate::pe; use crate::read::util::StringTable; use crate::read::{ self, CompressedData, CompressedFileRange, Error, ObjectSection, ObjectSegment, ReadError, - ReadRef, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, + ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, }; use super::{CoffFile, CoffHeader, CoffRelocationIterator}; @@ -384,6 +384,10 @@ impl<'data, 'file, R: ReadRef<'data>, Coff: CoffHeader> ObjectSection<'data> } } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + fn flags(&self) -> SectionFlags { SectionFlags::Coff { characteristics: self.section.characteristics.get(LE), diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index ae835528..5b7e8491 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -6,7 +6,7 @@ use crate::endian::{self, Endianness, U32Bytes}; use crate::pod::Pod; use crate::read::{ self, Bytes, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, - ReadError, ReadRef, SectionFlags, SectionIndex, SectionKind, StringTable, + ReadError, ReadRef, RelocationMap, SectionFlags, SectionIndex, SectionKind, StringTable, }; use super::{ @@ -593,6 +593,10 @@ where } } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + fn flags(&self) -> SectionFlags { SectionFlags::Elf { sh_flags: self.section.sh_flags(self.file.endian).into(), diff --git a/src/read/macho/section.rs b/src/read/macho/section.rs index 569600fa..16c199c3 100644 --- a/src/read/macho/section.rs +++ b/src/read/macho/section.rs @@ -5,8 +5,8 @@ use crate::endian::{self, Endianness}; use crate::macho; use crate::pod::Pod; use crate::read::{ - self, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, Result, - SectionFlags, SectionIndex, SectionKind, + self, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, RelocationMap, + Result, SectionFlags, SectionIndex, SectionKind, }; use super::{MachHeader, MachOFile, MachORelocationIterator}; @@ -199,6 +199,10 @@ where } } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + fn flags(&self) -> SectionFlags { SectionFlags::MachO { flags: self.internal.section.flags(self.file.endian), diff --git a/src/read/mod.rs b/src/read/mod.rs index 48b6e992..241a87b7 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -45,6 +45,11 @@ use alloc::borrow::Cow; use alloc::vec::Vec; use core::{fmt, result}; +#[cfg(not(feature = "std"))] +use alloc::collections::btree_map::BTreeMap as Map; +#[cfg(feature = "std")] +use std::collections::hash_map::HashMap as Map; + pub use crate::common::*; mod read_ref; @@ -727,6 +732,90 @@ impl Relocation { } } +/// A map from section offsets to relocation information. +/// +/// This can be used to apply relocations to a value at a given section offset. +/// This is intended for use with DWARF in relocatable object files, and only +/// supports relocations that are used in DWARF. +/// +/// Returned by [`ObjectSection::relocation_map`]. +#[derive(Debug, Default)] +pub struct RelocationMap(Map); + +impl RelocationMap { + /// Construct a new relocation map for a section. + /// + /// Fails if any relocation cannot be added to the map. + /// You can manually use `add` if you need different error handling, + /// such as to list all errors or to ignore them. + pub fn new<'data, 'file, T>(file: &'file T, section: &T::Section) -> Result + where + T: Object<'data, 'file>, + { + let mut map = RelocationMap(Map::new()); + for (offset, relocation) in section.relocations() { + map.add(file, offset, relocation)?; + } + Ok(map) + } + + /// Add a single relocation to the map. + pub fn add<'data: 'file, 'file, T>( + &mut self, + file: &'file T, + offset: u64, + relocation: Relocation, + ) -> Result<()> + where + T: Object<'data, 'file>, + { + let mut entry = RelocationMapEntry { + implicit_addend: relocation.has_implicit_addend(), + addend: relocation.addend() as u64, + }; + match relocation.kind() { + RelocationKind::Absolute => match relocation.target() { + RelocationTarget::Symbol(symbol_idx) => { + let symbol = file + .symbol_by_index(symbol_idx) + .read_error("Relocation with invalid symbol")?; + entry.addend = symbol.address().wrapping_add(entry.addend); + } + _ => { + return Err(Error("Unsupported relocation target")); + } + }, + _ => { + return Err(Error("Unsupported relocation type")); + } + } + if self.0.insert(offset, entry).is_some() { + return Err(Error("Multiple relocations for offset")); + } + Ok(()) + } + + /// Relocate a value that was read from the section at the given offset. + pub fn relocate(&self, offset: u64, value: u64) -> u64 { + if let Some(relocation) = self.0.get(&offset) { + if relocation.implicit_addend { + // Use the explicit addend too, because it may have the symbol value. + value.wrapping_add(relocation.addend) + } else { + relocation.addend + } + } else { + value + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct RelocationMapEntry { + implicit_addend: bool, + addend: u64, +} + /// A data compression format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] diff --git a/src/read/pe/section.rs b/src/read/pe/section.rs index ecbc5451..2b4fd1f0 100644 --- a/src/read/pe/section.rs +++ b/src/read/pe/section.rs @@ -6,7 +6,7 @@ use crate::pe; use crate::pe::ImageSectionHeader; use crate::read::{ self, CompressedData, CompressedFileRange, ObjectSection, ObjectSegment, ReadError, ReadRef, - Relocation, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, + Relocation, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, }; use super::{ImageNtHeaders, PeFile, SectionTable}; @@ -288,6 +288,10 @@ where PeRelocationIterator(PhantomData) } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + fn flags(&self) -> SectionFlags { SectionFlags::Coff { characteristics: self.section.characteristics.get(LE), diff --git a/src/read/traits.rs b/src/read/traits.rs index 9f77aa0d..1747b924 100644 --- a/src/read/traits.rs +++ b/src/read/traits.rs @@ -4,9 +4,9 @@ use alloc::vec::Vec; use crate::endian::Endianness; use crate::read::{ self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export, - FileFlags, Import, ObjectKind, ObjectMap, Relocation, Result, SectionFlags, SectionIndex, - SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, - SymbolMapName, SymbolScope, SymbolSection, + FileFlags, Import, ObjectKind, ObjectMap, Relocation, RelocationMap, Result, SectionFlags, + SectionIndex, SectionKind, SegmentFlags, SubArchitecture, SymbolFlags, SymbolIndex, SymbolKind, + SymbolMap, SymbolMapName, SymbolScope, SymbolSection, }; /// An object file. @@ -419,6 +419,9 @@ pub trait ObjectSection<'data>: read::private::Sealed { /// Get the relocations for this section. fn relocations(&self) -> Self::RelocationIterator; + /// Construct a relocation map for this section. + fn relocation_map(&self) -> Result; + /// Section flags that are specific to each file format. fn flags(&self) -> SectionFlags; } diff --git a/src/read/wasm.rs b/src/read/wasm.rs index 6c96f43c..619a3380 100644 --- a/src/read/wasm.rs +++ b/src/read/wasm.rs @@ -11,9 +11,9 @@ use wasmparser as wp; use crate::read::{ self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection, - ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, Result, - SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, SymbolKind, - SymbolScope, SymbolSection, + ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Relocation, RelocationMap, + Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags, SymbolFlags, SymbolIndex, + SymbolKind, SymbolScope, SymbolSection, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -737,6 +737,10 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for WasmSection<'data WasmRelocationIterator(PhantomData) } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + #[inline] fn flags(&self) -> SectionFlags { SectionFlags::None diff --git a/src/read/xcoff/section.rs b/src/read/xcoff/section.rs index 660e289f..a7a4df5d 100644 --- a/src/read/xcoff/section.rs +++ b/src/read/xcoff/section.rs @@ -4,8 +4,8 @@ use core::{iter, result, slice, str}; use crate::endian::BigEndian as BE; use crate::pod::Pod; use crate::read::{ - self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError, ReadRef, Result, - SectionFlags, SectionIndex, SectionKind, + self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError, ReadRef, + RelocationMap, Result, SectionFlags, SectionIndex, SectionKind, }; use crate::xcoff; @@ -191,6 +191,10 @@ where } } + fn relocation_map(&self) -> read::Result { + RelocationMap::new(self.file, self) + } + fn flags(&self) -> SectionFlags { SectionFlags::Xcoff { s_flags: self.section.s_flags(),