diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/minidump-writer/src/linux/android.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/minidump-writer/src/linux/android.rs')
-rw-r--r-- | third_party/rust/minidump-writer/src/linux/android.rs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/third_party/rust/minidump-writer/src/linux/android.rs b/third_party/rust/minidump-writer/src/linux/android.rs new file mode 100644 index 0000000000..0c8195cc0f --- /dev/null +++ b/third_party/rust/minidump-writer/src/linux/android.rs @@ -0,0 +1,146 @@ +use crate::errors::AndroidError; +use crate::maps_reader::MappingInfo; +use crate::ptrace_dumper::PtraceDumper; +use crate::thread_info::Pid; +use goblin::elf; +#[cfg(target_pointer_width = "32")] +use goblin::elf::dynamic::dyn32::{Dyn, SIZEOF_DYN}; +#[cfg(target_pointer_width = "64")] +use goblin::elf::dynamic::dyn64::{Dyn, SIZEOF_DYN}; +#[cfg(target_pointer_width = "32")] +use goblin::elf::header::header32 as elf_header; +#[cfg(target_pointer_width = "64")] +use goblin::elf::header::header64 as elf_header; +#[cfg(target_pointer_width = "32")] +use goblin::elf::program_header::program_header32::ProgramHeader; +#[cfg(target_pointer_width = "64")] +use goblin::elf::program_header::program_header64::ProgramHeader; +use std::ffi::c_void; + +type Result<T> = std::result::Result<T, AndroidError>; + +// From /usr/include/elf.h of the android SDK +// #define DT_ANDROID_REL (DT_LOOS + 2) +// #define DT_ANDROID_RELSZ (DT_LOOS + 3) +// #define DT_ANDROID_RELA (DT_LOOS + 4) +// #define DT_ANDROID_RELASZ (DT_LOOS + 5) +#[cfg(target_pointer_width = "64")] +const DT_ANDROID_REL: u64 = elf::dynamic::DT_LOOS + 2; +#[cfg(target_pointer_width = "64")] +const DT_ANDROID_RELA: u64 = elf::dynamic::DT_LOOS + 4; +#[cfg(target_pointer_width = "32")] +const DT_ANDROID_REL: u32 = (elf::dynamic::DT_LOOS + 2) as u32; +#[cfg(target_pointer_width = "32")] +const DT_ANDROID_RELA: u32 = (elf::dynamic::DT_LOOS + 4) as u32; + +struct DynVaddresses { + min_vaddr: usize, + dyn_vaddr: usize, + dyn_count: usize, +} + +fn has_android_packed_relocations(pid: Pid, load_bias: usize, vaddrs: DynVaddresses) -> Result<()> { + let dyn_addr = load_bias + vaddrs.dyn_vaddr; + for idx in 0..vaddrs.dyn_count { + let addr = (dyn_addr + SIZEOF_DYN * idx) as *mut c_void; + let dyn_data = PtraceDumper::copy_from_process(pid, addr, SIZEOF_DYN)?; + // TODO: Couldn't find a nice way to use goblin for that, to avoid the unsafe-block + let dyn_obj: Dyn; + unsafe { + dyn_obj = std::mem::transmute::<[u8; SIZEOF_DYN], Dyn>(dyn_data.as_slice().try_into()?); + } + + if dyn_obj.d_tag == DT_ANDROID_REL || dyn_obj.d_tag == DT_ANDROID_RELA { + return Ok(()); + } + } + Err(AndroidError::NoRelFound) +} + +fn get_effective_load_bias(pid: Pid, ehdr: &elf_header::Header, address: usize) -> usize { + let ph = parse_loaded_elf_program_headers(pid, ehdr, address); + // If |min_vaddr| is non-zero and we find Android packed relocation tags, + // return the effective load bias. + + if ph.min_vaddr != 0 { + let load_bias = address - ph.min_vaddr; + if has_android_packed_relocations(pid, load_bias, ph).is_ok() { + return load_bias; + } + } + // Either |min_vaddr| is zero, or it is non-zero but we did not find the + // expected Android packed relocations tags. + address +} + +fn parse_loaded_elf_program_headers( + pid: Pid, + ehdr: &elf_header::Header, + address: usize, +) -> DynVaddresses { + let phdr_addr = address + ehdr.e_phoff as usize; + let mut min_vaddr = usize::MAX; + let mut dyn_vaddr = 0; + let mut dyn_count = 0; + + let phdr_opt = PtraceDumper::copy_from_process( + pid, + phdr_addr as *mut c_void, + elf_header::SIZEOF_EHDR * ehdr.e_phnum as usize, + ); + if let Ok(ph_data) = phdr_opt { + // TODO: The original C code doesn't have error-handling here at all. + // We silently ignore "not parsable" for now, but might bubble it up. + // TODO2: `from_bytes` might panic, `parse()` would return a Result<>, so maybe better + // to switch to that at some point. + for phdr in ProgramHeader::from_bytes(&ph_data, ehdr.e_phnum as usize) { + let p_vaddr = phdr.p_vaddr as usize; + if phdr.p_type == elf::program_header::PT_LOAD && p_vaddr < min_vaddr { + min_vaddr = p_vaddr; + } + + if phdr.p_type == elf::program_header::PT_DYNAMIC { + dyn_vaddr = p_vaddr; + dyn_count = phdr.p_memsz as usize / SIZEOF_DYN; + } + } + } + + DynVaddresses { + min_vaddr, + dyn_vaddr, + dyn_count, + } +} + +pub fn late_process_mappings(pid: Pid, mappings: &mut [MappingInfo]) -> Result<()> { + // Only consider exec mappings that indicate a file path was mapped, and + // where the ELF header indicates a mapped shared library. + for mut map in mappings + .iter_mut() + .filter(|m| m.executable && m.name.as_ref().map_or(false, |n| n.starts_with("/"))) + { + let ehdr_opt = PtraceDumper::copy_from_process( + pid, + map.start_address as *mut c_void, + elf_header::SIZEOF_EHDR, + ) + .ok() + .and_then(|x| elf_header::Header::parse(&x).ok()); + + if let Some(ehdr) = ehdr_opt { + if ehdr.e_type == elf_header::ET_DYN { + // Compute the effective load bias for this mapped library, and update + // the mapping to hold that rather than |start_addr|, at the same time + // adjusting |size| to account for the change in |start_addr|. Where + // the library does not contain Android packed relocations, + // GetEffectiveLoadBias() returns |start_addr| and the mapping entry + // is not changed. + let load_bias = get_effective_load_bias(pid, &ehdr, map.start_address); + map.size += map.start_address - load_bias; + map.start_address = load_bias; + } + } + } + Ok(()) +} |