| 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 |
| ); |
| } |