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/tests/task_dumper.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.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/tests/task_dumper.rs')
-rw-r--r-- | third_party/rust/minidump-writer/tests/task_dumper.rs | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/third_party/rust/minidump-writer/tests/task_dumper.rs b/third_party/rust/minidump-writer/tests/task_dumper.rs new file mode 100644 index 0000000000..1411acc34a --- /dev/null +++ b/third_party/rust/minidump-writer/tests/task_dumper.rs @@ -0,0 +1,158 @@ +//! All of these tests are specific to the MacOS task dumper +#![cfg(target_os = "macos")] + +use minidump_writer::{mach::LoadCommand, task_dumper::TaskDumper}; +use std::fmt::Write; + +fn call_otool(args: &[&str]) -> String { + let mut cmd = std::process::Command::new("otool"); + cmd.args(args); + + let exe_path = std::env::current_exe().expect("unable to retrieve test executable path"); + cmd.arg(exe_path); + + let output = cmd.output().expect("failed to spawn otool"); + + assert!(output.status.success()); + + String::from_utf8(output.stdout).expect("stdout was invalid utf-8") +} + +/// Validates we can iterate the load commands for all of the images in the task +#[test] +fn iterates_load_commands() { + let lc_str = call_otool(&["-l"]); + + let mut expected = String::new(); + let mut lc_index = 0; + + expected.push('\n'); + + while let Some(nlc) = lc_str[lc_index..].find("Load command ") { + lc_index += nlc; + + let block = match lc_str[lc_index + 13..].find("Load command ") { + Some(ind) => &lc_str[lc_index + 13..lc_index + 13 + ind], + None => &lc_str[lc_index..], + }; + + // otool prints the load command index for each command, but we only + // handle the small subset of the available load commands we care about + // so just ignore that + let block = &block[block.find('\n').unwrap() + 1..]; + + // otool also prints all the sections for LC_SEGMENT_* commands, but + // we don't care about those, so ignore them + let block = match block.find("Section") { + Some(ind) => &block[..ind], + None => block, + }; + + lc_index += 13; + + let cmd = block + .find("cmd ") + .expect("load commnd didn't specify cmd kind"); + let cmd_end = block[cmd..] + .find('\n') + .expect("load cmd didn't end with newline"); + if matches!( + &block[cmd + 4..cmd + cmd_end], + "LC_SEGMENT_64" | "LC_UUID" | "LC_ID_DYLIB" | "LC_LOAD_DYLINKER" + ) { + expected.push_str(block); + } + } + + let task_dumper = TaskDumper::new( + // SAFETY: syscall + unsafe { mach2::traps::mach_task_self() }, + ); + + let mut actual = String::new(); + + // Unfortunately, Apple decided to move dynamic libs into a shared cache, + // removing them from the file system completely, and unless I'm missing it + // there is no way to get the load commands for the dylibs since otool + // only understands file paths? So we just get the load commands for the main + // executable instead, this means that we miss the `LC_ID_DYLIB` commands + // since they only apply to dylibs, but this test is more that we can + // correctly iterate through the load commands themselves, so this _should_ + // be fine... + let exe_img = task_dumper + .read_executable_image() + .expect("failed to read executable image"); + + { + let lcmds = task_dumper + .read_load_commands(&exe_img) + .expect("failed to read load commands"); + + for lc in lcmds.iter() { + match lc { + LoadCommand::Segment(seg) => { + let segname = std::str::from_utf8(&seg.segment_name).unwrap(); + let segname = &segname[..segname.find('\0').unwrap()]; + write!( + &mut actual, + " + cmd LC_SEGMENT_64 + cmdsize {} + segname {} + vmaddr 0x{:016x} + vmsize 0x{:016x} + fileoff {} + filesize {} + maxprot 0x{:08x} + initprot 0x{:08x} + nsects {} + flags 0x{:x}", + seg.cmd_size, + segname, + seg.vm_addr, + seg.vm_size, + seg.file_off, + seg.file_size, + seg.max_prot, + seg.init_prot, + seg.num_sections, + seg.flags, + ) + .unwrap(); + } + LoadCommand::Dylib(_dylib) => { + unreachable!(); + } + LoadCommand::Uuid(uuid) => { + let id = uuid::Uuid::from_bytes(uuid.uuid); + let mut uuid_buf = [0u8; uuid::fmt::Hyphenated::LENGTH]; + let uuid_str = id.hyphenated().encode_upper(&mut uuid_buf); + + write!( + &mut actual, + " + cmd LC_UUID + cmdsize {} + uuid {uuid_str} +", + uuid.cmd_size, + ) + .unwrap(); + } + LoadCommand::DylinkerCommand(dy_cmd) => { + write!( + &mut actual, + " + cmd LC_LOAD_DYLINKER + cmdsize {} + name {} (offset {})", + dy_cmd.cmd_size, dy_cmd.name, dy_cmd.name_offset, + ) + .unwrap(); + } + } + } + } + + similar_asserts::assert_eq!(expected, actual); +} |