diff --git a/src/elf/mod.rs b/src/elf/mod.rs index d93040f24..afd069e15 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -75,6 +75,7 @@ if_sylvan! { pub type Dyn = dyn::Dyn; pub type Dynamic = dyn::Dynamic; pub type Reloc = reloc::Reloc; + pub type RelocSection<'a> = reloc::RelocSection<'a>; pub type ProgramHeaders = Vec; pub type SectionHeaders = Vec; @@ -106,13 +107,13 @@ if_sylvan! { /// Contains dynamic linking information, with the _DYNAMIC array + a preprocessed DynamicInfo for that array pub dynamic: Option, /// The dynamic relocation entries (strings, copy-data, etc.) with an addend - pub dynrelas: Vec, + pub dynrelas: RelocSection<'a>, /// The dynamic relocation entries without an addend - pub dynrels: Vec, + pub dynrels: RelocSection<'a>, /// The plt relocation entries (procedure linkage table). For 32-bit binaries these are usually Rel (no addend) - pub pltrelocs: Vec, + pub pltrelocs: RelocSection<'a>, /// Section relocations by section index (only present if this is a relocatable object file) - pub shdr_relocs: Vec<(ShdrIdx, Vec)>, + pub shdr_relocs: Vec<(ShdrIdx, RelocSection<'a>)>, /// The binary's soname, if it has one pub soname: Option<&'a str>, /// The binary's program interpreter (e.g., dynamic linker), if it has one @@ -279,9 +280,9 @@ if_sylvan! { let mut soname = None; let mut libraries = vec![]; let mut dynsyms = Symtab::default(); - let mut dynrelas = vec![]; - let mut dynrels = vec![]; - let mut pltrelocs = vec![]; + let mut dynrelas = RelocSection::default(); + let mut dynrels = RelocSection::default(); + let mut pltrelocs = RelocSection::default(); let mut dynstrtab = Strtab::default(); let dynamic = Dynamic::parse(bytes, &program_headers, bias, ctx)?; if let Some(ref dynamic) = dynamic { @@ -301,10 +302,10 @@ if_sylvan! { let num_syms = if dyn_info.syment == 0 { 0 } else { if dyn_info.strtab <= dyn_info.symtab { 0 } else { (dyn_info.strtab - dyn_info.symtab) / dyn_info.syment }}; dynsyms = Symtab::parse(bytes, dyn_info.symtab, num_syms, ctx)?; // parse the dynamic relocations - dynrelas = Reloc::parse(bytes, dyn_info.rela, dyn_info.relasz, true, ctx)?; - dynrels = Reloc::parse(bytes, dyn_info.rel, dyn_info.relsz, false, ctx)?; + dynrelas = RelocSection::parse(bytes, dyn_info.rela, dyn_info.relasz, true, ctx)?; + dynrels = RelocSection::parse(bytes, dyn_info.rel, dyn_info.relsz, false, ctx)?; let is_rela = dyn_info.pltrel as u64 == dyn::DT_RELA; - pltrelocs = Reloc::parse(bytes, dyn_info.jmprel, dyn_info.pltrelsz, is_rela, ctx)?; + pltrelocs = RelocSection::parse(bytes, dyn_info.jmprel, dyn_info.pltrelsz, is_rela, ctx)?; } // iterate through shdrs again iff we're an ET_REL @@ -314,12 +315,12 @@ if_sylvan! { for (idx, section) in section_headers.iter().enumerate() { if section.sh_type == section_header::SHT_REL { section.check_size(bytes.len())?; - let sh_relocs = Reloc::parse(bytes, section.sh_offset as usize, section.sh_size as usize, false, ctx)?; + let sh_relocs = RelocSection::parse(bytes, section.sh_offset as usize, section.sh_size as usize, false, ctx)?; relocs.push((idx, sh_relocs)); } if section.sh_type == section_header::SHT_RELA { section.check_size(bytes.len())?; - let sh_relocs = Reloc::parse(bytes, section.sh_offset as usize, section.sh_size as usize, true, ctx)?; + let sh_relocs = RelocSection::parse(bytes, section.sh_offset as usize, section.sh_size as usize, true, ctx)?; relocs.push((idx, sh_relocs)); } } diff --git a/src/elf/reloc.rs b/src/elf/reloc.rs index a2a655bbb..ffc1fc903 100644 --- a/src/elf/reloc.rs +++ b/src/elf/reloc.rs @@ -254,6 +254,7 @@ pub mod reloc64 { ///////////////////////////// if_alloc! { use scroll::{ctx, Pread}; + use scroll::ctx::SizeWith; use core::fmt; use core::result; use container::{Ctx, Container}; @@ -278,18 +279,6 @@ if_alloc! { use scroll::ctx::SizeWith; Reloc::size_with(&(is_rela, ctx)) } - #[cfg(feature = "endian_fd")] - pub fn parse(bytes: &[u8], mut offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> ::error::Result> { - use scroll::Pread; - let count = filesz / Reloc::size(is_rela, ctx); - let mut relocs = Vec::with_capacity(count); - let offset = &mut offset; - for _ in 0..count { - let reloc = bytes.gread_with::(offset, (is_rela, ctx))?; - relocs.push(reloc); - } - Ok(relocs) - } } type RelocCtx = (bool, Ctx); @@ -392,6 +381,84 @@ if_alloc! { } } + #[derive(Default)] + pub struct RelocSection<'a> { + bytes: &'a [u8], + count: usize, + ctx: RelocCtx, + start: usize, + end: usize, + } + + impl<'a> fmt::Debug for RelocSection<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let len = self.bytes.len(); + fmt.debug_struct("RelocSection") + .field("bytes", &len) + .field("range", &format!("{:#x}..{:#x}", self.start, self.end)) + .field("count", &self.count) + .field("Relocations", &self.to_vec()) + .finish() + } + } + + impl<'a> RelocSection<'a> { + #[cfg(feature = "endian_fd")] + pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> ::error::Result> { + // TODO: better error message when too large (see symtab implementation) + let bytes = bytes.pread_with(offset, filesz)?; + + Ok(RelocSection { + bytes: bytes, + count: filesz / Reloc::size(is_rela, ctx), + ctx: (is_rela, ctx), + start: offset, + end: offset + filesz, + }) + } + + /// Try to parse a single relocation from the binary, at `index`. + #[inline] + pub fn get(&self, index: usize) -> Option { + if index >= self.count { + None + } else { + Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap()) + } + } + + /// The number of relocations in the section. + pub fn len(&self) -> usize { + self.count + } + + /// Iterate over all relocations. + pub fn iter(&self) -> RelocIterator<'a> { + self.into_iter() + } + + /// Parse all relocations into a vector. + pub fn to_vec(&self) -> Vec { + self.iter().collect() + } + } + + impl<'a, 'b> IntoIterator for &'b RelocSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = RelocIterator<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + RelocIterator { + bytes: self.bytes, + offset: 0, + index: 0, + count: self.count, + ctx: self.ctx, + } + } + } + pub struct RelocIterator<'a> { bytes: &'a [u8], offset: usize,