1use elf_parse::{Elf64FileHeader, Elf64ProgramHeader};
6use std::borrow::Cow;
7use std::ffi::{c_char, c_void};
8
9mod sys;
10use sys::{Elf64_Ehdr, Elf64_Phdr, elf_search_wrapper};
11
12#[derive(Debug)]
13pub struct ModuleInfo<'a> {
14 pub name: Cow<'a, str>,
15 pub vaddr: usize,
16 pub build_id: &'a [u8],
17 pub ehdr: &'a Elf64FileHeader,
18 pub phdrs: &'a [Elf64ProgramHeader],
19}
20
21pub fn for_each_module<F: FnMut(&ModuleInfo<'_>)>(
22 process: &zx::Process,
23 mut func: F,
24) -> Result<(), zx::Status> {
25 let status = unsafe {
27 elf_search_wrapper(
28 process.raw_handle(),
29 Some(callback::<F>),
30 &mut func as *mut F as *mut c_void,
31 )
32 };
33 zx::Status::ok(status)
34}
35
36unsafe extern "C" fn callback<F: FnMut(&ModuleInfo<'_>)>(
37 name: *const c_char,
38 name_len: usize,
39 vaddr: u64,
40 build_id: *const u8,
41 build_id_len: usize,
42 ehdr: *const Elf64_Ehdr,
43 phdrs: *const Elf64_Phdr,
44 phdrs_len: usize,
45 arg: *mut c_void,
46) {
47 let name = unsafe { std::slice::from_raw_parts(name as *const u8, name_len) };
49 let build_id = unsafe { std::slice::from_raw_parts(build_id, build_id_len) };
50 let ehdr = unsafe { &*ehdr };
51 let phdrs = unsafe { std::slice::from_raw_parts(phdrs, phdrs_len) };
52
53 let ehdr: &Elf64FileHeader = zerocopy::transmute_ref!(ehdr);
55 let phdrs: &[Elf64ProgramHeader] = zerocopy::transmute_ref!(phdrs);
56
57 let info = ModuleInfo {
58 name: String::from_utf8_lossy(name),
59 vaddr: vaddr as usize,
60 build_id,
61 ehdr,
62 phdrs,
63 };
64
65 let func = unsafe { &mut *(arg as *mut F) };
67 func(&info);
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use elf_parse::{SegmentFlags, SegmentType};
74
75 #[test]
76 fn test_find_executable_regions() {
77 let mut regions = Vec::new();
79 for_each_module(&fuchsia_runtime::process_self(), |info| {
80 for phdr in info.phdrs {
81 if phdr.segment_type == SegmentType::Load as u32
82 && (phdr.flags & SegmentFlags::EXECUTE.bits()) != 0
83 {
84 let start = info.vaddr + phdr.vaddr as usize;
85 let size = phdr.memsz as usize;
86 regions.push((start, size));
87 }
88 }
89 })
90 .unwrap();
91
92 let test_address = test_find_executable_regions as *const () as usize;
94 let count = regions
95 .iter()
96 .filter(|(start, size)| (*start..*start + *size).contains(&test_address))
97 .count();
98
99 assert_eq!(
100 count, 1,
101 "test address {:x} not covered exactly once by {:?}",
102 test_address, regions
103 );
104 }
105}