plt_rs/
lib.rs

1use std::borrow::Cow;
2use thiserror::Error;
3
4#[cfg(target_pointer_width = "64")]
5pub mod elf64;
6#[cfg(target_pointer_width = "64")]
7use elf64 as elf;
8
9#[cfg(target_pointer_width = "32")]
10pub mod elf32;
11#[cfg(target_pointer_width = "32")]
12use elf32 as elf;
13
14/// Errors related to dynamic libraries
15#[derive(Debug, Error)]
16pub enum DynamicError {
17    /// Tried to cast from a raw type section and was unmapped
18    #[error("Unknown type witnessed: `{0}`")]
19    TypeCast(#[from] elf::DynTypeError),
20
21    /// Given prescence of `0` section type, `1` section type would be required
22    #[error("Given the prescence of `{0:#?}`, expected prescence of `{1:#?}`")]
23    DependentSection(DynamicSectionType, DynamicSectionType),
24
25    #[error("Failed to parse, required section missing `{0:#?}`")]
26    RequiredSection(DynamicSectionType),
27
28    #[error("No dynamic program header available")]
29    ProgramHeader,
30}
31
32/// Section type enumeration
33#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
34#[allow(non_camel_case_types)]
35pub enum DynamicSectionType {
36    DT_NULL,
37    DT_PLTRELSZ,
38    DT_PLTGOT,
39    DT_PLTREL,
40
41    DT_STRTAB,
42    DT_SYMTAB,
43    DT_SYMENT,
44
45    DT_RELA,
46    DT_RELASZ,
47    DT_RELAENT,
48
49    DT_REL,
50    DT_RELSZ,
51    DT_RELENT,
52
53    DT_STRSZ,
54    DT_JMPREL,
55}
56
57/// Container of Dynamic Relocations
58pub struct DynamicRelocations<'a> {
59    inner: &'a [elf::DynRel],
60}
61
62impl DynamicRelocations<'_> {
63    /// Extract string from table starting at carrot position
64    pub fn read_at(&self, index: usize) -> Option<&elf::DynRel> {
65        self.inner.get(index)
66    }
67
68    /// Dynamic relocations internal slice
69    pub fn entries(&self) -> &[elf::DynRel] {
70        self.inner
71    }
72}
73
74/// Container of Dynamic Addend Relocations
75pub struct DynamicAddendRelocations<'a> {
76    inner: &'a [elf::DynRela],
77}
78
79impl DynamicAddendRelocations<'_> {
80    /// Extract string from table starting at carrot position
81    pub fn read_at(&self, index: usize) -> Option<&elf::DynRela> {
82        self.inner.get(index)
83    }
84
85    /// Dynamic addend relocations internal slice
86    pub fn entries(&self) -> &[elf::DynRela] {
87        self.inner
88    }
89}
90
91/// Container of Dynamic Symbols
92pub struct DynamicSymbols<'a> {
93    inner: &'a elf::DynSym,
94}
95
96impl DynamicSymbols<'_> {
97    /// gets the dynamic symbol at this index
98    fn get(&self, index: usize) -> Option<&elf::DynSym> {
99        unsafe { (self.inner as *const elf::DynSym).add(index).as_ref() }
100    }
101
102    /// resolves the name of the dynamic symbol at `index`
103    pub fn resolve_name<'b>(
104        &'b self,
105        index: usize,
106        string_table: &'b StringTable<'b>,
107    ) -> Option<Cow<str>> {
108        let entry = self.get(index)?;
109        string_table.read_at(entry.st_name as usize)
110    }
111}
112
113/// Container of Dynamic Entries
114pub struct DynamicSection<'a> {
115    inner: &'a elf::DynEntry,
116}
117
118#[derive(Debug)]
119/// A view of the Library's String table
120/// The inner `raw` reference refers to a continguous array of zero terminated strings.
121/// The string table view is needed to arbitrarily access into the data and pull out the null terminated strings.
122pub struct StringTable<'a> {
123    raw: &'a [libc::c_char],
124}
125
126impl<'a> StringTable<'a> {
127    /// Extract string from table starting at carrot position
128    pub fn read_at(&self, carrot: usize) -> Option<Cow<str>> {
129        match carrot >= self.raw.len() {
130            true => None,
131            false => unsafe { Some(std::ffi::CStr::from_ptr(&self.raw[carrot]).to_string_lossy()) },
132        }
133    }
134
135    /// total size of the string table in memory.
136    /// This does not reflect how many strings
137    pub fn total_size(&self) -> usize {
138        self.raw.len()
139    }
140}
141
142impl DynamicSection<'_> {
143    /// Iterate dynamic section's DynEntry link list attempting to find section with target section type
144    fn find_section(&self, tag: DynamicSectionType) -> Option<&elf::DynEntry> {
145        let mut current = Some(self.inner);
146        while let Some(inner) = current {
147            match DynamicSectionType::try_from(inner.d_tag) {
148                Ok(DynamicSectionType::DT_NULL) => return None,
149                Ok(this_tag) if this_tag == tag => return Some(inner),
150                Ok(_) => {
151                    // nothing to do.
152                }
153                Err(_err) => {
154                    // continue for now...;
155                }
156            }
157
158            current = unsafe { (inner as *const elf::DynEntry).offset(1).as_ref() };
159        }
160
161        None
162    }
163}
164
165/// An Elf Program Header
166/// Primary examples are PT_LOAD and PT_DYNAMIC
167pub struct ProgramHeader<'a> {
168    inner: &'a elf::ProgramHeader,
169}
170
171impl ProgramHeader<'_> {
172    /// Access the program headers type
173    pub fn header_type(&self) -> elf::Word {
174        self.inner.p_type
175    }
176
177    /// Access the program headers virtual address
178    pub fn virtual_addr(&self) -> usize {
179        self.inner.p_vaddr as usize
180    }
181
182    /// Total size in memory
183    pub fn memory_size(&self) -> usize {
184        self.inner.p_memsz as usize
185    }
186
187    /// File size
188    pub fn file_size(&self) -> usize {
189        self.inner.p_filesz as usize
190    }
191
192    /// Program headers absolute address
193    pub fn program_addr(&self) -> usize {
194        self.inner.p_paddr as usize
195    }
196
197    /// Program headers offset
198    pub fn offset(&self) -> usize {
199        self.inner.p_offset as usize
200    }
201}
202
203/// A dynamic libraries plt maybe be addend entries or non addend entries
204pub enum RelocationTable<'a> {
205    WithAddend(DynamicAddendRelocations<'a>),
206    WithoutAddend(DynamicRelocations<'a>),
207}
208
209/// Dynamic Library Entry
210/// An 'upgraded' LibraryEntry with the dynamic section resolved.
211pub struct DynamicLibrary<'a> {
212    library: LoadedLibrary<'a>,
213    dyn_section: DynamicSection<'a>,
214    dyn_string_table: StringTable<'a>,
215
216    dyn_symbols: Option<DynamicSymbols<'a>>,
217    dyn_relocs: Option<DynamicRelocations<'a>>,
218    dyn_addend_relocs: Option<DynamicAddendRelocations<'a>>,
219    dyn_plt: Option<RelocationTable<'a>>,
220}
221
222/// Access the libraries dynamic symbols through the library's dynamic section
223fn extract_dyn_symbols<'a, 'b>(
224    lib: &'a LoadedLibrary<'a>,
225    dynamic_section: &'a DynamicSection<'a>,
226) -> std::result::Result<Option<DynamicSymbols<'b>>, DynamicError> {
227    // No actual requirement for dynamic symbols.
228    let Some(dyn_symbol_table) = dynamic_section.find_section(DynamicSectionType::DT_SYMTAB) else {
229        return Ok(None);
230    };
231
232    // We use explicit Elf Dynamic entry structs.
233    // The SYMENT size doesn't seem relevant anymore, so we can assert it the same size as the dyn entry to combat any egregious misusages
234    let table_size = dynamic_section
235        .find_section(DynamicSectionType::DT_SYMENT)
236        .ok_or(DynamicError::DependentSection(
237            DynamicSectionType::DT_SYMTAB,
238            DynamicSectionType::DT_SYMENT,
239        ))?
240        .d_val_ptr as usize;
241    assert_eq!(table_size, std::mem::size_of::<elf::DynSym>());
242
243    // We don't have enough information to tell if this elf represents an Object Mapped or Shared Library / Executable mapped entry
244    // For object mapped the ptr's are relative. So we have to rebase by the virtual address from dl_info
245    let dyn_sym_ptr = match dyn_symbol_table.d_val_ptr as usize <= lib.addr() {
246        false => dyn_symbol_table.d_val_ptr as usize,
247        true => dyn_symbol_table.d_val_ptr as usize + lib.addr(),
248    } as *const elf::DynSym;
249
250    Ok(Some(DynamicSymbols {
251        inner: unsafe { dyn_sym_ptr.as_ref().unwrap() },
252    }))
253}
254
255/// Access the libraries dynamic section by dereferencing the PT_DYN program header's virtual address value
256fn extract_dyn_section<'a, 'b>(
257    lib: &'a LoadedLibrary<'a>,
258) -> std::result::Result<DynamicSection<'b>, DynamicError> {
259    let dynamic_header = lib
260        .program_headers()
261        .find(|p_h| p_h.header_type() == 0x02)
262        .ok_or(DynamicError::ProgramHeader)?;
263
264    let dynamic_sections = lib.addr() + dynamic_header.virtual_addr();
265    let dynamic_sections = dynamic_sections as *const elf::DynEntry;
266    Ok(DynamicSection {
267        inner: unsafe { dynamic_sections.as_ref().unwrap() },
268    })
269}
270
271/// Access the libraries string table through the library's dynamic section
272fn extract_dyn_string_table<'a, 'b>(
273    lib: &'a LoadedLibrary<'a>,
274    dynamic_section: &'a DynamicSection<'a>,
275) -> std::result::Result<StringTable<'b>, DynamicError> {
276    let str_table_entry = dynamic_section
277        .find_section(DynamicSectionType::DT_STRTAB)
278        .ok_or(DynamicError::RequiredSection(DynamicSectionType::DT_STRTAB))?;
279    let table_size = dynamic_section
280        .find_section(DynamicSectionType::DT_STRSZ)
281        .ok_or(DynamicError::DependentSection(
282            DynamicSectionType::DT_STRTAB,
283            DynamicSectionType::DT_STRSZ,
284        ))?
285        .d_val_ptr as usize;
286
287    // We don't have enough information to tell if this elf represents an Object Mapped or Shared Library / Executable mapped entry
288    // For object mapped the ptr's are relative. So we have to rebase by the virtual address from dl_info
289    let str_table_ptr = match str_table_entry.d_val_ptr as usize <= lib.addr() {
290        false => str_table_entry.d_val_ptr as usize,
291        true => str_table_entry.d_val_ptr as usize + lib.addr(),
292    } as *const libc::c_char;
293
294    Ok(StringTable {
295        raw: unsafe { std::slice::from_raw_parts(str_table_ptr, table_size) },
296    })
297}
298
299/// Access the libaries dynamic relocations through the dynamic program header
300fn extract_dyn_relocs<'a, 'b>(
301    lib: &'a LoadedLibrary<'a>,
302    dynamic_section: &'a DynamicSection<'a>,
303) -> std::result::Result<Option<DynamicRelocations<'b>>, DynamicError> {
304    let Some(dyn_rel_entry) = dynamic_section.find_section(DynamicSectionType::DT_REL) else {
305        return Ok(None);
306    };
307
308    let total_size = dynamic_section
309        .find_section(DynamicSectionType::DT_RELSZ)
310        .ok_or(DynamicError::DependentSection(
311            DynamicSectionType::DT_REL,
312            DynamicSectionType::DT_RELSZ,
313        ))?
314        .d_val_ptr as usize;
315    let entry_size = dynamic_section
316        .find_section(DynamicSectionType::DT_RELENT)
317        .ok_or(DynamicError::DependentSection(
318            DynamicSectionType::DT_REL,
319            DynamicSectionType::DT_RELENT,
320        ))?
321        .d_val_ptr as usize;
322
323    assert_eq!(entry_size, std::mem::size_of::<elf::DynRel>());
324
325    let entry_count = total_size / entry_size;
326    // We don't have enough information to tell if this elf represents an Object Mapped or Shared Library / Executable mapped entry
327    // For object mapped the ptr's are relative. So we have to rebase by the virtual address from dl_info
328    let dyn_rel_entry = match dyn_rel_entry.d_val_ptr as usize <= lib.addr() {
329        false => dyn_rel_entry.d_val_ptr as usize,
330        true => dyn_rel_entry.d_val_ptr as usize + lib.addr(),
331    } as *const elf::DynRel;
332
333    Ok(Some(DynamicRelocations {
334        inner: unsafe { std::slice::from_raw_parts(dyn_rel_entry, entry_count) },
335    }))
336}
337
338/// Access the libaries dynamic addend relocations through the dynamic program header
339fn extract_dyn_addend_relocs<'a, 'b>(
340    lib: &'a LoadedLibrary<'a>,
341    dynamic_section: &'a DynamicSection<'a>,
342) -> std::result::Result<Option<DynamicAddendRelocations<'b>>, DynamicError> {
343    let Some(dyn_rel_entry) = dynamic_section.find_section(DynamicSectionType::DT_RELA) else {
344        return Ok(None);
345    };
346
347    let total_size = dynamic_section
348        .find_section(DynamicSectionType::DT_RELASZ)
349        .ok_or(DynamicError::DependentSection(
350            DynamicSectionType::DT_RELA,
351            DynamicSectionType::DT_RELASZ,
352        ))?
353        .d_val_ptr as usize;
354    let entry_size = dynamic_section
355        .find_section(DynamicSectionType::DT_RELAENT)
356        .ok_or(DynamicError::DependentSection(
357            DynamicSectionType::DT_RELA,
358            DynamicSectionType::DT_RELAENT,
359        ))?
360        .d_val_ptr as usize;
361
362    assert_eq!(entry_size, std::mem::size_of::<elf::DynRela>());
363
364    let entry_count = total_size / entry_size;
365    // We don't have enough information to tell if this elf represents an Object Mapped or Shared Library / Executable mapped entry
366    // For object mapped the ptr's are relative. So we have to rebase by the virtual address from dl_info
367    let dyn_rel_entry = match dyn_rel_entry.d_val_ptr as usize <= lib.addr() {
368        false => dyn_rel_entry.d_val_ptr as usize,
369        true => dyn_rel_entry.d_val_ptr as usize + lib.addr(),
370    } as *const elf::DynRela;
371
372    Ok(Some(DynamicAddendRelocations {
373        inner: unsafe { std::slice::from_raw_parts(dyn_rel_entry, entry_count) },
374    }))
375}
376
377/// Access the libraries plt relocations
378fn extract_dyn_plt<'a, 'b>(
379    lib: &'a LoadedLibrary<'a>,
380    dynamic_section: &'a DynamicSection<'a>,
381) -> std::result::Result<Option<RelocationTable<'b>>, DynamicError> {
382    // decipher if its rel or rela relocation entries
383    // if this isn't present we can't have a plt
384    let Some(dyn_type) = dynamic_section.find_section(DynamicSectionType::DT_PLTREL) else {
385        return Ok(None);
386    };
387
388    let relocation_type = DynamicSectionType::try_from(dyn_type.d_val_ptr)?;
389
390    let dyn_plt_entry = dynamic_section
391        .find_section(DynamicSectionType::DT_JMPREL)
392        .ok_or(DynamicError::DependentSection(
393            DynamicSectionType::DT_PLTREL,
394            DynamicSectionType::DT_JMPREL,
395        ))?;
396    let total_size = dynamic_section
397        .find_section(DynamicSectionType::DT_PLTRELSZ)
398        .ok_or(DynamicError::DependentSection(
399            DynamicSectionType::DT_PLTREL,
400            DynamicSectionType::DT_PLTRELSZ,
401        ))?
402        .d_val_ptr as usize;
403
404    let entry_addr = match dyn_plt_entry.d_val_ptr as usize <= lib.addr() {
405        false => dyn_plt_entry.d_val_ptr as usize,
406        true => dyn_plt_entry.d_val_ptr as usize + lib.addr(),
407    };
408
409    Ok(match relocation_type {
410        DynamicSectionType::DT_REL => {
411            let entry_count = total_size / std::mem::size_of::<elf::DynRel>();
412            Some(RelocationTable::WithoutAddend(DynamicRelocations {
413                inner: unsafe {
414                    std::slice::from_raw_parts(entry_addr as *const elf::DynRel, entry_count)
415                },
416            }))
417        }
418        DynamicSectionType::DT_RELA => {
419            let entry_count = total_size / std::mem::size_of::<elf::DynRela>();
420            Some(RelocationTable::WithAddend(DynamicAddendRelocations {
421                inner: unsafe {
422                    std::slice::from_raw_parts(entry_addr as *const elf::DynRela, entry_count)
423                },
424            }))
425        }
426        _ => None,
427    })
428}
429
430impl<'a> DynamicLibrary<'a> {
431    /// Try to consume a LoadedLibrary and create a resolved Dynamic view
432    /// The Dynamic Library will take ownership of the load library as well as store
433    /// all relevant dynamic sections for easy access and symbol resolution
434    pub fn initialize(lib: LoadedLibrary<'a>) -> std::result::Result<Self, DynamicError> {
435        let dyn_section = extract_dyn_section(&lib)?;
436        let dyn_string_table = extract_dyn_string_table(&lib, &dyn_section)?;
437        let dyn_symbols = extract_dyn_symbols(&lib, &dyn_section)?;
438        let dyn_relocs = extract_dyn_relocs(&lib, &dyn_section)?;
439        let dyn_addend_relocs = extract_dyn_addend_relocs(&lib, &dyn_section)?;
440        let dyn_plt = extract_dyn_plt(&lib, &dyn_section)?;
441
442        Ok(Self {
443            library: lib,
444            dyn_section,
445            dyn_string_table,
446            dyn_symbols,
447            dyn_relocs,
448            dyn_addend_relocs,
449            dyn_plt,
450        })
451    }
452
453    /// Finding target function differs on 32 bit and 64 bit.
454    /// On 32 bit we want to check the relocations table only, opposed to the addend relocations table.
455    /// Additionally, we will fall back to the plt given it is an addendless relocation table.
456    #[cfg(target_pointer_width = "32")]
457    pub fn try_find_function(&self, symbol_name: &str) -> Option<&'_ elf32::DynRel> {
458        let string_table = self.string_table();
459        let dyn_symbols = self.symbols()?;
460        if let Some(dyn_relas) = self.relocs() {
461            let dyn_relas = dyn_relas.entries().iter();
462            if let Some(symbol) = dyn_relas
463                .flat_map(|e| {
464                    dyn_symbols
465                        .resolve_name(e.symbol_index() as usize, string_table)
466                        .map(|s| (e, s))
467                })
468                .filter(|(_, s)| s.eq(symbol_name))
469                .next()
470                .map(|(target_function, _)| target_function)
471            {
472                return Some(symbol);
473            }
474        }
475
476        if let Some(dyn_relas) = self.plt_rel() {
477            let dyn_relas = dyn_relas.entries().iter();
478            if let Some(symbol) = dyn_relas
479                .flat_map(|e| {
480                    dyn_symbols
481                        .resolve_name(e.symbol_index() as usize, string_table)
482                        .map(|s| (e, s))
483                })
484                .filter(|(_, s)| s.eq(symbol_name))
485                .next()
486                .map(|(target_function, _)| target_function)
487            {
488                return Some(symbol);
489            }
490        }
491        None
492    }
493
494    /// Finding target function differs on 32 bit and 64 bit.
495    /// On 64 bit we want to check the addended relocations table only, opposed to the addendless relocations table.
496    /// Additionally, we will fall back to the plt given it is an addended relocation table.
497    #[cfg(target_pointer_width = "64")]
498    pub fn try_find_function(&self, symbol_name: &str) -> Option<&'_ elf64::DynRela> {
499        let string_table = self.string_table();
500        let symbols = self.symbols()?;
501        if let Some(dyn_relas) = self.addend_relocs() {
502            let dyn_relas = dyn_relas.entries().iter();
503            if let Some(symbol) = dyn_relas
504                .flat_map(|e| {
505                    symbols
506                        .resolve_name(e.symbol_index() as usize, string_table)
507                        .map(|s| (e, s))
508                })
509                .find(|(_, s)| s.eq(symbol_name))
510                .map(|(target_function, _)| target_function)
511            {
512                return Some(symbol);
513            }
514        }
515
516        if let Some(dyn_relas) = self.plt_rela() {
517            let dyn_relas = dyn_relas.entries().iter();
518            if let Some(symbol) = dyn_relas
519                .flat_map(|e| {
520                    symbols
521                        .resolve_name(e.symbol_index() as usize, string_table)
522                        .map(|s| (e, s))
523                })
524                .find(|(_, s)| s.eq(symbol_name))
525                .map(|(target_function, _)| target_function)
526            {
527                return Some(symbol);
528            }
529        }
530        None
531    }
532    /// Access the plt as a dynamic relocation table if possible
533    /// can fail if the plt is not available or the plt is with addend
534    pub fn plt_rel(&self) -> Option<&DynamicRelocations<'_>> {
535        match self.plt() {
536            Some(RelocationTable::WithoutAddend(relocs)) => Some(relocs),
537            _ => None,
538        }
539    }
540
541    /// Access the plt as a dynamic addend relocation table if possible
542    /// can fail if the plt is not available or the plt is without addend
543    pub fn plt_rela(&self) -> Option<&DynamicAddendRelocations<'_>> {
544        match self.plt() {
545            Some(RelocationTable::WithAddend(relocs)) => Some(relocs),
546            _ => None,
547        }
548    }
549    /// Access the dynamic libraries plt if available
550    /// Can be either a DynamicRelocations or DynamicAddendRelocations
551    pub fn plt(&self) -> Option<&RelocationTable<'_>> {
552        self.dyn_plt.as_ref()
553    }
554
555    /// Access the dynamic libraries relocations if available
556    pub fn relocs(&self) -> Option<&DynamicRelocations<'_>> {
557        self.dyn_relocs.as_ref()
558    }
559
560    /// Access the dynamic libraries addend relocations if available
561    pub fn addend_relocs(&self) -> Option<&DynamicAddendRelocations<'_>> {
562        self.dyn_addend_relocs.as_ref()
563    }
564
565    /// Access the dynamic libraries symbol table if available
566    pub fn symbols(&self) -> Option<&DynamicSymbols<'_>> {
567        self.dyn_symbols.as_ref()
568    }
569
570    /// Access the dynamic libraries dynamic section
571    pub fn dyn_section(&self) -> &DynamicSection<'_> {
572        &self.dyn_section
573    }
574
575    /// Access the dynamic libraries backing general loaded library structure
576    /// capable of providing the name and base address of the in memory
577    pub fn library(&self) -> &LoadedLibrary<'_> {
578        &self.library
579    }
580
581    /// Access the dynamic string table
582    pub fn string_table(&self) -> &StringTable<'_> {
583        &self.dyn_string_table
584    }
585}
586
587/// A library loaded in the process
588pub struct LoadedLibrary<'a> {
589    addr: usize,
590    name: Cow<'a, str>,
591    program_headers: &'a [elf::ProgramHeader],
592}
593
594impl LoadedLibrary<'_> {
595    /// Access the libraries string name
596    /// This is more the libraries `path` than the name per say
597    pub fn name(&self) -> &str {
598        &self.name
599    }
600
601    /// Access the libraries virtual address
602    pub fn addr(&self) -> usize {
603        self.addr
604    }
605
606    /// Iterate the libraries program headers
607    pub fn program_headers(&self) -> impl Iterator<Item = ProgramHeader<'_>> {
608        self.program_headers
609            .iter()
610            .map(|header| ProgramHeader { inner: header })
611    }
612
613    /// Access the libraries PT_INTERP program headers
614    pub fn interpreter_header(&self) -> Option<ProgramHeader<'_>> {
615        self.program_headers().find(|p_h| p_h.header_type() == 0x03)
616    }
617
618    /// Access the libraries PT_LOAD program headers
619    pub fn load_headers(&self) -> impl Iterator<Item = ProgramHeader<'_>> {
620        self.program_headers()
621            .filter(|p_h| p_h.header_type() == 0x01)
622    }
623}
624
625/// Returns a `Vec` of objects loaded into the current address space.
626pub fn collect_modules<'a>() -> Vec<LoadedLibrary<'a>> {
627    let mut ret = Vec::new();
628
629    // Pushes an `Object` into the result vector on the behalf of C.
630    extern "C" fn push_object(objs: &mut Vec<LoadedLibrary>, dl_info: &libc::dl_phdr_info) {
631        let name = unsafe { std::ffi::CStr::from_ptr(dl_info.dlpi_name) }.to_string_lossy();
632        // We have to copy sthe `dl_phdr_info` struct out, as the same memory buffer is used for
633        // each entry during the iteration process. Otherwise we could have used a vector of
634        // pointers.
635        println!("{} {}", dl_info.dlpi_addr, dl_info.dlpi_phnum);
636
637        if dl_info.dlpi_phnum == 0 {
638            return;
639        }
640
641        let program_headers =
642            unsafe { std::slice::from_raw_parts(dl_info.dlpi_phdr, dl_info.dlpi_phnum as usize) };
643        objs.push(LoadedLibrary {
644            addr: dl_info.dlpi_addr as usize,
645            name,
646            program_headers,
647        });
648    }
649
650    // Callback for `dl_iterate_phdr(3)`.
651    unsafe extern "C" fn collect_objs(
652        info: *mut libc::dl_phdr_info,
653        _sz: usize,
654        data: *mut libc::c_void,
655    ) -> libc::c_int {
656        if let Some(info) = unsafe { info.as_ref() } {
657            push_object(&mut *(data as *mut Vec<LoadedLibrary>), info); // Get Rust to push the object.
658        };
659
660        0
661    }
662
663    let ret_void_p = &mut ret as *mut Vec<LoadedLibrary> as *mut libc::c_void;
664    unsafe { libc::dl_iterate_phdr(Some(collect_objs), ret_void_p) };
665
666    ret
667}