1 macro_rules! elf_dyn {
2     ($size:ty) => {
3         // XXX: Do not import scroll traits here.
4         // See: https://github.com/rust-lang/rust/issues/65090#issuecomment-538668155
5 
6         #[repr(C)]
7         #[derive(Copy, Clone, PartialEq, Default)]
8         #[cfg_attr(
9             feature = "alloc",
10             derive(scroll::Pread, scroll::Pwrite, scroll::SizeWith)
11         )]
12         /// An entry in the dynamic array
13         pub struct Dyn {
14             /// Dynamic entry type
15             pub d_tag: $size,
16             /// Integer value
17             pub d_val: $size,
18         }
19 
20         use plain;
21         unsafe impl plain::Plain for Dyn {}
22     };
23 }
24 
25 // TODO: figure out what's the best, most friendly + safe API choice here - u32s or u64s
26 // remember that DT_TAG is "pointer sized"/used as address sometimes Original rationale: I
27 // decided to use u64 instead of u32 due to pattern matching use case seems safer to cast the
28 // elf32's d_tag from u32 -> u64 at runtime instead of casting the elf64's d_tag from u64 ->
29 // u32 at runtime
30 
31 /// Marks end of dynamic section
32 pub const DT_NULL: u64 = 0;
33 /// Name of needed library
34 pub const DT_NEEDED: u64 = 1;
35 /// Size in bytes of PLT relocs
36 pub const DT_PLTRELSZ: u64 = 2;
37 /// Processor defined value
38 pub const DT_PLTGOT: u64 = 3;
39 /// Address of symbol hash table
40 pub const DT_HASH: u64 = 4;
41 /// Address of string table
42 pub const DT_STRTAB: u64 = 5;
43 /// Address of symbol table
44 pub const DT_SYMTAB: u64 = 6;
45 /// Address of Rela relocs
46 pub const DT_RELA: u64 = 7;
47 /// Total size of Rela relocs
48 pub const DT_RELASZ: u64 = 8;
49 /// Size of one Rela reloc
50 pub const DT_RELAENT: u64 = 9;
51 /// Size of string table
52 pub const DT_STRSZ: u64 = 10;
53 /// Size of one symbol table entry
54 pub const DT_SYMENT: u64 = 11;
55 /// Address of init function
56 pub const DT_INIT: u64 = 12;
57 /// Address of termination function
58 pub const DT_FINI: u64 = 13;
59 /// Name of shared object
60 pub const DT_SONAME: u64 = 14;
61 /// Library search path (deprecated)
62 pub const DT_RPATH: u64 = 15;
63 /// Start symbol search here
64 pub const DT_SYMBOLIC: u64 = 16;
65 /// Address of Rel relocs
66 pub const DT_REL: u64 = 17;
67 /// Total size of Rel relocs
68 pub const DT_RELSZ: u64 = 18;
69 /// Size of one Rel reloc
70 pub const DT_RELENT: u64 = 19;
71 /// Type of reloc in PLT
72 pub const DT_PLTREL: u64 = 20;
73 /// For debugging; unspecified
74 pub const DT_DEBUG: u64 = 21;
75 /// Reloc might modify .text
76 pub const DT_TEXTREL: u64 = 22;
77 /// Address of PLT relocs
78 pub const DT_JMPREL: u64 = 23;
79 /// Process relocations of object
80 pub const DT_BIND_NOW: u64 = 24;
81 /// Array with addresses of init fct
82 pub const DT_INIT_ARRAY: u64 = 25;
83 /// Array with addresses of fini fct
84 pub const DT_FINI_ARRAY: u64 = 26;
85 /// Size in bytes of DT_INIT_ARRAY
86 pub const DT_INIT_ARRAYSZ: u64 = 27;
87 /// Size in bytes of DT_FINI_ARRAY
88 pub const DT_FINI_ARRAYSZ: u64 = 28;
89 /// Library search path
90 pub const DT_RUNPATH: u64 = 29;
91 /// Flags for the object being loaded
92 pub const DT_FLAGS: u64 = 30;
93 /// Start of encoded range
94 pub const DT_ENCODING: u64 = 32;
95 /// Array with addresses of preinit fct
96 pub const DT_PREINIT_ARRAY: u64 = 32;
97 /// size in bytes of DT_PREINIT_ARRAY
98 pub const DT_PREINIT_ARRAYSZ: u64 = 33;
99 /// Number used
100 pub const DT_NUM: u64 = 34;
101 /// Start of OS-specific
102 pub const DT_LOOS: u64 = 0x6000_000d;
103 /// End of OS-specific
104 pub const DT_HIOS: u64 = 0x6fff_f000;
105 /// Start of processor-specific
106 pub const DT_LOPROC: u64 = 0x7000_0000;
107 /// End of processor-specific
108 pub const DT_HIPROC: u64 = 0x7fff_ffff;
109 // Most used by any processor
110 // pub const DT_PROCNUM: u64 = DT_MIPS_NUM;
111 
112 /// DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
113 /// Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
114 ///
115 /// If any adjustment is made to the ELF object after it has been
116 /// built these entries will need to be adjusted.
117 pub const DT_ADDRRNGLO: u64 = 0x6fff_fe00;
118 /// GNU-style hash table
119 pub const DT_GNU_HASH: u64 = 0x6fff_fef5;
120 ///
121 pub const DT_TLSDESC_PLT: u64 = 0x6fff_fef6;
122 ///
123 pub const DT_TLSDESC_GOT: u64 = 0x6fff_fef7;
124 /// Start of conflict section
125 pub const DT_GNU_CONFLICT: u64 = 0x6fff_fef8;
126 /// Library list
127 pub const DT_GNU_LIBLIST: u64 = 0x6fff_fef9;
128 /// Configuration information
129 pub const DT_CONFIG: u64 = 0x6fff_fefa;
130 /// Dependency auditing
131 pub const DT_DEPAUDIT: u64 = 0x6fff_fefb;
132 /// Object auditing
133 pub const DT_AUDIT: u64 = 0x6fff_fefc;
134 /// PLT padding
135 pub const DT_PLTPAD: u64 = 0x6fff_fefd;
136 /// Move table
137 pub const DT_MOVETAB: u64 = 0x6fff_fefe;
138 /// Syminfo table
139 pub const DT_SYMINFO: u64 = 0x6fff_feff;
140 ///
141 pub const DT_ADDRRNGHI: u64 = 0x6fff_feff;
142 
143 //DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
144 pub const DT_ADDRNUM: u64 = 11;
145 
146 /// The versioning entry types. The next are defined as part of the GNU extension
147 pub const DT_VERSYM: u64 = 0x6fff_fff0;
148 pub const DT_RELACOUNT: u64 = 0x6fff_fff9;
149 pub const DT_RELCOUNT: u64 = 0x6fff_fffa;
150 /// State flags, see DF_1_* below
151 pub const DT_FLAGS_1: u64 = 0x6fff_fffb;
152 /// Address of version definition table
153 pub const DT_VERDEF: u64 = 0x6fff_fffc;
154 /// Number of version definitions
155 pub const DT_VERDEFNUM: u64 = 0x6fff_fffd;
156 /// Address of table with needed versions
157 pub const DT_VERNEED: u64 = 0x6fff_fffe;
158 /// Number of needed versions
159 pub const DT_VERNEEDNUM: u64 = 0x6fff_ffff;
160 
161 /// Converts a tag to its string representation.
162 #[inline]
tag_to_str(tag: u64) -> &'static str163 pub fn tag_to_str(tag: u64) -> &'static str {
164     match tag {
165         DT_NULL => "DT_NULL",
166         DT_NEEDED => "DT_NEEDED",
167         DT_PLTRELSZ => "DT_PLTRELSZ",
168         DT_PLTGOT => "DT_PLTGOT",
169         DT_HASH => "DT_HASH",
170         DT_STRTAB => "DT_STRTAB",
171         DT_SYMTAB => "DT_SYMTAB",
172         DT_RELA => "DT_RELA",
173         DT_RELASZ => "DT_RELASZ",
174         DT_RELAENT => "DT_RELAENT",
175         DT_STRSZ => "DT_STRSZ",
176         DT_SYMENT => "DT_SYMENT",
177         DT_INIT => "DT_INIT",
178         DT_FINI => "DT_FINI",
179         DT_SONAME => "DT_SONAME",
180         DT_RPATH => "DT_RPATH",
181         DT_SYMBOLIC => "DT_SYMBOLIC",
182         DT_REL => "DT_REL",
183         DT_RELSZ => "DT_RELSZ",
184         DT_RELENT => "DT_RELENT",
185         DT_PLTREL => "DT_PLTREL",
186         DT_DEBUG => "DT_DEBUG",
187         DT_TEXTREL => "DT_TEXTREL",
188         DT_JMPREL => "DT_JMPREL",
189         DT_BIND_NOW => "DT_BIND_NOW",
190         DT_INIT_ARRAY => "DT_INIT_ARRAY",
191         DT_FINI_ARRAY => "DT_FINI_ARRAY",
192         DT_INIT_ARRAYSZ => "DT_INIT_ARRAYSZ",
193         DT_FINI_ARRAYSZ => "DT_FINI_ARRAYSZ",
194         DT_RUNPATH => "DT_RUNPATH",
195         DT_FLAGS => "DT_FLAGS",
196         DT_PREINIT_ARRAY => "DT_PREINIT_ARRAY",
197         DT_PREINIT_ARRAYSZ => "DT_PREINIT_ARRAYSZ",
198         DT_NUM => "DT_NUM",
199         DT_LOOS => "DT_LOOS",
200         DT_HIOS => "DT_HIOS",
201         DT_LOPROC => "DT_LOPROC",
202         DT_HIPROC => "DT_HIPROC",
203         DT_VERSYM => "DT_VERSYM",
204         DT_RELACOUNT => "DT_RELACOUNT",
205         DT_RELCOUNT => "DT_RELCOUNT",
206         DT_GNU_HASH => "DT_GNU_HASH",
207         DT_VERDEF => "DT_VERDEF",
208         DT_VERDEFNUM => "DT_VERDEFNUM",
209         DT_VERNEED => "DT_VERNEED",
210         DT_VERNEEDNUM => "DT_VERNEEDNUM",
211         DT_FLAGS_1 => "DT_FLAGS_1",
212         _ => "UNKNOWN_TAG",
213     }
214 }
215 
216 // Values of `d_un.d_val` in the DT_FLAGS entry
217 /// Object may use DF_ORIGIN.
218 pub const DF_ORIGIN: u64 = 0x0000_0001;
219 /// Symbol resolutions starts here.
220 pub const DF_SYMBOLIC: u64 = 0x0000_0002;
221 /// Object contains text relocations.
222 pub const DF_TEXTREL: u64 = 0x0000_0004;
223 /// No lazy binding for this object.
224 pub const DF_BIND_NOW: u64 = 0x0000_0008;
225 /// Module uses the static TLS model.
226 pub const DF_STATIC_TLS: u64 = 0x0000_0010;
227 
df_tag_to_str(tag: u64) -> &'static str228 pub fn df_tag_to_str(tag: u64) -> &'static str {
229     match tag {
230         DF_ORIGIN => "DF_ORIGIN",
231         DF_SYMBOLIC => "DF_SYMBOLIC",
232         DF_TEXTREL => "DF_TEXTREL",
233         DF_BIND_NOW => "DF_BIND_NOW",
234         DF_STATIC_TLS => "DF_STATIC_TLS",
235         _ => "UNKNOWN_TAG",
236     }
237 }
238 
239 /// === State flags ===
240 /// selectable in the `d_un.d_val` element of the DT_FLAGS_1 entry in the dynamic section.
241 ///
242 /// Set RTLD_NOW for this object.
243 pub const DF_1_NOW: u64 = 0x0000_0001;
244 /// Set RTLD_GLOBAL for this object.
245 pub const DF_1_GLOBAL: u64 = 0x0000_0002;
246 /// Set RTLD_GROUP for this object.
247 pub const DF_1_GROUP: u64 = 0x0000_0004;
248 /// Set RTLD_NODELETE for this object.
249 pub const DF_1_NODELETE: u64 = 0x0000_0008;
250 /// Trigger filtee loading at runtime.
251 pub const DF_1_LOADFLTR: u64 = 0x0000_0010;
252 /// Set RTLD_INITFIRST for this object.
253 pub const DF_1_INITFIRST: u64 = 0x0000_0020;
254 /// Set RTLD_NOOPEN for this object.
255 pub const DF_1_NOOPEN: u64 = 0x0000_0040;
256 /// $ORIGIN must be handled.
257 pub const DF_1_ORIGIN: u64 = 0x0000_0080;
258 /// Direct binding enabled.
259 pub const DF_1_DIRECT: u64 = 0x0000_0100;
260 pub const DF_1_TRANS: u64 = 0x0000_0200;
261 /// Object is used to interpose.
262 pub const DF_1_INTERPOSE: u64 = 0x0000_0400;
263 /// Ignore default lib search path.
264 pub const DF_1_NODEFLIB: u64 = 0x0000_0800;
265 /// Object can't be dldump'ed.
266 pub const DF_1_NODUMP: u64 = 0x0000_1000;
267 /// Configuration alternative created.
268 pub const DF_1_CONFALT: u64 = 0x0000_2000;
269 /// Filtee terminates filters search.
270 pub const DF_1_ENDFILTEE: u64 = 0x0000_4000;
271 /// Disp reloc applied at build time.
272 pub const DF_1_DISPRELDNE: u64 = 0x0000_8000;
273 /// Disp reloc applied at run-time.
274 pub const DF_1_DISPRELPND: u64 = 0x0001_0000;
275 /// Object has no-direct binding.
276 pub const DF_1_NODIRECT: u64 = 0x0002_0000;
277 pub const DF_1_IGNMULDEF: u64 = 0x0004_0000;
278 pub const DF_1_NOKSYMS: u64 = 0x0008_0000;
279 pub const DF_1_NOHDR: u64 = 0x0010_0000;
280 /// Object is modified after built.
281 pub const DF_1_EDITED: u64 = 0x0020_0000;
282 pub const DF_1_NORELOC: u64 = 0x0040_0000;
283 /// Object has individual interposers.
284 pub const DF_1_SYMINTPOSE: u64 = 0x0080_0000;
285 /// Global auditing required.
286 pub const DF_1_GLOBAUDIT: u64 = 0x0100_0000;
287 /// Singleton dyn are used.
288 pub const DF_1_SINGLETON: u64 = 0x0200_0000;
289 /// Object is a Position Independent Executable (PIE).
290 pub const DF_1_PIE: u64 = 0x0800_0000;
291 
df_1_tag_to_str(tag: u64) -> &'static str292 pub fn df_1_tag_to_str(tag: u64) -> &'static str {
293     match tag {
294         DF_1_NOW => "DF_1_NOW",
295         DF_1_GLOBAL => "DF_1_GLOBAL",
296         DF_1_GROUP => "DF_1_GROUP",
297         DF_1_NODELETE => "DF_1_NODELETE",
298         DF_1_LOADFLTR => "DF_1_LOADFLTR",
299         DF_1_INITFIRST => "DF_1_INITFIRST",
300         DF_1_NOOPEN => "DF_1_NOOPEN",
301         DF_1_ORIGIN => "DF_1_ORIGIN",
302         DF_1_DIRECT => "DF_1_DIRECT",
303         DF_1_TRANS => "DF_1_TRANS",
304         DF_1_INTERPOSE => "DF_1_INTERPOSE",
305         DF_1_NODEFLIB => "DF_1_NODEFLIB",
306         DF_1_NODUMP => "DF_1_NODUMP",
307         DF_1_CONFALT => "DF_1_CONFALT",
308         DF_1_ENDFILTEE => "DF_1_ENDFILTEE",
309         DF_1_DISPRELDNE => "DF_1_DISPRELDNE",
310         DF_1_DISPRELPND => "DF_1_DISPRELPND",
311         DF_1_NODIRECT => "DF_1_NODIRECT",
312         DF_1_IGNMULDEF => "DF_1_IGNMULDEF",
313         DF_1_NOKSYMS => "DF_1_NOKSYMS",
314         DF_1_NOHDR => "DF_1_NOHDR",
315         DF_1_EDITED => "DF_1_EDITED",
316         DF_1_NORELOC => "DF_1_NORELOC",
317         DF_1_SYMINTPOSE => "DF_1_SYMINTPOSE",
318         DF_1_GLOBAUDIT => "DF_1_GLOBAUDIT",
319         DF_1_SINGLETON => "DF_1_SINGLETON",
320         DF_1_PIE => "DF_1_PIE",
321         _ => "UNKNOWN_TAG",
322     }
323 }
324 
325 if_alloc! {
326     use core::fmt;
327     use scroll::ctx;
328     use core::result;
329     use crate::container::{Ctx, Container};
330     use crate::strtab::Strtab;
331     use alloc::vec::Vec;
332 
333     #[derive(Default, PartialEq, Clone)]
334     pub struct Dyn {
335         pub d_tag: u64,
336         pub d_val: u64,
337     }
338 
339     impl Dyn {
340         #[inline]
341         pub fn size(container: Container) -> usize {
342             use scroll::ctx::SizeWith;
343             Self::size_with(&Ctx::from(container))
344         }
345     }
346 
347     impl fmt::Debug for Dyn {
348         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349             f.debug_struct("Dyn")
350                 .field("d_tag", &tag_to_str(self.d_tag))
351                 .field("d_val", &format_args!("0x{:x}", self.d_val))
352                 .finish()
353         }
354     }
355 
356     impl ctx::SizeWith<Ctx> for Dyn {
357         fn size_with(&Ctx { container, .. }: &Ctx) -> usize {
358             match container {
359                 Container::Little => {
360                     dyn32::SIZEOF_DYN
361                 },
362                 Container::Big => {
363                     dyn64::SIZEOF_DYN
364                 },
365             }
366         }
367     }
368 
369     impl<'a> ctx::TryFromCtx<'a, Ctx> for Dyn {
370         type Error = crate::error::Error;
371         fn try_from_ctx(bytes: &'a [u8], Ctx { container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> {
372             use scroll::Pread;
373             let dynamic = match container {
374                 Container::Little => {
375                     (bytes.pread_with::<dyn32::Dyn>(0, le)?.into(), dyn32::SIZEOF_DYN)
376                 },
377                 Container::Big => {
378                     (bytes.pread_with::<dyn64::Dyn>(0, le)?.into(), dyn64::SIZEOF_DYN)
379                 }
380             };
381             Ok(dynamic)
382         }
383     }
384 
385     impl ctx::TryIntoCtx<Ctx> for Dyn {
386         type Error = crate::error::Error;
387         fn try_into_ctx(self, bytes: &mut [u8], Ctx { container, le}: Ctx) -> result::Result<usize, Self::Error> {
388             use scroll::Pwrite;
389             match container {
390                 Container::Little => {
391                     let dynamic: dyn32::Dyn = self.into();
392                     Ok(bytes.pwrite_with(dynamic, 0, le)?)
393                 },
394                 Container::Big => {
395                     let dynamic: dyn64::Dyn = self.into();
396                     Ok(bytes.pwrite_with(dynamic, 0, le)?)
397                 }
398             }
399         }
400     }
401 
402     #[derive(Debug)]
403     pub struct Dynamic {
404         pub dyns: Vec<Dyn>,
405         pub info: DynamicInfo,
406         count: usize,
407     }
408 
409     impl Dynamic {
410         #[cfg(feature = "endian_fd")]
411         /// Returns a vector of dynamic entries from the underlying byte `bytes`, with `endianness`, using the provided `phdrs`
412         pub fn parse(bytes: &[u8], phdrs: &[crate::elf::program_header::ProgramHeader], ctx: Ctx) -> crate::error::Result<Option<Self>> {
413             use scroll::ctx::SizeWith;
414             use scroll::Pread;
415             use crate::elf::program_header;
416             for phdr in phdrs {
417                 if phdr.p_type == program_header::PT_DYNAMIC {
418                     let offset = phdr.p_offset as usize;
419                     let filesz = phdr.p_filesz as usize;
420                     // Ensure offset and filesz are valid.
421                     let bytes = if filesz > 0 {
422                         bytes
423                             .pread_with::<&[u8]>(offset, filesz)
424                             .map_err(|_| crate::error::Error::Malformed(format!("Invalid PT_DYNAMIC size (offset {:#x}, filesz {:#x})",
425                                                                offset, filesz)))?
426                     } else {
427                         &[]
428                     };
429                     let size = Dyn::size_with(&ctx);
430                     let count = filesz / size;
431                     let mut dyns = Vec::with_capacity(count);
432                     let mut offset = 0;
433                     for _ in 0..count {
434                         let dynamic = bytes.gread_with::<Dyn>(&mut offset, ctx)?;
435                         let tag = dynamic.d_tag;
436                         dyns.push(dynamic);
437                         if tag == DT_NULL { break }
438                     }
439                     let mut info = DynamicInfo::default();
440                     for dynamic in &dyns {
441                         info.update(phdrs, dynamic);
442                     }
443                     let count = dyns.len();
444                     return Ok(Some(Dynamic { dyns: dyns, info: info, count: count }));
445                 }
446             }
447             Ok(None)
448         }
449 
450         pub fn get_libraries<'a>(&self, strtab: &Strtab<'a>) -> Vec<&'a str> {
451             use log::warn;
452             let count = self.info.needed_count;
453             let mut needed = Vec::with_capacity(count);
454             for dynamic in &self.dyns {
455                 if dynamic.d_tag as u64 == DT_NEEDED {
456                     if let Some(lib) = strtab.get_at(dynamic.d_val as usize) {
457                         needed.push(lib)
458                     } else {
459                         warn!("Invalid DT_NEEDED {}", dynamic.d_val)
460                     }
461                 }
462             }
463             needed
464         }
465     }
466 }
467 
468 macro_rules! elf_dyn_std_impl {
469     ($size:ident, $phdr:ty) => {
470 
471         #[cfg(test)]
472         mod tests {
473             use super::*;
474             #[test]
475             fn size_of() {
476                 assert_eq!(::std::mem::size_of::<Dyn>(), SIZEOF_DYN);
477             }
478         }
479 
480         if_alloc! {
481             use core::fmt;
482             use core::slice;
483             use alloc::vec::Vec;
484 
485             use crate::elf::program_header::{PT_DYNAMIC};
486             use crate::strtab::Strtab;
487 
488             use crate::elf::dynamic::Dyn as ElfDyn;
489 
490             if_std! {
491                 use std::fs::File;
492                 use std::io::{Read, Seek};
493                 use std::io::SeekFrom::Start;
494                 use crate::error::Result;
495             }
496 
497             impl From<ElfDyn> for Dyn {
498                 fn from(dynamic: ElfDyn) -> Self {
499                     Dyn {
500                         d_tag: dynamic.d_tag as $size,
501                         d_val: dynamic.d_val as $size,
502                     }
503                 }
504             }
505             impl From<Dyn> for ElfDyn {
506                 fn from(dynamic: Dyn) -> Self {
507                     ElfDyn {
508                         d_tag: u64::from(dynamic.d_tag),
509                         d_val: u64::from(dynamic.d_val),
510                     }
511                 }
512             }
513 
514             impl fmt::Debug for Dyn {
515                 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516                     f.debug_struct("Dyn")
517                         .field("d_tag", &tag_to_str(u64::from(self.d_tag)))
518                         .field("d_val", &format_args!("0x{:x}", self.d_val))
519                         .finish()
520                 }
521             }
522 
523             /// Returns a vector of dynamic entries from the given fd and program headers
524             #[cfg(feature = "std")]
525             pub fn from_fd(mut fd: &File, phdrs: &[$phdr]) -> Result<Option<Vec<Dyn>>> {
526                 for phdr in phdrs {
527                     if phdr.p_type == PT_DYNAMIC {
528                         // FIXME: validate filesz before allocating
529                         let filesz = phdr.p_filesz as usize;
530                         let dync = filesz / SIZEOF_DYN;
531                         let mut dyns = vec![Dyn::default(); dync];
532                         fd.seek(Start(u64::from(phdr.p_offset)))?;
533                         unsafe {
534                             fd.read_exact(plain::as_mut_bytes(&mut *dyns))?;
535                         }
536                         dyns.dedup();
537                         return Ok(Some(dyns));
538                     }
539                 }
540                 Ok(None)
541             }
542 
543             /// 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
544             pub unsafe fn from_raw<'a>(bias: usize, vaddr: usize) -> &'a [Dyn] {
545                 let dynp = vaddr.wrapping_add(bias) as *const Dyn;
546                 let mut idx = 0;
547                 while u64::from((*dynp.offset(idx)).d_tag) != DT_NULL {
548                     idx += 1;
549                 }
550                 slice::from_raw_parts(dynp, idx as usize)
551             }
552 
553             // TODO: these bare functions have always seemed awkward, but not sure where they should go...
554             /// Maybe gets and returns the dynamic array with the same lifetime as the [phdrs], using the provided bias with wrapping addition.
555             /// If the bias is wrong, it will either segfault or give you incorrect values, beware
556             pub unsafe fn from_phdrs(bias: usize, phdrs: &[$phdr]) -> Option<&[Dyn]> {
557                 for phdr in phdrs {
558                     // FIXME: change to casting to u64 similar to DT_*?
559                     if phdr.p_type as u32 == PT_DYNAMIC {
560                         return Some(from_raw(bias, phdr.p_vaddr as usize));
561                     }
562                 }
563                 None
564             }
565 
566             /// Gets the needed libraries from the `_DYNAMIC` array, with the str slices lifetime tied to the dynamic array/strtab's lifetime(s)
567             pub unsafe fn get_needed<'a>(dyns: &[Dyn], strtab: *const Strtab<'a>, count: usize) -> Vec<&'a str> {
568                 let mut needed = Vec::with_capacity(count);
569                 for dynamic in dyns {
570                     if u64::from(dynamic.d_tag) == DT_NEEDED {
571                         let lib = &(*strtab)[dynamic.d_val as usize];
572                         needed.push(lib);
573                     }
574                 }
575                 needed
576             }
577         }
578     };
579 }
580 
581 macro_rules! elf_dynamic_info_std_impl {
582     ($size:ident, $phdr:ty) => {
583         /// Convert a virtual memory address to a file offset
584         fn vm_to_offset(phdrs: &[$phdr], address: $size) -> Option<$size> {
585             for ph in phdrs {
586                 if address >= ph.p_vaddr {
587                     let offset = address - ph.p_vaddr;
588                     if offset < ph.p_memsz {
589                         return ph.p_offset.checked_add(offset);
590                     }
591                 }
592             }
593             None
594         }
595 
596         /// Important dynamic linking info generated via a single pass through the `_DYNAMIC` array
597         #[derive(Default, PartialEq)]
598         pub struct DynamicInfo {
599             pub rela: usize,
600             pub relasz: usize,
601             pub relaent: $size,
602             pub relacount: usize,
603             pub rel: usize,
604             pub relsz: usize,
605             pub relent: $size,
606             pub relcount: usize,
607             pub gnu_hash: Option<$size>,
608             pub hash: Option<$size>,
609             pub strtab: usize,
610             pub strsz: usize,
611             pub symtab: usize,
612             pub syment: usize,
613             pub pltgot: Option<$size>,
614             pub pltrelsz: usize,
615             pub pltrel: $size,
616             pub jmprel: usize,
617             pub verdef: $size,
618             pub verdefnum: $size,
619             pub verneed: $size,
620             pub verneednum: $size,
621             pub versym: $size,
622             pub init: $size,
623             pub fini: $size,
624             pub init_array: $size,
625             pub init_arraysz: usize,
626             pub fini_array: $size,
627             pub fini_arraysz: usize,
628             pub needed_count: usize,
629             pub flags: $size,
630             pub flags_1: $size,
631             pub soname: usize,
632             pub textrel: bool,
633         }
634 
635         impl DynamicInfo {
636             #[inline]
637             pub fn update(&mut self, phdrs: &[$phdr], dynamic: &Dyn) {
638                 match u64::from(dynamic.d_tag) {
639                     DT_RELA => self.rela = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rela.dyn
640                     DT_RELASZ => self.relasz = dynamic.d_val as usize,
641                     DT_RELAENT => self.relaent = dynamic.d_val as _,
642                     DT_RELACOUNT => self.relacount = dynamic.d_val as usize,
643                     DT_REL => self.rel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rel.dyn
644                     DT_RELSZ => self.relsz = dynamic.d_val as usize,
645                     DT_RELENT => self.relent = dynamic.d_val as _,
646                     DT_RELCOUNT => self.relcount = dynamic.d_val as usize,
647                     DT_GNU_HASH => self.gnu_hash = vm_to_offset(phdrs, dynamic.d_val),
648                     DT_HASH => self.hash = vm_to_offset(phdrs, dynamic.d_val),
649                     DT_STRTAB => {
650                         self.strtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
651                     }
652                     DT_STRSZ => self.strsz = dynamic.d_val as usize,
653                     DT_SYMTAB => {
654                         self.symtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
655                     }
656                     DT_SYMENT => self.syment = dynamic.d_val as usize,
657                     DT_PLTGOT => self.pltgot = vm_to_offset(phdrs, dynamic.d_val),
658                     DT_PLTRELSZ => self.pltrelsz = dynamic.d_val as usize,
659                     DT_PLTREL => self.pltrel = dynamic.d_val as _,
660                     DT_JMPREL => {
661                         self.jmprel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
662                     } // .rela.plt
663                     DT_VERDEF => self.verdef = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
664                     DT_VERDEFNUM => self.verdefnum = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
665                     DT_VERNEED => self.verneed = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
666                     DT_VERNEEDNUM => self.verneednum = dynamic.d_val as _,
667                     DT_VERSYM => self.versym = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
668                     DT_INIT => self.init = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
669                     DT_FINI => self.fini = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
670                     DT_INIT_ARRAY => {
671                         self.init_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0)
672                     }
673                     DT_INIT_ARRAYSZ => self.init_arraysz = dynamic.d_val as _,
674                     DT_FINI_ARRAY => {
675                         self.fini_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0)
676                     }
677                     DT_FINI_ARRAYSZ => self.fini_arraysz = dynamic.d_val as _,
678                     DT_NEEDED => self.needed_count += 1,
679                     DT_FLAGS => self.flags = dynamic.d_val as _,
680                     DT_FLAGS_1 => self.flags_1 = dynamic.d_val as _,
681                     DT_SONAME => self.soname = dynamic.d_val as _,
682                     DT_TEXTREL => self.textrel = true,
683                     _ => (),
684                 }
685             }
686             pub fn new(dynamic: &[Dyn], phdrs: &[$phdr]) -> DynamicInfo {
687                 let mut info = DynamicInfo::default();
688                 for dyna in dynamic {
689                     info.update(phdrs, &dyna);
690                 }
691                 info
692             }
693         }
694 
695         if_alloc! {
696             impl fmt::Debug for DynamicInfo {
697                 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
698                     let gnu_hash = self.gnu_hash.unwrap_or(0);
699                     let hash = self.hash.unwrap_or(0);
700                     let pltgot = self.pltgot.unwrap_or(0);
701 
702                     let flags: Vec<&'static str> = [DF_ORIGIN, DF_SYMBOLIC, DF_TEXTREL, DF_BIND_NOW, DF_STATIC_TLS,][..]
703                                     .iter()
704                                     .filter(|f| (self.flags as u64 & *f) != 0)
705                                     .map(|f| df_tag_to_str(*f))
706                                     .collect();
707 
708                     let flags_1: Vec<&'static str> = [
709                             DF_1_NOW,
710                             DF_1_GLOBAL,
711                             DF_1_GROUP,
712                             DF_1_NODELETE,
713                             DF_1_LOADFLTR,
714                             DF_1_INITFIRST,
715                             DF_1_NOOPEN,
716                             DF_1_ORIGIN,
717                             DF_1_DIRECT,
718                             DF_1_TRANS,
719                             DF_1_INTERPOSE,
720                             DF_1_NODEFLIB,
721                             DF_1_NODUMP,
722                             DF_1_CONFALT,
723                             DF_1_ENDFILTEE,
724                             DF_1_DISPRELDNE,
725                             DF_1_DISPRELPND,
726                             DF_1_NODIRECT,
727                             DF_1_IGNMULDEF,
728                             DF_1_NOKSYMS,
729                             DF_1_NOHDR,
730                             DF_1_EDITED,
731                             DF_1_NORELOC,
732                             DF_1_SYMINTPOSE,
733                             DF_1_GLOBAUDIT,
734                             DF_1_SINGLETON,
735                             DF_1_PIE,
736                         ][..]
737                         .iter()
738                         .filter(|f| (self.flags_1 as u64 & *f) != 0)
739                         .map(|f| df_1_tag_to_str(*f))
740                         .collect();
741 
742                     f.debug_struct("DynamicInfo")
743                         .field("rela", &format_args!("0x{:x}", self.rela))
744                         .field("relasz", &self.relasz)
745                         .field("relaent", &self.relaent)
746                         .field("relacount", &self.relacount)
747                         .field("gnu_hash", &format_args!("0x{:x}", gnu_hash))
748                         .field("hash", &format_args!("0x{:x}", hash))
749                         .field("strtab", &format_args!("0x{:x}", self.strtab))
750                         .field("strsz", &self.strsz)
751                         .field("symtab", &format_args!("0x{:x}", self.symtab))
752                         .field("syment", &self.syment)
753                         .field("pltgot", &format_args!("0x{:x}", pltgot))
754                         .field("pltrelsz", &self.pltrelsz)
755                         .field("pltrel", &self.pltrel)
756                         .field("jmprel", &format_args!("0x{:x}", self.jmprel))
757                         .field("verdef", &format_args!("0x{:x}", self.verdef))
758                         .field("verdefnum", &self.verdefnum)
759                         .field("verneed", &format_args!("0x{:x}", self.verneed))
760                         .field("verneednum", &self.verneednum)
761                         .field("versym", &format_args!("0x{:x}", self.versym))
762                         .field("init", &format_args!("0x{:x}", self.init))
763                         .field("fini", &format_args!("0x{:x}", self.fini))
764                         .field("init_array", &format_args!("{:#x}", self.init_array))
765                         .field("init_arraysz", &self.init_arraysz)
766                         .field("needed_count", &self.needed_count)
767                         .field("flags", &format_args!("{:#0width$x} {:?}", self.flags, flags, width = core::mem::size_of_val(&self.flags)))
768                         .field("flags_1", &format_args!("{:#0width$x} {:?}", self.flags_1, flags_1, width = core::mem::size_of_val(&self.flags_1)))
769                         .field("soname", &self.soname)
770                         .field("textrel", &self.textrel)
771                         .finish()
772                 }
773             }
774         }
775     };
776 }
777 
778 if_alloc! {
779     elf_dynamic_info_std_impl!(u64, crate::elf::program_header::ProgramHeader);
780 }
781 
782 pub mod dyn32 {
783     pub use crate::elf::dynamic::*;
784 
785     elf_dyn!(u32);
786 
787     pub const SIZEOF_DYN: usize = 8;
788 
789     elf_dyn_std_impl!(u32, crate::elf32::program_header::ProgramHeader);
790     elf_dynamic_info_std_impl!(
791         u32,
792         crate::elf::program_header::program_header32::ProgramHeader
793     );
794 }
795 
796 pub mod dyn64 {
797     pub use crate::elf::dynamic::*;
798 
799     elf_dyn!(u64);
800 
801     pub const SIZEOF_DYN: usize = 16;
802 
803     elf_dyn_std_impl!(u64, crate::elf64::program_header::ProgramHeader);
804     elf_dynamic_info_std_impl!(
805         u64,
806         crate::elf::program_header::program_header64::ProgramHeader
807     );
808 }
809