summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/elf/dynamic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/goblin/src/elf/dynamic.rs')
-rw-r--r--third_party/rust/goblin/src/elf/dynamic.rs807
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
+ );
+}