goblin_experimental/elf/
dynamic.rs

1macro_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
32pub const DT_NULL: u64 = 0;
33/// Name of needed library
34pub const DT_NEEDED: u64 = 1;
35/// Size in bytes of PLT relocs
36pub const DT_PLTRELSZ: u64 = 2;
37/// Processor defined value
38pub const DT_PLTGOT: u64 = 3;
39/// Address of symbol hash table
40pub const DT_HASH: u64 = 4;
41/// Address of string table
42pub const DT_STRTAB: u64 = 5;
43/// Address of symbol table
44pub const DT_SYMTAB: u64 = 6;
45/// Address of Rela relocs
46pub const DT_RELA: u64 = 7;
47/// Total size of Rela relocs
48pub const DT_RELASZ: u64 = 8;
49/// Size of one Rela reloc
50pub const DT_RELAENT: u64 = 9;
51/// Size of string table
52pub const DT_STRSZ: u64 = 10;
53/// Size of one symbol table entry
54pub const DT_SYMENT: u64 = 11;
55/// Address of init function
56pub const DT_INIT: u64 = 12;
57/// Address of termination function
58pub const DT_FINI: u64 = 13;
59/// Name of shared object
60pub const DT_SONAME: u64 = 14;
61/// Library search path (deprecated)
62pub const DT_RPATH: u64 = 15;
63/// Start symbol search here
64pub const DT_SYMBOLIC: u64 = 16;
65/// Address of Rel relocs
66pub const DT_REL: u64 = 17;
67/// Total size of Rel relocs
68pub const DT_RELSZ: u64 = 18;
69/// Size of one Rel reloc
70pub const DT_RELENT: u64 = 19;
71/// Type of reloc in PLT
72pub const DT_PLTREL: u64 = 20;
73/// For debugging; unspecified
74pub const DT_DEBUG: u64 = 21;
75/// Reloc might modify .text
76pub const DT_TEXTREL: u64 = 22;
77/// Address of PLT relocs
78pub const DT_JMPREL: u64 = 23;
79/// Process relocations of object
80pub const DT_BIND_NOW: u64 = 24;
81/// Array with addresses of init fct
82pub const DT_INIT_ARRAY: u64 = 25;
83/// Array with addresses of fini fct
84pub const DT_FINI_ARRAY: u64 = 26;
85/// Size in bytes of DT_INIT_ARRAY
86pub const DT_INIT_ARRAYSZ: u64 = 27;
87/// Size in bytes of DT_FINI_ARRAY
88pub const DT_FINI_ARRAYSZ: u64 = 28;
89/// Library search path
90pub const DT_RUNPATH: u64 = 29;
91/// Flags for the object being loaded
92pub const DT_FLAGS: u64 = 30;
93/// Start of encoded range
94pub const DT_ENCODING: u64 = 32;
95/// Array with addresses of preinit fct
96pub const DT_PREINIT_ARRAY: u64 = 32;
97/// size in bytes of DT_PREINIT_ARRAY
98pub const DT_PREINIT_ARRAYSZ: u64 = 33;
99/// Number used
100pub const DT_NUM: u64 = 34;
101/// Start of OS-specific
102pub const DT_LOOS: u64 = 0x6000_000d;
103/// End of OS-specific
104pub const DT_HIOS: u64 = 0x6fff_f000;
105/// Start of processor-specific
106pub const DT_LOPROC: u64 = 0x7000_0000;
107/// End of processor-specific
108pub 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.
117pub const DT_ADDRRNGLO: u64 = 0x6fff_fe00;
118/// GNU-style hash table
119pub const DT_GNU_HASH: u64 = 0x6fff_fef5;
120///
121pub const DT_TLSDESC_PLT: u64 = 0x6fff_fef6;
122///
123pub const DT_TLSDESC_GOT: u64 = 0x6fff_fef7;
124/// Start of conflict section
125pub const DT_GNU_CONFLICT: u64 = 0x6fff_fef8;
126/// Library list
127pub const DT_GNU_LIBLIST: u64 = 0x6fff_fef9;
128/// Configuration information
129pub const DT_CONFIG: u64 = 0x6fff_fefa;
130/// Dependency auditing
131pub const DT_DEPAUDIT: u64 = 0x6fff_fefb;
132/// Object auditing
133pub const DT_AUDIT: u64 = 0x6fff_fefc;
134/// PLT padding
135pub const DT_PLTPAD: u64 = 0x6fff_fefd;
136/// Move table
137pub const DT_MOVETAB: u64 = 0x6fff_fefe;
138/// Syminfo table
139pub const DT_SYMINFO: u64 = 0x6fff_feff;
140///
141pub const DT_ADDRRNGHI: u64 = 0x6fff_feff;
142
143//DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
144pub const DT_ADDRNUM: u64 = 11;
145
146/// The versioning entry types. The next are defined as part of the GNU extension
147pub const DT_VERSYM: u64 = 0x6fff_fff0;
148pub const DT_RELACOUNT: u64 = 0x6fff_fff9;
149pub const DT_RELCOUNT: u64 = 0x6fff_fffa;
150/// State flags, see DF_1_* below
151pub const DT_FLAGS_1: u64 = 0x6fff_fffb;
152/// Address of version definition table
153pub const DT_VERDEF: u64 = 0x6fff_fffc;
154/// Number of version definitions
155pub const DT_VERDEFNUM: u64 = 0x6fff_fffd;
156/// Address of table with needed versions
157pub const DT_VERNEED: u64 = 0x6fff_fffe;
158/// Number of needed versions
159pub const DT_VERNEEDNUM: u64 = 0x6fff_ffff;
160
161/// Converts a tag to its string representation.
162#[inline]
163pub 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.
218pub const DF_ORIGIN: u64 = 0x0000_0001;
219/// Symbol resolutions starts here.
220pub const DF_SYMBOLIC: u64 = 0x0000_0002;
221/// Object contains text relocations.
222pub const DF_TEXTREL: u64 = 0x0000_0004;
223/// No lazy binding for this object.
224pub const DF_BIND_NOW: u64 = 0x0000_0008;
225/// Module uses the static TLS model.
226pub const DF_STATIC_TLS: u64 = 0x0000_0010;
227
228pub 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.
243pub const DF_1_NOW: u64 = 0x0000_0001;
244/// Set RTLD_GLOBAL for this object.
245pub const DF_1_GLOBAL: u64 = 0x0000_0002;
246/// Set RTLD_GROUP for this object.
247pub const DF_1_GROUP: u64 = 0x0000_0004;
248/// Set RTLD_NODELETE for this object.
249pub const DF_1_NODELETE: u64 = 0x0000_0008;
250/// Trigger filtee loading at runtime.
251pub const DF_1_LOADFLTR: u64 = 0x0000_0010;
252/// Set RTLD_INITFIRST for this object.
253pub const DF_1_INITFIRST: u64 = 0x0000_0020;
254/// Set RTLD_NOOPEN for this object.
255pub const DF_1_NOOPEN: u64 = 0x0000_0040;
256/// $ORIGIN must be handled.
257pub const DF_1_ORIGIN: u64 = 0x0000_0080;
258/// Direct binding enabled.
259pub const DF_1_DIRECT: u64 = 0x0000_0100;
260pub const DF_1_TRANS: u64 = 0x0000_0200;
261/// Object is used to interpose.
262pub const DF_1_INTERPOSE: u64 = 0x0000_0400;
263/// Ignore default lib search path.
264pub const DF_1_NODEFLIB: u64 = 0x0000_0800;
265/// Object can't be dldump'ed.
266pub const DF_1_NODUMP: u64 = 0x0000_1000;
267/// Configuration alternative created.
268pub const DF_1_CONFALT: u64 = 0x0000_2000;
269/// Filtee terminates filters search.
270pub const DF_1_ENDFILTEE: u64 = 0x0000_4000;
271/// Disp reloc applied at build time.
272pub const DF_1_DISPRELDNE: u64 = 0x0000_8000;
273/// Disp reloc applied at run-time.
274pub const DF_1_DISPRELPND: u64 = 0x0001_0000;
275/// Object has no-direct binding.
276pub const DF_1_NODIRECT: u64 = 0x0002_0000;
277pub const DF_1_IGNMULDEF: u64 = 0x0004_0000;
278pub const DF_1_NOKSYMS: u64 = 0x0008_0000;
279pub const DF_1_NOHDR: u64 = 0x0010_0000;
280/// Object is modified after built.
281pub const DF_1_EDITED: u64 = 0x0020_0000;
282pub const DF_1_NORELOC: u64 = 0x0040_0000;
283/// Object has individual interposers.
284pub const DF_1_SYMINTPOSE: u64 = 0x0080_0000;
285/// Global auditing required.
286pub const DF_1_GLOBAUDIT: u64 = 0x0100_0000;
287/// Singleton dyn are used.
288pub const DF_1_SINGLETON: u64 = 0x0200_0000;
289/// Object is a Position Independent Executable (PIE).
290pub const DF_1_PIE: u64 = 0x0800_0000;
291
292pub 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
325if_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    }
407
408    impl Dynamic {
409        #[cfg(feature = "endian_fd")]
410        /// Returns a vector of dynamic entries from the underlying byte `bytes`, with `endianness`, using the provided `phdrs`
411        pub fn parse(bytes: &[u8], phdrs: &[crate::elf::program_header::ProgramHeader], ctx: Ctx) -> crate::error::Result<Option<Self>> {
412            use scroll::ctx::SizeWith;
413            use scroll::Pread;
414            use crate::elf::program_header;
415            for phdr in phdrs {
416                if phdr.p_type == program_header::PT_DYNAMIC {
417                    let offset = phdr.p_offset as usize;
418                    let filesz = phdr.p_filesz as usize;
419                    // Ensure offset and filesz are valid.
420                    let bytes = if filesz > 0 {
421                        bytes
422                            .pread_with::<&[u8]>(offset, filesz)
423                            .map_err(|_| crate::error::Error::Malformed(format!("Invalid PT_DYNAMIC size (offset {:#x}, filesz {:#x})",
424                                                               offset, filesz)))?
425                    } else {
426                        &[]
427                    };
428                    let size = Dyn::size_with(&ctx);
429                    // the validity of `count` was implicitly checked by reading `bytes`.
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                    return Ok(Some(Dynamic { dyns: dyns, info: info, }));
444                }
445            }
446            Ok(None)
447        }
448
449        pub fn get_libraries<'a>(&self, strtab: &Strtab<'a>) -> Vec<&'a str> {
450            use log::warn;
451            let count = self.info.needed_count.min(self.dyns.len());
452            let mut needed = Vec::with_capacity(count);
453            for dynamic in &self.dyns {
454                if dynamic.d_tag as u64 == DT_NEEDED {
455                    if let Some(lib) = strtab.get_at(dynamic.d_val as usize) {
456                        needed.push(lib)
457                    } else {
458                        warn!("Invalid DT_NEEDED {}", dynamic.d_val)
459                    }
460                }
461            }
462            needed
463        }
464    }
465}
466
467macro_rules! elf_dyn_std_impl {
468    ($size:ident, $phdr:ty) => {
469
470        #[cfg(test)]
471        mod tests {
472            use super::*;
473            #[test]
474            fn size_of() {
475                assert_eq!(::std::mem::size_of::<Dyn>(), SIZEOF_DYN);
476            }
477        }
478
479        if_alloc! {
480            use core::fmt;
481            use core::slice;
482            use alloc::vec::Vec;
483
484            use crate::elf::program_header::{PT_DYNAMIC};
485            use crate::strtab::Strtab;
486
487            use crate::elf::dynamic::Dyn as ElfDyn;
488
489            if_std! {
490                use std::fs::File;
491                use std::io::{Read, Seek};
492                use std::io::SeekFrom::Start;
493                use crate::error::Result;
494            }
495
496            impl From<ElfDyn> for Dyn {
497                fn from(dynamic: ElfDyn) -> Self {
498                    Dyn {
499                        d_tag: dynamic.d_tag as $size,
500                        d_val: dynamic.d_val as $size,
501                    }
502                }
503            }
504            impl From<Dyn> for ElfDyn {
505                fn from(dynamic: Dyn) -> Self {
506                    ElfDyn {
507                        d_tag: u64::from(dynamic.d_tag),
508                        d_val: u64::from(dynamic.d_val),
509                    }
510                }
511            }
512
513            impl fmt::Debug for Dyn {
514                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
515                    f.debug_struct("Dyn")
516                        .field("d_tag", &tag_to_str(u64::from(self.d_tag)))
517                        .field("d_val", &format_args!("0x{:x}", self.d_val))
518                        .finish()
519                }
520            }
521
522            /// Returns a vector of dynamic entries from the given fd and program headers
523            #[cfg(feature = "std")]
524            pub fn from_fd(mut fd: &File, phdrs: &[$phdr]) -> Result<Option<Vec<Dyn>>> {
525                for phdr in phdrs {
526                    if phdr.p_type == PT_DYNAMIC {
527                        // FIXME: validate filesz before allocating
528                        let filesz = phdr.p_filesz as usize;
529                        let dync = filesz / SIZEOF_DYN;
530                        let mut dyns = vec![Dyn::default(); dync];
531                        fd.seek(Start(u64::from(phdr.p_offset)))?;
532                        unsafe {
533                            fd.read_exact(plain::as_mut_bytes(&mut *dyns))?;
534                        }
535                        dyns.dedup();
536                        return Ok(Some(dyns));
537                    }
538                }
539                Ok(None)
540            }
541
542            /// 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
543            pub unsafe fn from_raw<'a>(bias: usize, vaddr: usize) -> &'a [Dyn] {
544                let dynp = vaddr.wrapping_add(bias) as *const Dyn;
545                let mut idx = 0;
546                while u64::from((*dynp.offset(idx)).d_tag) != DT_NULL {
547                    idx += 1;
548                }
549                slice::from_raw_parts(dynp, idx as usize)
550            }
551
552            // TODO: these bare functions have always seemed awkward, but not sure where they should go...
553            /// Maybe gets and returns the dynamic array with the same lifetime as the `phdrs`, using the provided bias with wrapping addition.
554            /// If the bias is wrong, it will either segfault or give you incorrect values, beware
555            pub unsafe fn from_phdrs(bias: usize, phdrs: &[$phdr]) -> Option<&[Dyn]> {
556                for phdr in phdrs {
557                    // FIXME: change to casting to u64 similar to DT_*?
558                    if phdr.p_type as u32 == PT_DYNAMIC {
559                        return Some(from_raw(bias, phdr.p_vaddr as usize));
560                    }
561                }
562                None
563            }
564
565            /// Gets the needed libraries from the `_DYNAMIC` array, with the str slices lifetime tied to the dynamic array/strtab's lifetime(s)
566            pub unsafe fn get_needed<'a>(dyns: &[Dyn], strtab: *const Strtab<'a>, count: usize) -> Vec<&'a str> {
567                let mut needed = Vec::with_capacity(count.min(dyns.len()));
568                for dynamic in dyns {
569                    if u64::from(dynamic.d_tag) == DT_NEEDED {
570                        let lib = &(*strtab)[dynamic.d_val as usize];
571                        needed.push(lib);
572                    }
573                }
574                needed
575            }
576        }
577    };
578}
579
580macro_rules! elf_dynamic_info_std_impl {
581    ($size:ident, $phdr:ty) => {
582        /// Convert a virtual memory address to a file offset
583        fn vm_to_offset(phdrs: &[$phdr], address: $size) -> Option<$size> {
584            for ph in phdrs {
585                if ph.p_type == crate::elf::program_header::PT_LOAD && address >= ph.p_vaddr {
586                    let offset = address - ph.p_vaddr;
587                    if offset < ph.p_memsz {
588                        return ph.p_offset.checked_add(offset);
589                    }
590                }
591            }
592            None
593        }
594
595        /// Important dynamic linking info generated via a single pass through the `_DYNAMIC` array
596        #[derive(Default, PartialEq)]
597        pub struct DynamicInfo {
598            pub rela: usize,
599            pub relasz: usize,
600            pub relaent: $size,
601            pub relacount: usize,
602            pub rel: usize,
603            pub relsz: usize,
604            pub relent: $size,
605            pub relcount: usize,
606            pub gnu_hash: Option<$size>,
607            pub hash: Option<$size>,
608            pub strtab: usize,
609            pub strsz: usize,
610            pub symtab: usize,
611            pub syment: usize,
612            pub pltgot: Option<$size>,
613            pub pltrelsz: usize,
614            pub pltrel: $size,
615            pub jmprel: usize,
616            pub verdef: $size,
617            pub verdefnum: $size,
618            pub verneed: $size,
619            pub verneednum: $size,
620            pub versym: $size,
621            pub init: $size,
622            pub fini: $size,
623            pub init_array: $size,
624            pub init_arraysz: usize,
625            pub fini_array: $size,
626            pub fini_arraysz: usize,
627            pub needed_count: usize,
628            pub flags: $size,
629            pub flags_1: $size,
630            pub soname: usize,
631            pub textrel: bool,
632        }
633
634        impl DynamicInfo {
635            #[inline]
636            pub fn update(&mut self, phdrs: &[$phdr], dynamic: &Dyn) {
637                match u64::from(dynamic.d_tag) {
638                    DT_RELA => self.rela = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rela.dyn
639                    DT_RELASZ => self.relasz = dynamic.d_val as usize,
640                    DT_RELAENT => self.relaent = dynamic.d_val as _,
641                    DT_RELACOUNT => self.relacount = dynamic.d_val as usize,
642                    DT_REL => self.rel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize, // .rel.dyn
643                    DT_RELSZ => self.relsz = dynamic.d_val as usize,
644                    DT_RELENT => self.relent = dynamic.d_val as _,
645                    DT_RELCOUNT => self.relcount = dynamic.d_val as usize,
646                    DT_GNU_HASH => self.gnu_hash = vm_to_offset(phdrs, dynamic.d_val),
647                    DT_HASH => self.hash = vm_to_offset(phdrs, dynamic.d_val),
648                    DT_STRTAB => {
649                        self.strtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
650                    }
651                    DT_STRSZ => self.strsz = dynamic.d_val as usize,
652                    DT_SYMTAB => {
653                        self.symtab = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
654                    }
655                    DT_SYMENT => self.syment = dynamic.d_val as usize,
656                    DT_PLTGOT => self.pltgot = vm_to_offset(phdrs, dynamic.d_val),
657                    DT_PLTRELSZ => self.pltrelsz = dynamic.d_val as usize,
658                    DT_PLTREL => self.pltrel = dynamic.d_val as _,
659                    DT_JMPREL => {
660                        self.jmprel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize
661                    } // .rela.plt
662                    DT_VERDEF => self.verdef = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
663                    DT_VERDEFNUM => self.verdefnum = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
664                    DT_VERNEED => self.verneed = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
665                    DT_VERNEEDNUM => self.verneednum = dynamic.d_val as _,
666                    DT_VERSYM => self.versym = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
667                    DT_INIT => self.init = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
668                    DT_FINI => self.fini = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0),
669                    DT_INIT_ARRAY => {
670                        self.init_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0)
671                    }
672                    DT_INIT_ARRAYSZ => self.init_arraysz = dynamic.d_val as _,
673                    DT_FINI_ARRAY => {
674                        self.fini_array = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0)
675                    }
676                    DT_FINI_ARRAYSZ => self.fini_arraysz = dynamic.d_val as _,
677                    DT_NEEDED => self.needed_count += 1,
678                    DT_FLAGS => self.flags = dynamic.d_val as _,
679                    DT_FLAGS_1 => self.flags_1 = dynamic.d_val as _,
680                    DT_SONAME => self.soname = dynamic.d_val as _,
681                    DT_TEXTREL => self.textrel = true,
682                    _ => (),
683                }
684            }
685            pub fn new(dynamic: &[Dyn], phdrs: &[$phdr]) -> DynamicInfo {
686                let mut info = DynamicInfo::default();
687                for dyna in dynamic {
688                    info.update(phdrs, &dyna);
689                }
690                info
691            }
692        }
693
694        if_alloc! {
695            impl fmt::Debug for DynamicInfo {
696                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697                    let gnu_hash = self.gnu_hash.unwrap_or(0);
698                    let hash = self.hash.unwrap_or(0);
699                    let pltgot = self.pltgot.unwrap_or(0);
700
701                    let flags: Vec<&'static str> = [DF_ORIGIN, DF_SYMBOLIC, DF_TEXTREL, DF_BIND_NOW, DF_STATIC_TLS,][..]
702                                    .iter()
703                                    .filter(|f| (self.flags as u64 & *f) != 0)
704                                    .map(|f| df_tag_to_str(*f))
705                                    .collect();
706
707                    let flags_1: Vec<&'static str> = [
708                            DF_1_NOW,
709                            DF_1_GLOBAL,
710                            DF_1_GROUP,
711                            DF_1_NODELETE,
712                            DF_1_LOADFLTR,
713                            DF_1_INITFIRST,
714                            DF_1_NOOPEN,
715                            DF_1_ORIGIN,
716                            DF_1_DIRECT,
717                            DF_1_TRANS,
718                            DF_1_INTERPOSE,
719                            DF_1_NODEFLIB,
720                            DF_1_NODUMP,
721                            DF_1_CONFALT,
722                            DF_1_ENDFILTEE,
723                            DF_1_DISPRELDNE,
724                            DF_1_DISPRELPND,
725                            DF_1_NODIRECT,
726                            DF_1_IGNMULDEF,
727                            DF_1_NOKSYMS,
728                            DF_1_NOHDR,
729                            DF_1_EDITED,
730                            DF_1_NORELOC,
731                            DF_1_SYMINTPOSE,
732                            DF_1_GLOBAUDIT,
733                            DF_1_SINGLETON,
734                            DF_1_PIE,
735                        ][..]
736                        .iter()
737                        .filter(|f| (self.flags_1 as u64 & *f) != 0)
738                        .map(|f| df_1_tag_to_str(*f))
739                        .collect();
740
741                    f.debug_struct("DynamicInfo")
742                        .field("rela", &format_args!("0x{:x}", self.rela))
743                        .field("relasz", &self.relasz)
744                        .field("relaent", &self.relaent)
745                        .field("relacount", &self.relacount)
746                        .field("gnu_hash", &format_args!("0x{:x}", gnu_hash))
747                        .field("hash", &format_args!("0x{:x}", hash))
748                        .field("strtab", &format_args!("0x{:x}", self.strtab))
749                        .field("strsz", &self.strsz)
750                        .field("symtab", &format_args!("0x{:x}", self.symtab))
751                        .field("syment", &self.syment)
752                        .field("pltgot", &format_args!("0x{:x}", pltgot))
753                        .field("pltrelsz", &self.pltrelsz)
754                        .field("pltrel", &self.pltrel)
755                        .field("jmprel", &format_args!("0x{:x}", self.jmprel))
756                        .field("verdef", &format_args!("0x{:x}", self.verdef))
757                        .field("verdefnum", &self.verdefnum)
758                        .field("verneed", &format_args!("0x{:x}", self.verneed))
759                        .field("verneednum", &self.verneednum)
760                        .field("versym", &format_args!("0x{:x}", self.versym))
761                        .field("init", &format_args!("0x{:x}", self.init))
762                        .field("fini", &format_args!("0x{:x}", self.fini))
763                        .field("init_array", &format_args!("{:#x}", self.init_array))
764                        .field("init_arraysz", &self.init_arraysz)
765                        .field("needed_count", &self.needed_count)
766                        .field("flags", &format_args!("{:#0width$x} {:?}", self.flags, flags, width = core::mem::size_of_val(&self.flags)))
767                        .field("flags_1", &format_args!("{:#0width$x} {:?}", self.flags_1, flags_1, width = core::mem::size_of_val(&self.flags_1)))
768                        .field("soname", &self.soname)
769                        .field("textrel", &self.textrel)
770                        .finish()
771                }
772            }
773        }
774    };
775}
776
777if_alloc! {
778    elf_dynamic_info_std_impl!(u64, crate::elf::program_header::ProgramHeader);
779}
780
781pub mod dyn32 {
782    pub use crate::elf::dynamic::*;
783
784    elf_dyn!(u32);
785
786    pub const SIZEOF_DYN: usize = 8;
787
788    elf_dyn_std_impl!(u32, crate::elf32::program_header::ProgramHeader);
789    elf_dynamic_info_std_impl!(
790        u32,
791        crate::elf::program_header::program_header32::ProgramHeader
792    );
793}
794
795pub mod dyn64 {
796    pub use crate::elf::dynamic::*;
797
798    elf_dyn!(u64);
799
800    pub const SIZEOF_DYN: usize = 16;
801
802    elf_dyn_std_impl!(u64, crate::elf64::program_header::ProgramHeader);
803    elf_dynamic_info_std_impl!(
804        u64,
805        crate::elf::program_header::program_header64::ProgramHeader
806    );
807}