From e04e5a4e7bedc50a5ce59caeb0e3105f1de80ee9 Mon Sep 17 00:00:00 2001 From: Tal Risin Date: Sat, 8 Jul 2023 13:39:38 +0300 Subject: [PATCH 1/5] implement basic dynamic table parser --- src/read/elf/dynamic.rs | 217 +++++++++++++++++++++++++++++++++++++++- src/read/elf/symbol.rs | 15 +++ 2 files changed, 231 insertions(+), 1 deletion(-) diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index 5fe15b56..fd626741 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -4,7 +4,15 @@ use core::fmt::Debug; use crate::elf; use crate::endian; use crate::pod::Pod; -use crate::read::{ReadError, Result, StringTable}; +use crate::read::{ + self, + elf::{FileHeader, SymbolTable}, + Error, ReadError, Result, StringTable, +}; +use crate::ReadRef; + +use super::GnuHashTable; +use super::HashTable; /// A trait for generic access to `Dyn32` and `Dyn64`. #[allow(missing_docs)] @@ -115,3 +123,210 @@ impl Dyn for elf::Dyn64 { self.d_val.get(endian) } } + +#[derive(Debug)] +enum DynamicHashTableIternal<'data, Elf> +where + Elf: FileHeader, +{ + Hash(HashTable<'data, Elf>), + GnuHash(GnuHashTable<'data, Elf>), +} + +/// Hash table found through the dynamic segment. +#[derive(Debug)] +pub struct DynamicHashTable<'data, Elf> +where + Elf: FileHeader, +{ + inner: DynamicHashTableIternal<'data, Elf>, +} + +impl<'data, Elf> DynamicHashTable<'data, Elf> +where + Elf: FileHeader, +{ + /// Returns the symbol table length. + pub fn symbol_table_length(&self, endian: Elf::Endian) -> Option { + match &self.inner { + DynamicHashTableIternal::GnuHash(t) => t.symbol_table_length(endian), + DynamicHashTableIternal::Hash(t) => Some(t.symbol_table_length()), + } + } +} + +/// A parsed dynamic segment +#[derive(Debug)] +pub struct Dynamic<'data, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) base: usize, + pub(super) endian: Elf::Endian, + pub(super) data: R, + pub(super) table: &'data [Elf::Dyn], + pub(super) strings: StringTable<'data, R>, + pub(super) hash: DynamicHashTable<'data, Elf>, + pub(super) symbols: SymbolTable<'data, Elf, R>, +} + +impl<'data, Elf, R> Dynamic<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data> + 'data, +{ + /// Parse the dynamic table of a static elf. + pub fn new(endian: Elf::Endian, data: R, dynamic: &'data [Elf::Dyn]) -> read::Result { + // Since this elf is not loaded, the addresses should contain offsets into the elf, thus base is 0. + Self::new_loaded(0, endian, data, dynamic) + } + + /// Parse the dynamic table of a loaded elf. + /// `base` should point to the base address of the elf, and will be used to convert absolute memory addresses into + /// offsets into the elf. + pub fn new_loaded( + base: usize, + endian: Elf::Endian, + data: R, + dynamic: &'data [Elf::Dyn], + ) -> read::Result { + // Parse and check only mandatory fields. + + // The last element in dynamic must be DT_NULL: + if dynamic + .last() + .read_error("Dynamic table is empty!")? + .tag32(endian) + .read_error("Failed to parse dynamic table entry's tag!")? + != elf::DT_NULL + { + return Err(Error("Dynamic table's last element is not of type DT_NULL")); + } + + let strings = Self::strings(base, endian, data, dynamic)?; + let hash = Self::hash(base, endian, data, dynamic)?; + let symbols = Self::symbols(base, endian, data, dynamic, &hash, strings)?; + + Ok(Self { + base, + endian, + data, + table: dynamic, + strings, + hash, + symbols, + }) + } + + fn strings( + base: usize, + endian: Elf::Endian, + data: R, + dynamic: &'data [Elf::Dyn], + ) -> Result> { + let strings_val = dyn_by_tag::(endian, dynamic, elf::DT_STRTAB) + .read_error("Dynamic strings table is missing!")?; + let strings_offset = dyn_val_into_offset(base as u64, strings_val.into()); + let strings_size = dyn_by_tag::(endian, dynamic, elf::DT_STRSZ) + .read_error("Dynamic strings table size is missing!")?; + + Ok(StringTable::new( + data, + strings_offset, + strings_offset + strings_size.into(), + )) + } + + fn hash( + base: usize, + endian: Elf::Endian, + data: R, + dynamic: &'data [Elf::Dyn], + ) -> Result> { + // First, try finding GNU_HASH as it's the new de-facto standard. + let hash_val = dyn_by_tag::(endian, dynamic, elf::DT_GNU_HASH); + + if let Some(hash_val) = hash_val { + let hash_offset = dyn_val_into_offset(base as u64, hash_val.into()); + + let table = GnuHashTable::parse( + endian, + data.read_slice_at( + hash_offset, + (data.len().read_error("Can't get data len")? - hash_offset) as usize, + ) + .read_error("Failed to get slice of data to parse gnu hash table")?, + )?; + + let inner = DynamicHashTableIternal::GnuHash(table); + return Ok(DynamicHashTable { inner }); + }; + + // No gnu hash table, let's try OG hash table + let hash_val = dyn_by_tag::(endian, dynamic, elf::DT_HASH) + .read_error("Failed to find Gnu or regular hash table!")?; + + let hash_offset = dyn_val_into_offset(base as u64, hash_val.into()); + + let table = HashTable::parse( + endian, + data.read_slice_at( + hash_offset, + (data.len().read_error("Can't get data len")? - hash_offset) as usize, + ) + .read_error("Failed to get slice of data to parse hash table")?, + )?; + + let inner = DynamicHashTableIternal::Hash(table); + return Ok(DynamicHashTable { inner }); + } + + fn symbols( + base: usize, + endian: Elf::Endian, + data: R, + dynamic: &'data [Elf::Dyn], + hash: &DynamicHashTable<'data, Elf>, + strings: StringTable<'data, R>, + ) -> Result> { + let symbols_val = dyn_by_tag::(endian, dynamic, elf::DT_SYMTAB) + .read_error("Dynamic symbols table is missing!")?; + let symbols_offset = dyn_val_into_offset(base as u64, symbols_val.into()); + let symbols_amount = hash + .symbol_table_length(endian) + .read_error("Failed to get dynamic symbol table length")?; + let symbols: &[Elf::Sym] = data + .read_slice_at(symbols_offset as u64, symbols_amount as usize) + .read_error("Failed to read dynamic symbols table")?; + + SymbolTable::dynamic(symbols, strings) + } +} + +fn dyn_by_tag( + endian: Elf::Endian, + dynamic: &[Elf::Dyn], + tag: u32, +) -> Option { + dynamic.iter().find_map(|entry| { + let tag32 = entry.tag32(endian)?; + if tag32 == tag { + Some(entry.d_val(endian)) + } else { + None + } + }) +} + +#[cfg(target_os = "android")] +#[inline] +fn dyn_val_into_offset(_: u64, val: u64) -> u64 { + val +} + +#[cfg(target_os = "linux")] +#[inline] +fn dyn_val_into_offset(base: u64, val: u64) -> u64 { + val - base +} diff --git a/src/read/elf/symbol.rs b/src/read/elf/symbol.rs index ac109570..967297d3 100644 --- a/src/read/elf/symbol.rs +++ b/src/read/elf/symbol.rs @@ -88,6 +88,21 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { }) } + /// Create dynamic symbol table. + pub fn dynamic( + symbols: &'data [Elf::Sym], + strings: StringTable<'data, R>, + ) -> read::Result> { + Ok(SymbolTable { + section: SectionIndex(0), + string_section: SectionIndex(0), + symbols, + strings, + shndx: Default::default(), + shndx_section: SectionIndex(0), + }) + } + /// Return the section index of this symbol table. #[inline] pub fn section(&self) -> SectionIndex { From 5c7beb6bd3e6745f73c025e89993a8958d32e4a7 Mon Sep 17 00:00:00 2001 From: Tal Risin Date: Sat, 8 Jul 2023 13:57:02 +0300 Subject: [PATCH 2/5] allow access to dynamic table's fields --- src/read/elf/dynamic.rs | 43 ++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index fd626741..88053388 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -11,6 +11,7 @@ use crate::read::{ }; use crate::ReadRef; +use super::ElfSymbolTable; use super::GnuHashTable; use super::HashTable; @@ -171,7 +172,7 @@ where pub(super) symbols: SymbolTable<'data, Elf, R>, } -impl<'data, Elf, R> Dynamic<'data, Elf, R> +impl<'data, 'file, Elf, R> Dynamic<'data, Elf, R> where Elf: FileHeader, R: ReadRef<'data> + 'data, @@ -204,9 +205,9 @@ where return Err(Error("Dynamic table's last element is not of type DT_NULL")); } - let strings = Self::strings(base, endian, data, dynamic)?; - let hash = Self::hash(base, endian, data, dynamic)?; - let symbols = Self::symbols(base, endian, data, dynamic, &hash, strings)?; + let strings = Self::parse_strings(base, endian, data, dynamic)?; + let hash = Self::parse_hash(base, endian, data, dynamic)?; + let symbols = Self::parse_symbols(base, endian, data, dynamic, &hash, strings)?; Ok(Self { base, @@ -219,7 +220,35 @@ where }) } - fn strings( + /// Returns base address of elf + pub fn base(&self) -> usize { + self.base + } + + /// Returns endiannes + pub fn endian(&self) -> Elf::Endian { + self.endian + } + + /// Returns string table + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Returns hash table + pub fn hash(&self) -> &'_ DynamicHashTable<'data, Elf> { + &self.hash + } + + /// Returns symbol table + pub fn symbols(&'file self) -> ElfSymbolTable<'data, 'file, Elf, R> { + ElfSymbolTable { + endian: self.endian, + symbols: &self.symbols, + } + } + + fn parse_strings( base: usize, endian: Elf::Endian, data: R, @@ -238,7 +267,7 @@ where )) } - fn hash( + fn parse_hash( base: usize, endian: Elf::Endian, data: R, @@ -282,7 +311,7 @@ where return Ok(DynamicHashTable { inner }); } - fn symbols( + fn parse_symbols( base: usize, endian: Elf::Endian, data: R, From fd1e1eede83788edab0e02cf645a2e97b2beaec3 Mon Sep 17 00:00:00 2001 From: Tal Risin Date: Mon, 10 Jul 2023 22:33:43 +0300 Subject: [PATCH 3/5] add iterators for relocations from dynamic --- src/read/any.rs | 4 +- src/read/elf/dynamic.rs | 301 +++++++++++++++++++++++++++++++++---- src/read/elf/file.rs | 10 +- src/read/elf/relocation.rs | 65 +++++++- src/read/elf/segment.rs | 25 +++ 5 files changed, 359 insertions(+), 46 deletions(-) diff --git a/src/read/any.rs b/src/read/any.rs index 342ad75f..ec0df72d 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -1260,9 +1260,9 @@ where R: ReadRef<'data>, { #[cfg(feature = "elf")] - Elf32(elf::ElfDynamicRelocationIterator32<'data, 'file, Endianness, R>), + Elf32(elf::ElfDynamicRelocationIteratorFromFile32<'data, 'file, Endianness, R>), #[cfg(feature = "elf")] - Elf64(elf::ElfDynamicRelocationIterator64<'data, 'file, Endianness, R>), + Elf64(elf::ElfDynamicRelocationIteratorFromFile64<'data, 'file, Endianness, R>), // We need to always use the lifetime parameters. #[allow(unused)] None(PhantomData<(&'data (), &'file (), R)>), diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index 88053388..81f7af8d 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -1,20 +1,20 @@ use core::convert::TryInto; use core::fmt::Debug; +use core::mem; use crate::elf; use crate::endian; use crate::pod::Pod; use crate::read::{ self, - elf::{FileHeader, SymbolTable}, + elf::{ + ElfDynamicRelocationIteratorFromDynamic, ElfRelaIterator, ElfSymbolTable, FileHeader, + GnuHashTable, HashTable, ProgramHeader, SymbolTable, + }, Error, ReadError, Result, StringTable, }; use crate::ReadRef; -use super::ElfSymbolTable; -use super::GnuHashTable; -use super::HashTable; - /// A trait for generic access to `Dyn32` and `Dyn64`. #[allow(missing_docs)] pub trait Dyn: Debug + Pod { @@ -164,9 +164,10 @@ where R: ReadRef<'data>, { pub(super) base: usize, + pub(super) header: &'data Elf, pub(super) endian: Elf::Endian, pub(super) data: R, - pub(super) table: &'data [Elf::Dyn], + pub(super) dynamic: &'data [Elf::Dyn], pub(super) strings: StringTable<'data, R>, pub(super) hash: DynamicHashTable<'data, Elf>, pub(super) symbols: SymbolTable<'data, Elf, R>, @@ -178,9 +179,16 @@ where R: ReadRef<'data> + 'data, { /// Parse the dynamic table of a static elf. - pub fn new(endian: Elf::Endian, data: R, dynamic: &'data [Elf::Dyn]) -> read::Result { + pub fn new(elf: &'data Elf, data: R) -> read::Result { // Since this elf is not loaded, the addresses should contain offsets into the elf, thus base is 0. - Self::new_loaded(0, endian, data, dynamic) + let endian = elf.endian()?; + let program_headers = elf.program_headers(elf.endian()?, data)?; + let dynamic = program_headers + .iter() + .find_map(|ph| ph.dynamic(endian, data).transpose()) + .transpose()? + .read_error("No dynamic segment!")?; + Self::new_loaded(0, elf, data, Some(dynamic)) } /// Parse the dynamic table of a loaded elf. @@ -188,10 +196,24 @@ where /// offsets into the elf. pub fn new_loaded( base: usize, - endian: Elf::Endian, + header: &'data Elf, data: R, - dynamic: &'data [Elf::Dyn], + dynamic: Option<&'data [Elf::Dyn]>, ) -> read::Result { + let endian = header.endian()?; + let program_headers = header.program_headers(header.endian()?, data)?; + + // Use provided dynamic segment or find one. + let dynamic = if let Some(dynamic) = dynamic { + dynamic + } else { + program_headers + .iter() + .find_map(|ph| ph.dynamic_loaded(endian, data).transpose()) + .transpose()? + .read_error("No dynamic segment!")? + }; + // Parse and check only mandatory fields. // The last element in dynamic must be DT_NULL: @@ -211,9 +233,10 @@ where Ok(Self { base, + header, endian, data, - table: dynamic, + dynamic, strings, hash, symbols, @@ -248,16 +271,162 @@ where } } + /// Returns dynamic relocations iterator (`.rela.dyn`) + pub fn dynamic_relocations( + &'file self, + ) -> Result>> { + let pltrel = if let Some(p) = Self::dyn_by_tag(self.endian, self.dynamic, elf::DT_PLTREL) { + p + } else { + return Ok(None); + } + .into() as u32; + + let (dt_relsz, dt_relent) = match pltrel { + elf::DT_REL => (elf::DT_RELSZ, elf::DT_RELENT), + elf::DT_RELA => (elf::DT_RELASZ, elf::DT_RELAENT), + _ => return Err(Error("Invalid pltrel value!")), + }; + + let dynamic_relocations_val = + if let Some(r) = Self::dyn_by_tag(self.endian, self.dynamic, pltrel) { + r + } else { + return Ok(None); + }; + let dynamic_relocations_offset = + dyn_val_into_offset(self.base as u64, dynamic_relocations_val.into()); + + // Unwrap safety: according to the ELF manual, if DT_REL or DT_RELA exist, then DT_RELSZ and DT_RELENT or + // DT_RELASZ and DT_RELAENT (accordingly) *MUST* exist. + let dynamic_relocations_size = Self::dyn_by_tag(self.endian, self.dynamic, dt_relsz) + .unwrap() + .into() as usize; + let dynamic_relocations_entry_size = Self::dyn_by_tag(self.endian, self.dynamic, dt_relent) + .unwrap() + .into() as usize; + + // TODO: return error? + match pltrel { + elf::DT_REL => { + debug_assert_eq!(dynamic_relocations_entry_size, mem::size_of::()) + } + elf::DT_RELA => { + debug_assert_eq!(dynamic_relocations_entry_size, mem::size_of::()) + } + _ => unreachable!("should have returned an error in the previous match"), + }; + + let dynamic_relocations = match pltrel { + elf::DT_REL => ElfRelaIterator::::Rel( + self.data + .read_slice_at( + dynamic_relocations_offset, + dynamic_relocations_size / dynamic_relocations_entry_size, + ) + .read_error("Failed to read dynamic relocations")? + .iter(), + ), + elf::DT_RELA => ElfRelaIterator::::Rela( + self.data + .read_slice_at( + dynamic_relocations_offset, + dynamic_relocations_size / dynamic_relocations_entry_size, + ) + .read_error("Failed to read dynamic relocations")? + .iter(), + ), + _ => unreachable!("should have returned an error in the previous match"), + }; + + Ok(Some(ElfDynamicRelocationIteratorFromDynamic { + header: self.header, + endian: self.endian, + relocations: dynamic_relocations, + })) + } + + /// Returns PLT relocations iterator (`.rela.plt`) + pub fn plt_relocations( + &'file self, + ) -> Result>> { + let plt_relocations_val = + if let Some(r) = Self::dyn_by_tag(self.endian, self.dynamic, elf::DT_JMPREL) { + r + } else { + return Ok(None); + }; + let plt_relocations_offset = + dyn_val_into_offset(self.base as u64, plt_relocations_val.into()); + + // Unwrap safety: according to the ELF manual, if DT_JMPREL exists, then DT_PLTREL and DT_PLTRELSZ *MUST* exist. + let pltrel = Self::dyn_by_tag(self.endian, self.dynamic, elf::DT_PLTREL) + .unwrap() + .into() as u32; + let plt_relocations_size = Self::dyn_by_tag(self.endian, self.dynamic, elf::DT_PLTRELSZ) + .unwrap() + .into() as usize; + + let dt_relent = match pltrel { + elf::DT_REL => elf::DT_RELENT, + elf::DT_RELA => elf::DT_RELAENT, + _ => return Err(Error("Invalid pltrel value!")), + }; + + let plt_relocations_entry_size = Self::dyn_by_tag(self.endian, self.dynamic, dt_relent) + .ok_or(Error("Unable to find relocation size entry!"))? + .into() as usize; + + // TODO: return error? + match pltrel { + elf::DT_REL => { + debug_assert_eq!(plt_relocations_entry_size, mem::size_of::()) + } + elf::DT_RELA => { + debug_assert_eq!(plt_relocations_entry_size, mem::size_of::()) + } + _ => unreachable!("should have returned an error in the previous match"), + }; + + let plt_relocations = match pltrel { + elf::DT_REL => ElfRelaIterator::::Rel( + self.data + .read_slice_at( + plt_relocations_offset, + plt_relocations_size / plt_relocations_entry_size, + ) + .read_error("Failed to read dynamic relocations")? + .iter(), + ), + elf::DT_RELA => ElfRelaIterator::::Rela( + self.data + .read_slice_at( + plt_relocations_offset, + plt_relocations_size / plt_relocations_entry_size, + ) + .read_error("Failed to read dynamic relocations")? + .iter(), + ), + _ => unreachable!("should have returned an error in the previous match"), + }; + + Ok(Some(ElfDynamicRelocationIteratorFromDynamic { + header: self.header, + endian: self.endian, + relocations: plt_relocations, + })) + } + fn parse_strings( base: usize, endian: Elf::Endian, data: R, dynamic: &'data [Elf::Dyn], ) -> Result> { - let strings_val = dyn_by_tag::(endian, dynamic, elf::DT_STRTAB) + let strings_val = Self::dyn_by_tag(endian, dynamic, elf::DT_STRTAB) .read_error("Dynamic strings table is missing!")?; let strings_offset = dyn_val_into_offset(base as u64, strings_val.into()); - let strings_size = dyn_by_tag::(endian, dynamic, elf::DT_STRSZ) + let strings_size = Self::dyn_by_tag(endian, dynamic, elf::DT_STRSZ) .read_error("Dynamic strings table size is missing!")?; Ok(StringTable::new( @@ -274,7 +443,7 @@ where dynamic: &'data [Elf::Dyn], ) -> Result> { // First, try finding GNU_HASH as it's the new de-facto standard. - let hash_val = dyn_by_tag::(endian, dynamic, elf::DT_GNU_HASH); + let hash_val = Self::dyn_by_tag(endian, dynamic, elf::DT_GNU_HASH); if let Some(hash_val) = hash_val { let hash_offset = dyn_val_into_offset(base as u64, hash_val.into()); @@ -293,7 +462,7 @@ where }; // No gnu hash table, let's try OG hash table - let hash_val = dyn_by_tag::(endian, dynamic, elf::DT_HASH) + let hash_val = Self::dyn_by_tag(endian, dynamic, elf::DT_HASH) .read_error("Failed to find Gnu or regular hash table!")?; let hash_offset = dyn_val_into_offset(base as u64, hash_val.into()); @@ -308,7 +477,8 @@ where )?; let inner = DynamicHashTableIternal::Hash(table); - return Ok(DynamicHashTable { inner }); + + Ok(DynamicHashTable { inner }) } fn parse_symbols( @@ -319,33 +489,29 @@ where hash: &DynamicHashTable<'data, Elf>, strings: StringTable<'data, R>, ) -> Result> { - let symbols_val = dyn_by_tag::(endian, dynamic, elf::DT_SYMTAB) + let symbols_val = Self::dyn_by_tag(endian, dynamic, elf::DT_SYMTAB) .read_error("Dynamic symbols table is missing!")?; let symbols_offset = dyn_val_into_offset(base as u64, symbols_val.into()); let symbols_amount = hash .symbol_table_length(endian) .read_error("Failed to get dynamic symbol table length")?; let symbols: &[Elf::Sym] = data - .read_slice_at(symbols_offset as u64, symbols_amount as usize) + .read_slice_at(symbols_offset, symbols_amount as usize) .read_error("Failed to read dynamic symbols table")?; SymbolTable::dynamic(symbols, strings) } -} -fn dyn_by_tag( - endian: Elf::Endian, - dynamic: &[Elf::Dyn], - tag: u32, -) -> Option { - dynamic.iter().find_map(|entry| { - let tag32 = entry.tag32(endian)?; - if tag32 == tag { - Some(entry.d_val(endian)) - } else { - None - } - }) + fn dyn_by_tag(endian: Elf::Endian, dynamic: &[Elf::Dyn], tag: u32) -> Option { + dynamic.iter().find_map(|entry| { + let tag32 = entry.tag32(endian)?; + if tag32 == tag { + Some(entry.d_val(endian)) + } else { + None + } + }) + } } #[cfg(target_os = "android")] @@ -359,3 +525,74 @@ fn dyn_val_into_offset(_: u64, val: u64) -> u64 { fn dyn_val_into_offset(base: u64, val: u64) -> u64 { val - base } + +// let pltrel = find_dyn_by_tag(dynamic, elf::DT_PLTREL, endian) +// .ok_or(Error("No pltrel! Can't determine between RELA and REL"))?; + +// let (dt_relsz, dt_relent) = match pltrel { +// DT_REL => (elf::DT_RELSZ, elf::DT_RELENT), +// DT_RELA => (elf::DT_RELASZ, elf::DT_RELAENT), +// _ => todo!(), +// }; + +// let dynamic_relocations_address = +// find_dyn_by_tag(data, pltrel, endian).ok_or(Error("Can't rela_dyn section!"))? as usize; +// let dynamic_relocations_offset = dynamic_relocations_address - base; + +// // Unwrap safety: according to the ELF manual, if DT_REL or DT_RELA exist, then DT_RELSZ and DT_RELENT or +// // DT_RELASZ and DT_RELAENT (accordingly) *MUST* exist. +// let dynamic_relocations_size = find_dyn_by_tag(data, dt_relsz, endian).unwrap().into(); +// let dynamic_relocation_entry_size = +// find_dyn_by_tag(data, dt_relent, endian).unwrap().into(); + +// let dynamic_plt_relocations_address = find_dyn_by_tag(data, elf::DT_JMPREL, endian) +// .ok_or(Error("Can't rela_dyn section!"))? +// .into() as usize; +// let dynamic_plt_relocations_offset = dynamic_plt_relocations_address - base; + +// // Unwrap safety: according to the ELF manual, if DT_JMPREL exists, then DT_PLTRELSZ *MUST* exist. +// let dynamic_plt_relocations_size = find_dyn_by_tag(data, elf::DT_PLTRELSZ, endian) +// .unwrap() +// .into(); + +// let dynamic_relocations = match pltrel { +// DT_REL => ElfRelaIterator::::Rel( +// data.read_slice_at( +// dynamic_relocations_offset as u64, +// dynamic_relocations_size as usize / dynamic_relocation_entry_size as usize, +// ) +// .read_error("Failed to parse dynamic relocations")? +// .iter(), +// ), +// DT_RELA => ElfRelaIterator::::Rela( +// data.read_slice_at( +// dynamic_relocations_offset as u64, +// dynamic_relocations_size as usize / dynamic_relocation_entry_size as usize, +// ) +// .read_error("Failed to parse dynamic relocations")? +// .iter(), +// ), +// _ => return Err(Error("Unexpected DT_PLTREL value!")), +// }; + +// let dynamic_plt_relocations = match pltrel { +// DT_REL => ElfRelaIterator::::Rel( +// data.read_slice_at( +// dynamic_plt_relocations_offset as u64, +// dynamic_plt_relocations_size as usize / dynamic_relocation_entry_size as usize, +// ) +// .read_error("Failed to parse dynamic relocations")? +// .iter(), +// ), +// DT_RELA => ElfRelaIterator::::Rela( +// data.read_slice_at( +// dynamic_plt_relocations_offset as u64, +// dynamic_plt_relocations_size as usize / dynamic_relocation_entry_size as usize, +// ) +// .read_error("Failed to parse dynamic relocations")? +// .iter(), +// ), +// _ => return Err(Error("Unexpected DT_PLTREL value!")), +// }; + +// let all_relocations = dynamic_relocations.chain(dynamic_plt_relocations); diff --git a/src/read/elf/file.rs b/src/read/elf/file.rs index aac66e7c..79ec9687 100644 --- a/src/read/elf/file.rs +++ b/src/read/elf/file.rs @@ -10,8 +10,8 @@ use crate::read::{ use crate::{elf, endian, Endian, Endianness, Pod, U32}; use super::{ - CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection, - ElfSectionIterator, ElfSegment, ElfSegmentIterator, ElfSymbol, ElfSymbolIterator, + CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIteratorFromFile, + ElfSection, ElfSectionIterator, ElfSegment, ElfSegmentIterator, ElfSymbol, ElfSymbolIterator, ElfSymbolTable, NoteHeader, ProgramHeader, Rel, Rela, RelocationSections, SectionHeader, SectionTable, Sym, SymbolTable, }; @@ -149,7 +149,7 @@ where type Symbol = ElfSymbol<'data, 'file, Elf, R>; type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; type SymbolTable = ElfSymbolTable<'data, 'file, Elf, R>; - type DynamicRelocationIterator = ElfDynamicRelocationIterator<'data, 'file, Elf, R>; + type DynamicRelocationIterator = ElfDynamicRelocationIteratorFromFile<'data, 'file, Elf, R>; fn architecture(&self) -> Architecture { match ( @@ -296,8 +296,8 @@ where fn dynamic_relocations( &'file self, - ) -> Option> { - Some(ElfDynamicRelocationIterator { + ) -> Option> { + Some(ElfDynamicRelocationIteratorFromFile { section_index: SectionIndex(1), file: self, relocations: None, diff --git a/src/read/elf/relocation.rs b/src/read/elf/relocation.rs index 8443dbc7..5812d50f 100644 --- a/src/read/elf/relocation.rs +++ b/src/read/elf/relocation.rs @@ -92,14 +92,22 @@ impl<'data, Elf: FileHeader> Iterator for ElfRelaIterator<'data, Elf> { } /// An iterator over the dynamic relocations for an `ElfFile32`. -pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = - ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32, R>; +pub type ElfDynamicRelocationIteratorFromFile32< + 'data, + 'file, + Endian = Endianness, + R = &'data [u8], +> = ElfDynamicRelocationIteratorFromFile<'data, 'file, elf::FileHeader32, R>; /// An iterator over the dynamic relocations for an `ElfFile64`. -pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = - ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64, R>; +pub type ElfDynamicRelocationIteratorFromFile64< + 'data, + 'file, + Endian = Endianness, + R = &'data [u8], +> = ElfDynamicRelocationIteratorFromFile<'data, 'file, elf::FileHeader64, R>; /// An iterator over the dynamic relocations for an `ElfFile`. -pub struct ElfDynamicRelocationIterator<'data, 'file, Elf, R = &'data [u8]> +pub struct ElfDynamicRelocationIteratorFromFile<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, @@ -110,7 +118,7 @@ where pub(super) relocations: Option>, } -impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIteratorFromFile<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, @@ -154,7 +162,7 @@ where } } -impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIteratorFromFile<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, @@ -164,6 +172,49 @@ where } } +/// An iterator over the dynamic relocations for an `ElfFile32`. +pub type ElfDynamicRelocationIteratorFromDynamic32<'data, 'file, Endian = Endianness> = + ElfDynamicRelocationIteratorFromDynamic<'data, 'file, elf::FileHeader32>; +/// An iterator over the dynamic relocations for an `ElfFile64`. +pub type ElfDynamicRelocationIteratorFromDynamic64<'data, 'file, Endian = Endianness> = + ElfDynamicRelocationIteratorFromDynamic<'data, 'file, elf::FileHeader64>; + +/// An iterator over the dynamic relocations for an `ElfFile`. +pub struct ElfDynamicRelocationIteratorFromDynamic<'data, 'header, Elf> +where + Elf: FileHeader, +{ + pub(super) header: &'header Elf, + pub(super) endian: Elf::Endian, + pub(super) relocations: ElfRelaIterator<'data, Elf>, +} + +impl<'data, 'file, Elf> Iterator for ElfDynamicRelocationIteratorFromDynamic<'data, 'file, Elf> +where + Elf: FileHeader, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option { + if let Some(reloc) = self.relocations.next() { + let relocation = + parse_relocation(self.header, self.endian, reloc, self.relocations.is_rel()); + return Some((reloc.r_offset(self.endian).into(), relocation)); + } + + None + } +} + +impl<'data, 'file, Elf> fmt::Debug for ElfDynamicRelocationIteratorFromDynamic<'data, 'file, Elf> +where + Elf: FileHeader, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfDynamicRelocationIterator").finish() + } +} + /// An iterator over the relocations for an `ElfSection32`. pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32, R>; diff --git a/src/read/elf/segment.rs b/src/read/elf/segment.rs index 3972731e..78ddcbe9 100644 --- a/src/read/elf/segment.rs +++ b/src/read/elf/segment.rs @@ -219,6 +219,31 @@ pub trait ProgramHeader: Debug + Pod { Ok(Some(dynamic)) } + /// Return entries in a dynamic segment in a loaded elf. + /// + /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`. + /// Returns `Err` for invalid values. + fn dynamic_loaded<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result::Dyn]>> { + if self.p_type(endian) != elf::PT_DYNAMIC { + return Ok(None); + } + let offset = self.p_vaddr(endian).into(); + let size = self.p_memsz(endian).into() as usize; + + let dynamic = data + .read_slice_at( + offset, + size / mem::size_of::<::Dyn>(), + ) + .read_error("Invalid ELF dynamic segment offset or size")?; + + Ok(Some(dynamic)) + } + /// Return a note iterator for the segment data. /// /// Returns `Ok(None)` if the segment does not contain notes. From e0c542f6652a62ffc60b4838a25c539a7b89a306 Mon Sep 17 00:00:00 2001 From: Tal Risin Date: Mon, 10 Jul 2023 22:37:33 +0300 Subject: [PATCH 4/5] cleanup POC code --- src/read/elf/dynamic.rs | 71 ----------------------------------------- 1 file changed, 71 deletions(-) diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index 81f7af8d..ddaaea27 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -525,74 +525,3 @@ fn dyn_val_into_offset(_: u64, val: u64) -> u64 { fn dyn_val_into_offset(base: u64, val: u64) -> u64 { val - base } - -// let pltrel = find_dyn_by_tag(dynamic, elf::DT_PLTREL, endian) -// .ok_or(Error("No pltrel! Can't determine between RELA and REL"))?; - -// let (dt_relsz, dt_relent) = match pltrel { -// DT_REL => (elf::DT_RELSZ, elf::DT_RELENT), -// DT_RELA => (elf::DT_RELASZ, elf::DT_RELAENT), -// _ => todo!(), -// }; - -// let dynamic_relocations_address = -// find_dyn_by_tag(data, pltrel, endian).ok_or(Error("Can't rela_dyn section!"))? as usize; -// let dynamic_relocations_offset = dynamic_relocations_address - base; - -// // Unwrap safety: according to the ELF manual, if DT_REL or DT_RELA exist, then DT_RELSZ and DT_RELENT or -// // DT_RELASZ and DT_RELAENT (accordingly) *MUST* exist. -// let dynamic_relocations_size = find_dyn_by_tag(data, dt_relsz, endian).unwrap().into(); -// let dynamic_relocation_entry_size = -// find_dyn_by_tag(data, dt_relent, endian).unwrap().into(); - -// let dynamic_plt_relocations_address = find_dyn_by_tag(data, elf::DT_JMPREL, endian) -// .ok_or(Error("Can't rela_dyn section!"))? -// .into() as usize; -// let dynamic_plt_relocations_offset = dynamic_plt_relocations_address - base; - -// // Unwrap safety: according to the ELF manual, if DT_JMPREL exists, then DT_PLTRELSZ *MUST* exist. -// let dynamic_plt_relocations_size = find_dyn_by_tag(data, elf::DT_PLTRELSZ, endian) -// .unwrap() -// .into(); - -// let dynamic_relocations = match pltrel { -// DT_REL => ElfRelaIterator::::Rel( -// data.read_slice_at( -// dynamic_relocations_offset as u64, -// dynamic_relocations_size as usize / dynamic_relocation_entry_size as usize, -// ) -// .read_error("Failed to parse dynamic relocations")? -// .iter(), -// ), -// DT_RELA => ElfRelaIterator::::Rela( -// data.read_slice_at( -// dynamic_relocations_offset as u64, -// dynamic_relocations_size as usize / dynamic_relocation_entry_size as usize, -// ) -// .read_error("Failed to parse dynamic relocations")? -// .iter(), -// ), -// _ => return Err(Error("Unexpected DT_PLTREL value!")), -// }; - -// let dynamic_plt_relocations = match pltrel { -// DT_REL => ElfRelaIterator::::Rel( -// data.read_slice_at( -// dynamic_plt_relocations_offset as u64, -// dynamic_plt_relocations_size as usize / dynamic_relocation_entry_size as usize, -// ) -// .read_error("Failed to parse dynamic relocations")? -// .iter(), -// ), -// DT_RELA => ElfRelaIterator::::Rela( -// data.read_slice_at( -// dynamic_plt_relocations_offset as u64, -// dynamic_plt_relocations_size as usize / dynamic_relocation_entry_size as usize, -// ) -// .read_error("Failed to parse dynamic relocations")? -// .iter(), -// ), -// _ => return Err(Error("Unexpected DT_PLTREL value!")), -// }; - -// let all_relocations = dynamic_relocations.chain(dynamic_plt_relocations); From 93b3c89f5829e14528911dd78fd33dc9face0893 Mon Sep 17 00:00:00 2001 From: Tal Risin Date: Mon, 10 Jul 2023 23:05:50 +0300 Subject: [PATCH 5/5] documentation --- src/read/elf/dynamic.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index ddaaea27..0843db30 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -179,6 +179,14 @@ where R: ReadRef<'data> + 'data, { /// Parse the dynamic table of a static elf. + /// Example usage: + /// ```no_run + /// let file = std::fs::read("").unwrap(); + /// let file = file.as_slice(); + /// let elf = ElfFile64::::parse(file).unwrap(); + /// let dynamic = Dynamic::new(elf.raw_header(), file) + /// .unwrap(); + /// ``` pub fn new(elf: &'data Elf, data: R) -> read::Result { // Since this elf is not loaded, the addresses should contain offsets into the elf, thus base is 0. let endian = elf.endian()?; @@ -194,6 +202,8 @@ where /// Parse the dynamic table of a loaded elf. /// `base` should point to the base address of the elf, and will be used to convert absolute memory addresses into /// offsets into the elf. + /// `dynamic` is optional, as it can be derived from the header, or by the caller (for example using + /// `dl_iterate_phdr`). pub fn new_loaded( base: usize, header: &'data Elf,