diff options
Diffstat (limited to 'third_party/rust/goblin/src/elf/dynamic.rs')
-rw-r--r-- | third_party/rust/goblin/src/elf/dynamic.rs | 807 |
1 files changed, 807 insertions, 0 deletions
diff --git a/third_party/rust/goblin/src/elf/dynamic.rs b/third_party/rust/goblin/src/elf/dynamic.rs new file mode 100644 index 0000000000..c410469c6a --- /dev/null +++ b/third_party/rust/goblin/src/elf/dynamic.rs @@ -0,0 +1,807 @@ +macro_rules! elf_dyn { + ($size:ty) => { + // XXX: Do not import scroll traits here. + // See: https://github.com/rust-lang/rust/issues/65090#issuecomment-538668155 + + #[repr(C)] + #[derive(Copy, Clone, PartialEq, Default)] + #[cfg_attr( + feature = "alloc", + derive(scroll::Pread, scroll::Pwrite, scroll::SizeWith) + )] + /// An entry in the dynamic array + pub struct Dyn { + /// Dynamic entry type + pub d_tag: $size, + /// Integer value + pub d_val: $size, + } + + use plain; + unsafe impl plain::Plain for Dyn {} + }; +} + +// TODO: figure out what's the best, most friendly + safe API choice here - u32s or u64s +// remember that DT_TAG is "pointer sized"/used as address sometimes Original rationale: I +// decided to use u64 instead of u32 due to pattern matching use case seems safer to cast the +// elf32's d_tag from u32 -> u64 at runtime instead of casting the elf64's d_tag from u64 -> +// u32 at runtime + +/// Marks end of dynamic section +pub const DT_NULL: u64 = 0; +/// Name of needed library +pub const DT_NEEDED: u64 = 1; +/// Size in bytes of PLT relocs +pub const DT_PLTRELSZ: u64 = 2; +/// Processor defined value +pub const DT_PLTGOT: u64 = 3; +/// Address of symbol hash table +pub const DT_HASH: u64 = 4; +/// Address of string table +pub const DT_STRTAB: u64 = 5; +/// Address of symbol table +pub const DT_SYMTAB: u64 = 6; +/// Address of Rela relocs +pub const DT_RELA: u64 = 7; +/// Total size of Rela relocs +pub const DT_RELASZ: u64 = 8; +/// Size of one Rela reloc +pub const DT_RELAENT: u64 = 9; +/// Size of string table +pub const DT_STRSZ: u64 = 10; +/// Size of one symbol table entry +pub const DT_SYMENT: u64 = 11; +/// Address of init function +pub const DT_INIT: u64 = 12; +/// Address of termination function +pub const DT_FINI: u64 = 13; +/// Name of shared object +pub const DT_SONAME: u64 = 14; +/// Library search path (deprecated) +pub const DT_RPATH: u64 = 15; +/// Start symbol search here +pub const DT_SYMBOLIC: u64 = 16; +/// Address of Rel relocs +pub const DT_REL: u64 = 17; +/// Total size of Rel relocs +pub const DT_RELSZ: u64 = 18; +/// Size of one Rel reloc +pub const DT_RELENT: u64 = 19; +/// Type of reloc in PLT +pub const DT_PLTREL: u64 = 20; +/// For debugging; unspecified +pub const DT_DEBUG: u64 = 21; +/// Reloc might modify .text +pub const DT_TEXTREL: u64 = 22; +/// Address of PLT relocs +pub const DT_JMPREL: u64 = 23; +/// Process relocations of object +pub const DT_BIND_NOW: u64 = 24; +/// Array with addresses of init fct +pub const DT_INIT_ARRAY: u64 = 25; +/// Array with addresses of fini fct +pub const DT_FINI_ARRAY: u64 = 26; +/// Size in bytes of DT_INIT_ARRAY +pub const DT_INIT_ARRAYSZ: u64 = 27; +/// Size in bytes of DT_FINI_ARRAY +pub const DT_FINI_ARRAYSZ: u64 = 28; +/// Library search path +pub const DT_RUNPATH: u64 = 29; +/// Flags for the object being loaded +pub const DT_FLAGS: u64 = 30; +/// Start of encoded range +pub const DT_ENCODING: u64 = 32; +/// Array with addresses of preinit fct +pub const DT_PREINIT_ARRAY: u64 = 32; +/// size in bytes of DT_PREINIT_ARRAY +pub const DT_PREINIT_ARRAYSZ: u64 = 33; +/// Number used +pub const DT_NUM: u64 = 34; +/// Start of OS-specific +pub const DT_LOOS: u64 = 0x6000_000d; +/// End of OS-specific +pub const DT_HIOS: u64 = 0x6fff_f000; +/// Start of processor-specific +pub const DT_LOPROC: u64 = 0x7000_0000; +/// End of processor-specific +pub const DT_HIPROC: u64 = 0x7fff_ffff; +// Most used by any processor +// pub const DT_PROCNUM: u64 = DT_MIPS_NUM; + +/// DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the +/// Dyn.d_un.d_ptr field of the Elf*_Dyn structure. +/// +/// If any adjustment is made to the ELF object after it has been +/// built these entries will need to be adjusted. +pub const DT_ADDRRNGLO: u64 = 0x6fff_fe00; +/// GNU-style hash table +pub const DT_GNU_HASH: u64 = 0x6fff_fef5; +/// +pub const DT_TLSDESC_PLT: u64 = 0x6fff_fef6; +/// +pub const DT_TLSDESC_GOT: u64 = 0x6fff_fef7; +/// Start of conflict section +pub const DT_GNU_CONFLICT: u64 = 0x6fff_fef8; +/// Library list +pub const DT_GNU_LIBLIST: u64 = 0x6fff_fef9; +/// Configuration information +pub const DT_CONFIG: u64 = 0x6fff_fefa; +/// Dependency auditing +pub const DT_DEPAUDIT: u64 = 0x6fff_fefb; +/// Object auditing +pub const DT_AUDIT: u64 = 0x6fff_fefc; +/// PLT padding +pub const DT_PLTPAD: u64 = 0x6fff_fefd; +/// Move table +pub const DT_MOVETAB: u64 = 0x6fff_fefe; +/// Syminfo table +pub const DT_SYMINFO: u64 = 0x6fff_feff; +/// +pub const DT_ADDRRNGHI: u64 = 0x6fff_feff; + +//DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +pub const DT_ADDRNUM: u64 = 11; + +/// The versioning entry types. The next are defined as part of the GNU extension +pub const DT_VERSYM: u64 = 0x6fff_fff0; +pub const DT_RELACOUNT: u64 = 0x6fff_fff9; +pub const DT_RELCOUNT: u64 = 0x6fff_fffa; +/// State flags, see DF_1_* below +pub const DT_FLAGS_1: u64 = 0x6fff_fffb; +/// Address of version definition table +pub const DT_VERDEF: u64 = 0x6fff_fffc; +/// Number of version definitions +pub const DT_VERDEFNUM: u64 = 0x6fff_fffd; +/// Address of table with needed versions +pub const DT_VERNEED: u64 = 0x6fff_fffe; +/// Number of needed versions +pub const DT_VERNEEDNUM: u64 = 0x6fff_ffff; + +/// Converts a tag to its string representation. +#[inline] +pub fn tag_to_str(tag: u64) -> &'static str { + match tag { + DT_NULL => "DT_NULL", + DT_NEEDED => "DT_NEEDED", + DT_PLTRELSZ => "DT_PLTRELSZ", + DT_PLTGOT => "DT_PLTGOT", + DT_HASH => "DT_HASH", + DT_STRTAB => "DT_STRTAB", + DT_SYMTAB => "DT_SYMTAB", + DT_RELA => "DT_RELA", + DT_RELASZ => "DT_RELASZ", + DT_RELAENT => "DT_RELAENT", + DT_STRSZ => "DT_STRSZ", + DT_SYMENT => "DT_SYMENT", + DT_INIT => "DT_INIT", + DT_FINI => "DT_FINI", + DT_SONAME => "DT_SONAME", + DT_RPATH => "DT_RPATH", + DT_SYMBOLIC => "DT_SYMBOLIC", + DT_REL => "DT_REL", + DT_RELSZ => "DT_RELSZ", + DT_RELENT => "DT_RELENT", + DT_PLTREL => "DT_PLTREL", + DT_DEBUG => "DT_DEBUG", + DT_TEXTREL => "DT_TEXTREL", + DT_JMPREL => "DT_JMPREL", + DT_BIND_NOW => "DT_BIND_NOW", + DT_INIT_ARRAY => "DT_INIT_ARRAY", + DT_FINI_ARRAY => "DT_FINI_ARRAY", + DT_INIT_ARRAYSZ => "DT_INIT_ARRAYSZ", + DT_FINI_ARRAYSZ => "DT_FINI_ARRAYSZ", + DT_RUNPATH => "DT_RUNPATH", + DT_FLAGS => "DT_FLAGS", + DT_PREINIT_ARRAY => "DT_PREINIT_ARRAY", + DT_PREINIT_ARRAYSZ => "DT_PREINIT_ARRAYSZ", + DT_NUM => "DT_NUM", + DT_LOOS => "DT_LOOS", + DT_HIOS => "DT_HIOS", + DT_LOPROC => "DT_LOPROC", + DT_HIPROC => "DT_HIPROC", + DT_VERSYM => "DT_VERSYM", + DT_RELACOUNT => "DT_RELACOUNT", + DT_RELCOUNT => "DT_RELCOUNT", + DT_GNU_HASH => "DT_GNU_HASH", + DT_VERDEF => "DT_VERDEF", + DT_VERDEFNUM => "DT_VERDEFNUM", + DT_VERNEED => "DT_VERNEED", + DT_VERNEEDNUM => "DT_VERNEEDNUM", + DT_FLAGS_1 => "DT_FLAGS_1", + _ => "UNKNOWN_TAG", + } +} + +// Values of `d_un.d_val` in the DT_FLAGS entry +/// Object may use DF_ORIGIN. +pub const DF_ORIGIN: u64 = 0x0000_0001; +/// Symbol resolutions starts here. +pub const DF_SYMBOLIC: u64 = 0x0000_0002; +/// Object contains text relocations. +pub const DF_TEXTREL: u64 = 0x0000_0004; +/// No lazy binding for this object. +pub const DF_BIND_NOW: u64 = 0x0000_0008; +/// Module uses the static TLS model. +pub const DF_STATIC_TLS: u64 = 0x0000_0010; + +pub fn df_tag_to_str(tag: u64) -> &'static str { + match tag { + DF_ORIGIN => "DF_ORIGIN", + DF_SYMBOLIC => "DF_SYMBOLIC", + DF_TEXTREL => "DF_TEXTREL", + DF_BIND_NOW => "DF_BIND_NOW", + DF_STATIC_TLS => "DF_STATIC_TLS", + _ => "UNKNOWN_TAG", + } +} + +/// === State flags === +/// selectable in the `d_un.d_val` element of the DT_FLAGS_1 entry in the dynamic section. +/// +/// Set RTLD_NOW for this object. +pub const DF_1_NOW: u64 = 0x0000_0001; +/// Set RTLD_GLOBAL for this object. +pub const DF_1_GLOBAL: u64 = 0x0000_0002; +/// Set RTLD_GROUP for this object. +pub const DF_1_GROUP: u64 = 0x0000_0004; +/// Set RTLD_NODELETE for this object. +pub const DF_1_NODELETE: u64 = 0x0000_0008; +/// Trigger filtee loading at runtime. +pub const DF_1_LOADFLTR: u64 = 0x0000_0010; +/// Set RTLD_INITFIRST for this object. +pub const DF_1_INITFIRST: u64 = 0x0000_0020; +/// Set RTLD_NOOPEN for this object. +pub const DF_1_NOOPEN: u64 = 0x0000_0040; +/// $ORIGIN must be handled. +pub const DF_1_ORIGIN: u64 = 0x0000_0080; +/// Direct binding enabled. +pub const DF_1_DIRECT: u64 = 0x0000_0100; +pub const DF_1_TRANS: u64 = 0x0000_0200; +/// Object is used to interpose. +pub const DF_1_INTERPOSE: u64 = 0x0000_0400; +/// Ignore default lib search path. +pub const DF_1_NODEFLIB: u64 = 0x0000_0800; +/// Object can't be dldump'ed. +pub const DF_1_NODUMP: u64 = 0x0000_1000; +/// Configuration alternative created. +pub const DF_1_CONFALT: u64 = 0x0000_2000; +/// Filtee terminates filters search. +pub const DF_1_ENDFILTEE: u64 = 0x0000_4000; +/// Disp reloc applied at build time. +pub const DF_1_DISPRELDNE: u64 = 0x0000_8000; +/// Disp reloc applied at run-time. +pub const DF_1_DISPRELPND: u64 = 0x0001_0000; +/// Object has no-direct binding. +pub const DF_1_NODIRECT: u64 = 0x0002_0000; +pub const DF_1_IGNMULDEF: u64 = 0x0004_0000; +pub const DF_1_NOKSYMS: u64 = 0x0008_0000; +pub const DF_1_NOHDR: u64 = 0x0010_0000; +/// Object is modified after built. +pub const DF_1_EDITED: u64 = 0x0020_0000; +pub const DF_1_NORELOC: u64 = 0x0040_0000; +/// Object has individual interposers. +pub const DF_1_SYMINTPOSE: u64 = 0x0080_0000; +/// Global auditing required. +pub const DF_1_GLOBAUDIT: u64 = 0x0100_0000; +/// Singleton dyn are used. +pub const DF_1_SINGLETON: u64 = 0x0200_0000; +/// Object is a Position Independent Executable (PIE). +pub const DF_1_PIE: u64 = 0x0800_0000; + +pub fn df_1_tag_to_str(tag: u64) -> &'static str { + match tag { + DF_1_NOW => "DF_1_NOW", + DF_1_GLOBAL => "DF_1_GLOBAL", + DF_1_GROUP => "DF_1_GROUP", + DF_1_NODELETE => "DF_1_NODELETE", + DF_1_LOADFLTR => "DF_1_LOADFLTR", + DF_1_INITFIRST => "DF_1_INITFIRST", + DF_1_NOOPEN => "DF_1_NOOPEN", + DF_1_ORIGIN => "DF_1_ORIGIN", + DF_1_DIRECT => "DF_1_DIRECT", + DF_1_TRANS => "DF_1_TRANS", + DF_1_INTERPOSE => "DF_1_INTERPOSE", + DF_1_NODEFLIB => "DF_1_NODEFLIB", + DF_1_NODUMP => "DF_1_NODUMP", + DF_1_CONFALT => "DF_1_CONFALT", + DF_1_ENDFILTEE => "DF_1_ENDFILTEE", + DF_1_DISPRELDNE => "DF_1_DISPRELDNE", + DF_1_DISPRELPND => "DF_1_DISPRELPND", + DF_1_NODIRECT => "DF_1_NODIRECT", + DF_1_IGNMULDEF => "DF_1_IGNMULDEF", + DF_1_NOKSYMS => "DF_1_NOKSYMS", + DF_1_NOHDR => "DF_1_NOHDR", + DF_1_EDITED => "DF_1_EDITED", + DF_1_NORELOC => "DF_1_NORELOC", + DF_1_SYMINTPOSE => "DF_1_SYMINTPOSE", + DF_1_GLOBAUDIT => "DF_1_GLOBAUDIT", + DF_1_SINGLETON => "DF_1_SINGLETON", + DF_1_PIE => "DF_1_PIE", + _ => "UNKNOWN_TAG", + } +} + +if_alloc! { + use core::fmt; + use scroll::ctx; + use core::result; + use crate::container::{Ctx, Container}; + use crate::strtab::Strtab; + use alloc::vec::Vec; + + #[derive(Default, PartialEq, Clone)] + pub struct Dyn { + pub d_tag: u64, + pub d_val: u64, + } + + impl Dyn { + #[inline] + pub fn size(container: Container) -> usize { + use scroll::ctx::SizeWith; + Self::size_with(&Ctx::from(container)) + } + } + + impl fmt::Debug for Dyn { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Dyn") + .field("d_tag", &tag_to_str(self.d_tag)) + .field("d_val", &format_args!("0x{:x}", self.d_val)) + .finish() + } + } + + impl ctx::SizeWith<Ctx> for Dyn { + fn size_with(&Ctx { container, .. }: &Ctx) -> usize { + match container { + Container::Little => { + dyn32::SIZEOF_DYN + }, + Container::Big => { + dyn64::SIZEOF_DYN + }, + } + } + } + + impl<'a> ctx::TryFromCtx<'a, Ctx> for Dyn { + type Error = crate::error::Error; + fn try_from_ctx(bytes: &'a [u8], Ctx { container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> { + use scroll::Pread; + let dynamic = match container { + Container::Little => { + (bytes.pread_with::<dyn32::Dyn>(0, le)?.into(), dyn32::SIZEOF_DYN) + }, + Container::Big => { + (bytes.pread_with::<dyn64::Dyn>(0, le)?.into(), dyn64::SIZEOF_DYN) + } + }; + Ok(dynamic) + } + } + + impl ctx::TryIntoCtx<Ctx> for Dyn { + type Error = crate::error::Error; + fn try_into_ctx(self, bytes: &mut [u8], Ctx { container, le}: Ctx) -> result::Result<usize, Self::Error> { + use scroll::Pwrite; + match container { + Container::Little => { + let dynamic: dyn32::Dyn = self.into(); + Ok(bytes.pwrite_with(dynamic, 0, le)?) + }, + Container::Big => { + let dynamic: dyn64::Dyn = self.into(); + Ok(bytes.pwrite_with(dynamic, 0, le)?) + } + } + } + } + + #[derive(Debug)] + pub struct Dynamic { + pub dyns: Vec<Dyn>, + pub info: DynamicInfo, + } + + impl Dynamic { + #[cfg(feature = "endian_fd")] + /// Returns a vector of dynamic entries from the underlying byte `bytes`, with `endianness`, using the provided `phdrs` + pub fn parse(bytes: &[u8], phdrs: &[crate::elf::program_header::ProgramHeader], ctx: Ctx) -> crate::error::Result<Option<Self>> { + use scroll::ctx::SizeWith; + use scroll::Pread; + use crate::elf::program_header; + for phdr in phdrs { + if phdr.p_type == program_header::PT_DYNAMIC { + let offset = phdr.p_offset as usize; + let filesz = phdr.p_filesz as usize; + // Ensure offset and filesz are valid. + let bytes = if filesz > 0 { + bytes + .pread_with::<&[u8]>(offset, filesz) + .map_err(|_| crate::error::Error::Malformed(format!("Invalid PT_DYNAMIC size (offset {:#x}, filesz {:#x})", + offset, filesz)))? + } else { + &[] + }; + let size = Dyn::size_with(&ctx); + // the validity of `count` was implicitly checked by reading `bytes`. + let count = filesz / size; + let mut dyns = Vec::with_capacity(count); + let mut offset = 0; + for _ in 0..count { + let dynamic = bytes.gread_with::<Dyn>(&mut offset, ctx)?; + let tag = dynamic.d_tag; + dyns.push(dynamic); + if tag == DT_NULL { break } + } + let mut info = DynamicInfo::default(); + for dynamic in &dyns { + info.update(phdrs, dynamic); + } + return Ok(Some(Dynamic { dyns: dyns, info: info, })); + } + } + Ok(None) + } + + pub fn get_libraries<'a>(&self, strtab: &Strtab<'a>) -> Vec<&'a str> { + use log::warn; + let count = self.info.needed_count.min(self.dyns.len()); + let mut needed = Vec::with_capacity(count); + for dynamic in &self.dyns { + if dynamic.d_tag as u64 == DT_NEEDED { + if let Some(lib) = strtab.get_at(dynamic.d_val as usize) { + needed.push(lib) + } else { + warn!("Invalid DT_NEEDED {}", dynamic.d_val) + } + } + } + needed + } + } +} + +macro_rules! elf_dyn_std_impl { + ($size:ident, $phdr:ty) => { + + #[cfg(test)] + mod tests { + use super::*; + #[test] + fn size_of() { + assert_eq!(::std::mem::size_of::<Dyn>(), SIZEOF_DYN); + } + } + + if_alloc! { + use core::fmt; + use core::slice; + use alloc::vec::Vec; + + use crate::elf::program_header::{PT_DYNAMIC}; + use crate::strtab::Strtab; + + use crate::elf::dynamic::Dyn as ElfDyn; + + if_std! { + use std::fs::File; + use std::io::{Read, Seek}; + use std::io::SeekFrom::Start; + use crate::error::Result; + } + + impl From<ElfDyn> for Dyn { + fn from(dynamic: ElfDyn) -> Self { + Dyn { + d_tag: dynamic.d_tag as $size, + d_val: dynamic.d_val as $size, + } + } + } + impl From<Dyn> for ElfDyn { + fn from(dynamic: Dyn) -> Self { + ElfDyn { + d_tag: u64::from(dynamic.d_tag), + d_val: u64::from(dynamic.d_val), + } + } + } + + impl fmt::Debug for Dyn { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Dyn") + .field("d_tag", &tag_to_str(u64::from(self.d_tag))) + .field("d_val", &format_args!("0x{:x}", self.d_val)) + .finish() + } + } + + /// Returns a vector of dynamic entries from the given fd and program headers + #[cfg(feature = "std")] + pub fn from_fd(mut fd: &File, phdrs: &[$phdr]) -> Result<Option<Vec<Dyn>>> { + for phdr in phdrs { + if phdr.p_type == PT_DYNAMIC { + // FIXME: validate filesz before allocating + let filesz = phdr.p_filesz as usize; + let dync = filesz / SIZEOF_DYN; + let mut dyns = vec![Dyn::default(); dync]; + fd.seek(Start(u64::from(phdr.p_offset)))?; + unsafe { + fd.read_exact(plain::as_mut_bytes(&mut *dyns))?; + } + dyns.dedup(); + return Ok(Some(dyns)); + } + } + Ok(None) + } + + /// Given a bias and a memory address (typically for a _correctly_ mmap'd binary in memory), returns the `_DYNAMIC` array as a slice of that memory + pub unsafe fn from_raw<'a>(bias: usize, vaddr: usize) -> &'a [Dyn] { + let dynp = vaddr.wrapping_add(bias) as *const Dyn; + let mut idx = 0; + while u64::from((*dynp.offset(idx)).d_tag) != DT_NULL { + idx += 1; + } + slice::from_raw_parts(dynp, idx as usize) + } + + // TODO: these bare functions have always seemed awkward, but not sure where they should go... + /// Maybe gets and returns the dynamic array with the same lifetime as the [phdrs], using the provided bias with wrapping addition. + /// If the bias is wrong, it will either segfault or give you incorrect values, beware + pub unsafe fn from_phdrs(bias: usize, phdrs: &[$phdr]) -> Option<&[Dyn]> { + for phdr in phdrs { + // FIXME: change to casting to u64 similar to DT_*? + if phdr.p_type as u32 == PT_DYNAMIC { + return Some(from_raw(bias, phdr.p_vaddr as usize)); + } + } + None + } + + /// Gets the needed libraries from the `_DYNAMIC` array, with the str slices lifetime tied to the dynamic array/strtab's lifetime(s) + pub unsafe fn get_needed<'a>(dyns: &[Dyn], strtab: *const Strtab<'a>, count: usize) -> Vec<&'a str> { + let mut needed = Vec::with_capacity(count.min(dyns.len())); + for dynamic in dyns { + if u64::from(dynamic.d_tag) == DT_NEEDED { + let lib = &(*strtab)[dynamic.d_val as usize]; + needed.push(lib); + } + } + needed + } + } + }; +} + +macro_rules! elf_dynamic_info_std_impl { + ($size:ident, $phdr:ty) => { + /// Convert a virtual memory address to a file offset + fn vm_to_offset(phdrs: &[$phdr], address: $size) -> Option<$size> { + for ph in phdrs { + if ph.p_type == crate::elf::program_header::PT_LOAD && address >= ph.p_vaddr { + let offset = address - ph.p_vaddr; + if offset < ph.p_memsz { + return ph.p_offset.checked_add(offset); + } + } + } + None + } + + /// Important dynamic linking info generated via a single pass through the `_DYNAMIC` array + #[derive(Default, PartialEq)] + pub struct DynamicInfo { + pub rela: usize, + pub relasz: usize, + pub relaent: $size, + pub relacount: usize, + pub rel: usize, + pub relsz: usize, + pub relent: $size, + pub relcount: usize, + pub gnu_hash: Option<$size>, + pub hash: Option<$size>, + pub strtab: usize, + pub strsz: usize, + pub symtab: usize, + pub syment: usize, + pub pltgot: Option<$size>, + pub pltrelsz: usize, + pub pltrel: $size, + pub jmprel: usize, + pub verdef: $size, + pub verdefnum: $size, + pub verneed: $size, + pub verneednum: $size, + pub versym: $size, + pub init: $size, + pub fini: $size, + pub init_array: $size, + pub init_arraysz: usize, + pub fini_array: $size, + pub fini_arraysz: usize, + pub needed_count: usize, + pub flags: $size, + pub flags_1: $size, + pub soname: usize, + pub textrel: bool, + } + + impl DynamicInfo { + #[inline] + pub fn update(&mut self, phdrs: &[$phdr], dynamic: &Dyn) { + match u64::from(dynamic.d_tag) { + DT_RELA => self.rela = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rela.dyn + DT_RELASZ => self.relasz = dynamic.d_val as usize, + DT_RELAENT => self.relaent = dynamic.d_val as _, + DT_RELACOUNT => self.relacount = dynamic.d_val as usize, + DT_REL => self.rel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rel.dyn + DT_RELSZ => self.relsz = dynamic.d_val as usize, + DT_RELENT => self.relent = dynamic.d_val as _, + DT_RELCOUNT => self.relcount = dynamic.d_val as usize, + DT_GNU_HASH => self.gnu_hash = vm_to_offset(phdrs, dynamic.d_val), + DT_HASH => self.hash = vm_to_offset(phdrs, dynamic.d_val), + DT_STRTAB => { + self.strtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize + } + DT_STRSZ => self.strsz = dynamic.d_val as usize, + DT_SYMTAB => { + self.symtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize + } + DT_SYMENT => self.syment = dynamic.d_val as usize, + DT_PLTGOT => self.pltgot = vm_to_offset(phdrs, dynamic.d_val), + DT_PLTRELSZ => self.pltrelsz = dynamic.d_val as usize, + DT_PLTREL => self.pltrel = dynamic.d_val as _, + DT_JMPREL => { + self.jmprel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize + } // .rela.plt + DT_VERDEF => self.verdef = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_VERDEFNUM => self.verdefnum = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_VERNEED => self.verneed = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_VERNEEDNUM => self.verneednum = dynamic.d_val as _, + DT_VERSYM => self.versym = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_INIT => self.init = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_FINI => self.fini = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_INIT_ARRAY => { + self.init_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) + } + DT_INIT_ARRAYSZ => self.init_arraysz = dynamic.d_val as _, + DT_FINI_ARRAY => { + self.fini_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) + } + DT_FINI_ARRAYSZ => self.fini_arraysz = dynamic.d_val as _, + DT_NEEDED => self.needed_count += 1, + DT_FLAGS => self.flags = dynamic.d_val as _, + DT_FLAGS_1 => self.flags_1 = dynamic.d_val as _, + DT_SONAME => self.soname = dynamic.d_val as _, + DT_TEXTREL => self.textrel = true, + _ => (), + } + } + pub fn new(dynamic: &[Dyn], phdrs: &[$phdr]) -> DynamicInfo { + let mut info = DynamicInfo::default(); + for dyna in dynamic { + info.update(phdrs, &dyna); + } + info + } + } + + if_alloc! { + impl fmt::Debug for DynamicInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let gnu_hash = self.gnu_hash.unwrap_or(0); + let hash = self.hash.unwrap_or(0); + let pltgot = self.pltgot.unwrap_or(0); + + let flags: Vec<&'static str> = [DF_ORIGIN, DF_SYMBOLIC, DF_TEXTREL, DF_BIND_NOW, DF_STATIC_TLS,][..] + .iter() + .filter(|f| (self.flags as u64 & *f) != 0) + .map(|f| df_tag_to_str(*f)) + .collect(); + + let flags_1: Vec<&'static str> = [ + DF_1_NOW, + DF_1_GLOBAL, + DF_1_GROUP, + DF_1_NODELETE, + DF_1_LOADFLTR, + DF_1_INITFIRST, + DF_1_NOOPEN, + DF_1_ORIGIN, + DF_1_DIRECT, + DF_1_TRANS, + DF_1_INTERPOSE, + DF_1_NODEFLIB, + DF_1_NODUMP, + DF_1_CONFALT, + DF_1_ENDFILTEE, + DF_1_DISPRELDNE, + DF_1_DISPRELPND, + DF_1_NODIRECT, + DF_1_IGNMULDEF, + DF_1_NOKSYMS, + DF_1_NOHDR, + DF_1_EDITED, + DF_1_NORELOC, + DF_1_SYMINTPOSE, + DF_1_GLOBAUDIT, + DF_1_SINGLETON, + DF_1_PIE, + ][..] + .iter() + .filter(|f| (self.flags_1 as u64 & *f) != 0) + .map(|f| df_1_tag_to_str(*f)) + .collect(); + + f.debug_struct("DynamicInfo") + .field("rela", &format_args!("0x{:x}", self.rela)) + .field("relasz", &self.relasz) + .field("relaent", &self.relaent) + .field("relacount", &self.relacount) + .field("gnu_hash", &format_args!("0x{:x}", gnu_hash)) + .field("hash", &format_args!("0x{:x}", hash)) + .field("strtab", &format_args!("0x{:x}", self.strtab)) + .field("strsz", &self.strsz) + .field("symtab", &format_args!("0x{:x}", self.symtab)) + .field("syment", &self.syment) + .field("pltgot", &format_args!("0x{:x}", pltgot)) + .field("pltrelsz", &self.pltrelsz) + .field("pltrel", &self.pltrel) + .field("jmprel", &format_args!("0x{:x}", self.jmprel)) + .field("verdef", &format_args!("0x{:x}", self.verdef)) + .field("verdefnum", &self.verdefnum) + .field("verneed", &format_args!("0x{:x}", self.verneed)) + .field("verneednum", &self.verneednum) + .field("versym", &format_args!("0x{:x}", self.versym)) + .field("init", &format_args!("0x{:x}", self.init)) + .field("fini", &format_args!("0x{:x}", self.fini)) + .field("init_array", &format_args!("{:#x}", self.init_array)) + .field("init_arraysz", &self.init_arraysz) + .field("needed_count", &self.needed_count) + .field("flags", &format_args!("{:#0width$x} {:?}", self.flags, flags, width = core::mem::size_of_val(&self.flags))) + .field("flags_1", &format_args!("{:#0width$x} {:?}", self.flags_1, flags_1, width = core::mem::size_of_val(&self.flags_1))) + .field("soname", &self.soname) + .field("textrel", &self.textrel) + .finish() + } + } + } + }; +} + +if_alloc! { + elf_dynamic_info_std_impl!(u64, crate::elf::program_header::ProgramHeader); +} + +pub mod dyn32 { + pub use crate::elf::dynamic::*; + + elf_dyn!(u32); + + pub const SIZEOF_DYN: usize = 8; + + elf_dyn_std_impl!(u32, crate::elf32::program_header::ProgramHeader); + elf_dynamic_info_std_impl!( + u32, + crate::elf::program_header::program_header32::ProgramHeader + ); +} + +pub mod dyn64 { + pub use crate::elf::dynamic::*; + + elf_dyn!(u64); + + pub const SIZEOF_DYN: usize = 16; + + elf_dyn_std_impl!(u64, crate::elf64::program_header::ProgramHeader); + elf_dynamic_info_std_impl!( + u64, + crate::elf::program_header::program_header64::ProgramHeader + ); +} |