diff --git a/Cargo.toml b/Cargo.toml
index 237afe6a..7f5f8966 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,22 +1,33 @@
[package]
-name = "minidump_writer_linux"
+name = "minidump-writer"
version = "0.1.0"
authors = ["Martin Sirringhaus"]
-edition = "2018"
+edition = "2021"
license = "MIT"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
-tempfile = "3.1.0"
-nix = "0.15"
-libc = "0.2.74"
-memoffset = "0.5.1"
byteorder = "1.3.2"
-memmap2 = "0.2.2"
-goblin = "0.1.2"
+cfg-if = "1.0"
+goblin = "0.5"
+libc = "0.2.74"
+memmap2 = "0.5"
+memoffset = "0.6"
+minidump-common = "0.10"
+nix = "0.23"
+scroll = "0.11"
+tempfile = "3.1.0"
thiserror = "1.0.21"
+[dependencies.crash-context]
+#git = "https://github.com/EmbarkStudios/crash-handling"
+#branch = "impl"
+path = "../crash-handling/crash-context"
+features = ["fill-minidump"]
+
[dev-dependencies]
-minidump = {git = "https://github.com/luser/rust-minidump", rev="9652b3b" }
-minidump-common = {git = "https://github.com/luser/rust-minidump", rev="9652b3b" }
+debugid = "0.7.3"
+minidump = "0.10"
+
+[patch.crates-io]
+minidump = { git = "https://github.com/EmbarkStudios/rust-minidump", branch = "master" }
+minidump-common = { git = "https://github.com/EmbarkStudios/rust-minidump", branch = "master" }
diff --git a/src/bin/test.rs b/src/bin/test.rs
index 9899e8f9..bbfe2fe8 100644
--- a/src/bin/test.rs
+++ b/src/bin/test.rs
@@ -1,10 +1,11 @@
// This binary shouldn't be under /src, but under /tests, but that is
// currently not possible (https://github.com/rust-lang/cargo/issues/4356)
-use minidump_writer_linux::linux_ptrace_dumper::{LinuxPtraceDumper, AT_SYSINFO_EHDR};
-use minidump_writer_linux::{linux_ptrace_dumper, LINUX_GATE_LIBRARY_NAME};
+use minidump_writer::{
+ ptrace_dumper::{PtraceDumper, AT_SYSINFO_EHDR},
+ LINUX_GATE_LIBRARY_NAME,
+};
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
use nix::unistd::getppid;
-use std::convert::TryInto;
use std::env;
use std::error;
use std::result;
@@ -24,13 +25,13 @@ macro_rules! test {
fn test_setup() -> Result<()> {
let ppid = getppid();
- linux_ptrace_dumper::LinuxPtraceDumper::new(ppid.as_raw())?;
+ PtraceDumper::new(ppid.as_raw())?;
Ok(())
}
fn test_thread_list() -> Result<()> {
let ppid = getppid();
- let dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid.as_raw())?;
+ let dumper = PtraceDumper::new(ppid.as_raw())?;
test!(!dumper.threads.is_empty(), "No threads")?;
test!(
dumper
@@ -46,9 +47,9 @@ fn test_thread_list() -> Result<()> {
fn test_copy_from_process(stack_var: usize, heap_var: usize) -> Result<()> {
let ppid = getppid().as_raw();
- let mut dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid)?;
+ let mut dumper = PtraceDumper::new(ppid)?;
dumper.suspend_threads()?;
- let stack_res = LinuxPtraceDumper::copy_from_process(ppid, stack_var as *mut libc::c_void, 1)?;
+ let stack_res = PtraceDumper::copy_from_process(ppid, stack_var as *mut libc::c_void, 1)?;
let expected_stack: libc::c_long = 0x11223344;
test!(
@@ -56,7 +57,7 @@ fn test_copy_from_process(stack_var: usize, heap_var: usize) -> Result<()> {
"stack var not correct"
)?;
- let heap_res = LinuxPtraceDumper::copy_from_process(ppid, heap_var as *mut libc::c_void, 1)?;
+ let heap_res = PtraceDumper::copy_from_process(ppid, heap_var as *mut libc::c_void, 1)?;
let expected_heap: libc::c_long = 0x55667788;
test!(
heap_res == expected_heap.to_ne_bytes(),
@@ -68,7 +69,7 @@ fn test_copy_from_process(stack_var: usize, heap_var: usize) -> Result<()> {
fn test_find_mappings(addr1: usize, addr2: usize) -> Result<()> {
let ppid = getppid();
- let dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid.as_raw())?;
+ let dumper = PtraceDumper::new(ppid.as_raw())?;
dumper
.find_mapping(addr1)
.ok_or("No mapping for addr1 found")?;
@@ -85,7 +86,7 @@ fn test_file_id() -> Result<()> {
let ppid = getppid().as_raw();
let exe_link = format!("/proc/{}/exe", ppid);
let exe_name = std::fs::read_link(&exe_link)?.into_os_string();
- let mut dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(getppid().as_raw())?;
+ let mut dumper = PtraceDumper::new(getppid().as_raw())?;
let mut found_exe = None;
for (idx, mapping) in dumper.mappings.iter().enumerate() {
if mapping.name.as_ref().map(|x| x.into()).as_ref() == Some(&exe_name) {
@@ -102,7 +103,7 @@ fn test_file_id() -> Result<()> {
fn test_merged_mappings(path: String, mapped_mem: usize, mem_size: usize) -> Result<()> {
// Now check that LinuxPtraceDumper interpreted the mappings properly.
- let dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(getppid().as_raw())?;
+ let dumper = PtraceDumper::new(getppid().as_raw())?;
let mut mapping_count = 0;
for map in &dumper.mappings {
if map.name == Some(path.clone()) {
@@ -120,13 +121,13 @@ fn test_merged_mappings(path: String, mapped_mem: usize, mem_size: usize) -> Res
fn test_linux_gate_mapping_id() -> Result<()> {
let ppid = getppid().as_raw();
- let mut dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid)?;
+ let mut dumper = PtraceDumper::new(ppid)?;
let mut found_linux_gate = false;
for mut mapping in dumper.mappings.clone() {
if mapping.name.as_deref() == Some(LINUX_GATE_LIBRARY_NAME) {
found_linux_gate = true;
dumper.suspend_threads()?;
- let id = LinuxPtraceDumper::elf_identifier_for_mapping(&mut mapping, ppid)?;
+ let id = PtraceDumper::elf_identifier_for_mapping(&mut mapping, ppid)?;
test!(!id.is_empty(), "id-vec is empty")?;
test!(id.iter().any(|&x| x > 0), "all id elements are 0")?;
dumper.resume_threads()?;
@@ -139,7 +140,7 @@ fn test_linux_gate_mapping_id() -> Result<()> {
fn test_mappings_include_linux_gate() -> Result<()> {
let ppid = getppid().as_raw();
- let dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid)?;
+ let dumper = PtraceDumper::new(ppid)?;
let linux_gate_loc = dumper.auxv[&AT_SYSINFO_EHDR];
test!(linux_gate_loc != 0, "linux_gate_loc == 0")?;
let mut found_linux_gate = false;
diff --git a/src/crash_context/crash_context_aarch64.rs b/src/crash_context/crash_context_aarch64.rs
deleted file mode 100644
index e6fb3f9e..00000000
--- a/src/crash_context/crash_context_aarch64.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use super::CrashContext;
-
-impl CrashContext {
- pub fn get_instruction_pointer(&self) -> usize {
- self.context.uc_mcontext.sp as usize
- }
-
- pub fn get_stack_pointer(&self) -> usize {
- self.context.uc_mcontext.pc as usize
- }
-}
diff --git a/src/crash_context/crash_context_x86.rs b/src/crash_context/crash_context_x86.rs
deleted file mode 100644
index 181deeae..00000000
--- a/src/crash_context/crash_context_x86.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use super::CrashContext;
-use crate::minidump_cpu::imp::*;
-use crate::minidump_cpu::RawContextCPU;
-use libc::{
- REG_CS, REG_DS, REG_EAX, REG_EBP, REG_EBX, REG_ECX, REG_EDI, REG_EDX, REG_EFL, REG_EIP, REG_ES,
- REG_ESI, REG_ESP, REG_FS, REG_GS, REG_SS, REG_UESP,
-};
-impl CrashContext {
- pub fn get_instruction_pointer(&self) -> usize {
- self.context.uc_mcontext.gregs[REG_EIP as usize] as usize
- }
-
- pub fn get_stack_pointer(&self) -> usize {
- self.context.uc_mcontext.gregs[REG_ESP as usize] as usize
- }
-
- pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- out.context_flags = MD_CONTEXT_X86_FULL | MD_CONTEXT_X86_FLOATING_POINT;
-
- out.gs = self.context.uc_mcontext.gregs[REG_GS as usize] as u32;
- out.fs = self.context.uc_mcontext.gregs[REG_FS as usize] as u32;
- out.es = self.context.uc_mcontext.gregs[REG_ES as usize] as u32;
- out.ds = self.context.uc_mcontext.gregs[REG_DS as usize] as u32;
-
- out.edi = self.context.uc_mcontext.gregs[REG_EDI as usize] as u32;
- out.esi = self.context.uc_mcontext.gregs[REG_ESI as usize] as u32;
- out.ebx = self.context.uc_mcontext.gregs[REG_EBX as usize] as u32;
- out.edx = self.context.uc_mcontext.gregs[REG_EDX as usize] as u32;
- out.ecx = self.context.uc_mcontext.gregs[REG_ECX as usize] as u32;
- out.eax = self.context.uc_mcontext.gregs[REG_EAX as usize] as u32;
-
- out.ebp = self.context.uc_mcontext.gregs[REG_EBP as usize] as u32;
- out.eip = self.context.uc_mcontext.gregs[REG_EIP as usize] as u32;
- out.cs = self.context.uc_mcontext.gregs[REG_CS as usize] as u32;
- out.eflags = self.context.uc_mcontext.gregs[REG_EFL as usize] as u32;
- out.esp = self.context.uc_mcontext.gregs[REG_UESP as usize] as u32;
- out.ss = self.context.uc_mcontext.gregs[REG_SS as usize] as u32;
-
- out.float_save.control_word = self.float_state.cw;
- out.float_save.status_word = self.float_state.sw;
- out.float_save.tag_word = self.float_state.tag;
- out.float_save.error_offset = self.float_state.ipoff;
- out.float_save.error_selector = self.float_state.cssel;
- out.float_save.data_offset = self.float_state.dataoff;
- out.float_save.data_selector = self.float_state.datasel;
-
- // 8 registers * 10 bytes per register.
- // my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
- }
-}
diff --git a/src/crash_context/crash_context_x86_64.rs b/src/crash_context/crash_context_x86_64.rs
deleted file mode 100644
index dd82d29a..00000000
--- a/src/crash_context/crash_context_x86_64.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use super::CrashContext;
-use crate::minidump_cpu::imp::*;
-use crate::minidump_cpu::RawContextCPU;
-use crate::thread_info::to_u128;
-use libc::{
- REG_CSGSFS, REG_EFL, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8, REG_R9,
- REG_RAX, REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP,
-};
-
-impl CrashContext {
- pub fn get_instruction_pointer(&self) -> usize {
- self.context.uc_mcontext.gregs[REG_RIP as usize] as usize
- }
-
- pub fn get_stack_pointer(&self) -> usize {
- self.context.uc_mcontext.gregs[REG_RSP as usize] as usize
- }
-
- pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- out.context_flags = MD_CONTEXT_AMD64_FULL;
- out.cs = (self.context.uc_mcontext.gregs[REG_CSGSFS as usize] & 0xffff) as u16;
-
- out.fs = ((self.context.uc_mcontext.gregs[REG_CSGSFS as usize] >> 32) & 0xffff) as u16;
- out.gs = ((self.context.uc_mcontext.gregs[REG_CSGSFS as usize] >> 16) & 0xffff) as u16;
-
- out.eflags = self.context.uc_mcontext.gregs[REG_EFL as usize] as u32;
-
- out.rax = self.context.uc_mcontext.gregs[REG_RAX as usize] as u64;
- out.rcx = self.context.uc_mcontext.gregs[REG_RCX as usize] as u64;
- out.rdx = self.context.uc_mcontext.gregs[REG_RDX as usize] as u64;
- out.rbx = self.context.uc_mcontext.gregs[REG_RBX as usize] as u64;
-
- out.rsp = self.context.uc_mcontext.gregs[REG_RSP as usize] as u64;
- out.rbp = self.context.uc_mcontext.gregs[REG_RBP as usize] as u64;
- out.rsi = self.context.uc_mcontext.gregs[REG_RSI as usize] as u64;
- out.rdi = self.context.uc_mcontext.gregs[REG_RDI as usize] as u64;
- out.r8 = self.context.uc_mcontext.gregs[REG_R8 as usize] as u64;
- out.r9 = self.context.uc_mcontext.gregs[REG_R9 as usize] as u64;
- out.r10 = self.context.uc_mcontext.gregs[REG_R10 as usize] as u64;
- out.r11 = self.context.uc_mcontext.gregs[REG_R11 as usize] as u64;
- out.r12 = self.context.uc_mcontext.gregs[REG_R12 as usize] as u64;
- out.r13 = self.context.uc_mcontext.gregs[REG_R13 as usize] as u64;
- out.r14 = self.context.uc_mcontext.gregs[REG_R14 as usize] as u64;
- out.r15 = self.context.uc_mcontext.gregs[REG_R15 as usize] as u64;
-
- out.rip = self.context.uc_mcontext.gregs[REG_RIP as usize] as u64;
-
- out.flt_save.control_word = self.float_state.cwd;
- out.flt_save.status_word = self.float_state.swd;
- out.flt_save.tag_word = self.float_state.ftw as u8;
- out.flt_save.error_opcode = self.float_state.fop;
- out.flt_save.error_offset = self.float_state.rip as u32;
- out.flt_save.data_offset = self.float_state.rdp as u32;
- out.flt_save.error_selector = 0; // We don't have this.
- out.flt_save.data_selector = 0; // We don't have this.
- out.flt_save.mx_csr = self.float_state.mxcsr;
- out.flt_save.mx_csr_mask = self.float_state.mxcr_mask;
-
- let data = to_u128(&self.float_state.st_space);
- for idx in 0..data.len() {
- out.flt_save.float_registers[idx] = data[idx];
- }
-
- let data = to_u128(&self.float_state.xmm_space);
- for idx in 0..data.len() {
- out.flt_save.xmm_registers[idx] = data[idx];
- }
- }
-}
diff --git a/src/crash_context/mod.rs b/src/crash_context/mod.rs
deleted file mode 100644
index 2be8fb15..00000000
--- a/src/crash_context/mod.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use libc;
-
-// Minidump defines register structures which are different from the raw
-// structures which we get from the kernel. These are platform specific
-// functions to juggle the ucontext_t and user structures into minidump format.
-
-#[cfg(target_arch = "x86_64")]
-#[path = "crash_context_x86_64.rs"]
-pub mod imp;
-#[cfg(target_arch = "x86")]
-#[path = "crash_context_x86.rs"]
-pub mod imp;
-// Deactivated for now, as ucontext_t is missing from libc
-// #[cfg(target_arch = "arm")]
-// #[path = "crash_context_arm.rs"]
-// pub mod imp;
-#[cfg(target_arch = "arm")]
-use crate::minidump_cpu::RawContextCPU;
-#[cfg(target_arch = "arm")]
-impl CrashContext {
- pub fn get_instruction_pointer(&self) -> usize {
- 0
- }
-
- pub fn get_stack_pointer(&self) -> usize {
- 0
- }
-
- pub fn fill_cpu_context(&self, _: &mut RawContextCPU) {}
-}
-
-#[cfg(target_arch = "aarch64")]
-#[path = "crash_context_aarch64.rs"]
-pub mod imp;
-#[cfg(target_arch = "mips")]
-#[path = "crash_context_mips.rs"]
-pub mod imp;
-
-#[cfg(target_arch = "aarch64")]
-pub type fpstate_t = libc::fpsimd_context; // Currently not part of libc! This will produce an error.
-#[cfg(not(any(
- target_arch = "aarch64",
- target_arch = "mips",
- target_arch = "arm-eabi"
-)))]
-#[cfg(target_arch = "x86")]
-#[allow(non_camel_case_types)]
-pub type fpstate_t = libc::_libc_fpstate;
-#[cfg(target_arch = "x86_64")]
-#[allow(non_camel_case_types)]
-pub type fpstate_t = libc::user_fpregs_struct;
-
-#[repr(C)]
-#[derive(Clone)]
-pub struct CrashContext {
- pub siginfo: libc::siginfo_t,
- pub tid: libc::pid_t, // the crashing thread.
- #[cfg(not(target_arch = "arm"))]
- pub context: libc::ucontext_t,
- // #ifdef this out because FP state is not part of user ABI for Linux ARM.
- // In case of MIPS Linux FP state is already part of ucontext_t so
- // 'float_state' is not required.
- #[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
- pub float_state: fpstate_t,
-}
diff --git a/src/dumper_cpu_info/cpu_info_x86_mips.rs b/src/dumper_cpu_info/cpu_info_x86_mips.rs
deleted file mode 100644
index c37fa94f..00000000
--- a/src/dumper_cpu_info/cpu_info_x86_mips.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-use crate::errors::CpuInfoError;
-use crate::minidump_format::*;
-use std::convert::TryInto;
-use std::io::{BufRead, BufReader};
-use std::path;
-
-type Result = std::result::Result;
-
-struct CpuInfoEntry {
- info_name: &'static str,
- value: i32,
- found: bool,
-}
-
-impl CpuInfoEntry {
- fn new(info_name: &'static str, value: i32, found: bool) -> Self {
- CpuInfoEntry {
- info_name,
- value,
- found,
- }
- }
-}
-
-pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
- let vendor_id_name = "vendor_id";
- let mut cpu_info_table = [
- CpuInfoEntry::new("processor", -1, false),
- #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
- CpuInfoEntry::new("model", 0, false),
- #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
- CpuInfoEntry::new("stepping", 0, false),
- #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
- CpuInfoEntry::new("cpu family", 0, false),
- ];
-
- // processor_architecture should always be set, do this first
- if cfg!(target_arch = "mips") {
- sys_info.processor_architecture = MDCPUArchitecture::Mips as u16;
- } else if cfg!(target_arch = "mips64") {
- sys_info.processor_architecture = MDCPUArchitecture::Mips64 as u16;
- } else if cfg!(target_arch = "x86") {
- sys_info.processor_architecture = MDCPUArchitecture::X86 as u16;
- } else {
- sys_info.processor_architecture = MDCPUArchitecture::Amd64 as u16;
- }
-
- let cpuinfo_file = std::fs::File::open(path::PathBuf::from("/proc/cpuinfo"))?;
-
- let mut vendor_id = String::new();
- for line in BufReader::new(cpuinfo_file).lines() {
- let line = line?;
- // Expected format: + ':'
- // Note that:
- // - empty lines happen.
- // - can contain spaces.
- // - some fields have an empty
- if line.trim().is_empty() {
- continue;
- }
-
- let split: Vec<_> = line.split(':').map(|x| x.trim()).collect();
- let field = split[0];
- let value = split.get(1); // Option, might be missing
-
- let mut is_first_entry = true;
- for mut entry in cpu_info_table.iter_mut() {
- if !is_first_entry && entry.found {
- // except for the 'processor' field, ignore repeated values.
- continue;
- }
- is_first_entry = false;
- if field == entry.info_name {
- if let Some(val) = value {
- if let Ok(v) = val.parse() {
- entry.value = v;
- entry.found = true;
- } else {
- continue;
- }
- } else {
- continue;
- }
- }
-
- // special case for vendor_id
- if field == vendor_id_name && value.is_some() && !value.unwrap().is_empty() {
- vendor_id = value.unwrap().to_string();
- }
- }
- }
- // make sure we got everything we wanted
- if !cpu_info_table.iter().all(|x| x.found) {
- return Err(CpuInfoError::NotAllProcEntriesFound);
- }
- // cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo,
- // assuming this is the highest id, change it to the number of CPUs
- // by adding one.
- cpu_info_table[0].value += 1;
-
- sys_info.number_of_processors = cpu_info_table[0].value as u8; // TODO: might not work on special machines with LOTS of CPUs
- #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
- {
- sys_info.processor_level = cpu_info_table[3].value as u16;
- sys_info.processor_revision =
- (cpu_info_table[1].value << 8 | cpu_info_table[2].value) as u16;
- }
- if !vendor_id.is_empty() {
- let mut slice = vendor_id.as_bytes();
- for id_part in sys_info.cpu.vendor_id.iter_mut() {
- let (int_bytes, rest) = slice.split_at(std::mem::size_of::());
- slice = rest;
- *id_part = match int_bytes.try_into() {
- Ok(x) => u32::from_ne_bytes(x),
- Err(_) => {
- continue;
- }
- };
- }
- }
-
- Ok(())
-}
diff --git a/src/dumper_cpu_info/mod.rs b/src/dumper_cpu_info/mod.rs
deleted file mode 100644
index 248ae4c2..00000000
--- a/src/dumper_cpu_info/mod.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-#[cfg(any(
- target_arch = "x86_64",
- target_arch = "x86",
- target_arch = "mips",
- target_arch = "mips64"
-))]
-#[path = "cpu_info_x86_mips.rs"]
-pub mod imp;
-#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
-#[path = "cpu_info_arm.rs"]
-pub mod imp;
-
-pub use imp::write_cpu_information;
-
-use crate::errors::MemoryWriterError;
-use crate::minidump_format::{MDOSPlatform, MDRawSystemInfo};
-use crate::sections::write_string_to_location;
-use nix::sys::utsname::uname;
-use std::io::Cursor;
-
-type Result = std::result::Result;
-
-pub fn write_os_information(
- buffer: &mut Cursor>,
- sys_info: &mut MDRawSystemInfo,
-) -> Result<()> {
- let info = uname();
- if cfg!(target_os = "android") {
- sys_info.platform_id = MDOSPlatform::Android as u32;
- } else {
- sys_info.platform_id = MDOSPlatform::Linux as u32;
- }
- let merged = vec![
- info.sysname(),
- info.release(),
- info.version(),
- info.machine(),
- ]
- .join(" ");
-
- let location = write_string_to_location(buffer, &merged)?;
- sys_info.csd_version_rva = location.rva;
-
- Ok(())
-}
diff --git a/src/lib.rs b/src/lib.rs
index 1a774cd8..3bcb07e1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,17 +1,91 @@
-#[cfg(target_os = "android")]
-mod android;
-pub mod app_memory;
-mod auxv_reader;
-pub mod crash_context;
-mod dso_debug;
-mod dumper_cpu_info;
-pub mod errors;
-pub mod linux_ptrace_dumper;
-pub mod maps_reader;
+// BEGIN - Embark standard lints v6 for Rust 1.55+
+// do not change or add/remove here, but one can add exceptions after this section
+// for more info see:
+#![deny(unsafe_code)]
+#![warn(
+ clippy::all,
+ clippy::await_holding_lock,
+ clippy::char_lit_as_u8,
+ clippy::checked_conversions,
+ clippy::dbg_macro,
+ clippy::debug_assert_with_mut_call,
+ clippy::doc_markdown,
+ clippy::empty_enum,
+ clippy::enum_glob_use,
+ clippy::exit,
+ clippy::expl_impl_clone_on_copy,
+ clippy::explicit_deref_methods,
+ clippy::explicit_into_iter_loop,
+ clippy::fallible_impl_from,
+ clippy::filter_map_next,
+ clippy::flat_map_option,
+ clippy::float_cmp_const,
+ clippy::fn_params_excessive_bools,
+ clippy::from_iter_instead_of_collect,
+ clippy::if_let_mutex,
+ clippy::implicit_clone,
+ clippy::imprecise_flops,
+ clippy::inefficient_to_string,
+ clippy::invalid_upcast_comparisons,
+ clippy::large_digit_groups,
+ clippy::large_stack_arrays,
+ clippy::large_types_passed_by_value,
+ clippy::let_unit_value,
+ clippy::linkedlist,
+ clippy::lossy_float_literal,
+ clippy::macro_use_imports,
+ clippy::manual_ok_or,
+ clippy::map_err_ignore,
+ clippy::map_flatten,
+ clippy::map_unwrap_or,
+ clippy::match_on_vec_items,
+ clippy::match_same_arms,
+ clippy::match_wild_err_arm,
+ clippy::match_wildcard_for_single_variants,
+ clippy::mem_forget,
+ clippy::mismatched_target_os,
+ clippy::missing_enforced_import_renames,
+ clippy::mut_mut,
+ clippy::mutex_integer,
+ clippy::needless_borrow,
+ clippy::needless_continue,
+ clippy::needless_for_each,
+ clippy::option_option,
+ clippy::path_buf_push_overwrite,
+ clippy::ptr_as_ptr,
+ clippy::rc_mutex,
+ clippy::ref_option_ref,
+ clippy::rest_pat_in_fully_bound_structs,
+ clippy::same_functions_in_if_condition,
+ clippy::semicolon_if_nothing_returned,
+ clippy::single_match_else,
+ clippy::string_add_assign,
+ clippy::string_add,
+ clippy::string_lit_as_bytes,
+ clippy::string_to_string,
+ clippy::todo,
+ clippy::trait_duplication_in_bounds,
+ clippy::unimplemented,
+ clippy::unnested_or_patterns,
+ clippy::unused_self,
+ clippy::useless_transmute,
+ clippy::verbose_file_reads,
+ clippy::zero_sized_map_values,
+ future_incompatible,
+ nonstandard_style,
+ rust_2018_idioms
+)]
+// END - Embark standard lints v6 for Rust 1.55+
+// crate-specific exceptions:
+#![allow(unsafe_code)]
+
+cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ mod linux;
+
+ pub use linux::*;
+ }
+}
+
pub mod minidump_cpu;
pub mod minidump_format;
-pub mod minidump_writer;
-mod sections;
-pub mod thread_info;
-
-pub use maps_reader::LINUX_GATE_LIBRARY_NAME;
diff --git a/src/linux.rs b/src/linux.rs
new file mode 100644
index 00000000..8f8c649b
--- /dev/null
+++ b/src/linux.rs
@@ -0,0 +1,14 @@
+#[cfg(target_os = "android")]
+mod android;
+pub mod app_memory;
+mod auxv_reader;
+mod dso_debug;
+mod dumper_cpu_info;
+pub mod errors;
+pub mod maps_reader;
+pub mod minidump_writer;
+pub mod ptrace_dumper;
+mod sections;
+pub mod thread_info;
+
+pub use maps_reader::LINUX_GATE_LIBRARY_NAME;
diff --git a/src/android.rs b/src/linux/android.rs
similarity index 99%
rename from src/android.rs
rename to src/linux/android.rs
index b605b6ce..917b4ea1 100644
--- a/src/android.rs
+++ b/src/linux/android.rs
@@ -15,7 +15,6 @@ use goblin::elf::header::header64 as elf_header;
use goblin::elf::program_header::program_header32::ProgramHeader;
#[cfg(target_pointer_width = "64")]
use goblin::elf::program_header::program_header64::ProgramHeader;
-use std::convert::TryInto;
use std::ffi::c_void;
type Result = std::result::Result;
diff --git a/src/app_memory.rs b/src/linux/app_memory.rs
similarity index 100%
rename from src/app_memory.rs
rename to src/linux/app_memory.rs
diff --git a/src/auxv_reader.rs b/src/linux/auxv_reader.rs
similarity index 100%
rename from src/auxv_reader.rs
rename to src/linux/auxv_reader.rs
diff --git a/src/crash_context/crash_context_arm.rs b/src/linux/crash_context/arm.rs
similarity index 100%
rename from src/crash_context/crash_context_arm.rs
rename to src/linux/crash_context/arm.rs
diff --git a/src/crash_context/crash_context_mips.rs b/src/linux/crash_context/mips.rs
similarity index 100%
rename from src/crash_context/crash_context_mips.rs
rename to src/linux/crash_context/mips.rs
diff --git a/src/dso_debug.rs b/src/linux/dso_debug.rs
similarity index 91%
rename from src/dso_debug.rs
rename to src/linux/dso_debug.rs
index 781c34ab..d842a926 100644
--- a/src/dso_debug.rs
+++ b/src/linux/dso_debug.rs
@@ -1,11 +1,13 @@
-use crate::auxv_reader::AuxvType;
-use crate::errors::SectionDsoDebugError;
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::minidump_format::*;
-use crate::sections::{write_string_to_location, MemoryArrayWriter, MemoryWriter};
-use libc;
+use crate::{
+ linux::{
+ auxv_reader::AuxvType,
+ errors::SectionDsoDebugError,
+ ptrace_dumper::PtraceDumper,
+ sections::{write_string_to_location, Buffer, MemoryArrayWriter, MemoryWriter},
+ },
+ minidump_format::*,
+};
use std::collections::HashMap;
-use std::io::Cursor;
type Result = std::result::Result;
@@ -71,7 +73,7 @@ pub struct RDebug {
}
pub fn write_dso_debug_stream(
- buffer: &mut Cursor>,
+ buffer: &mut Buffer,
blamed_thread: i32,
auxv: &HashMap,
) -> Result {
@@ -95,7 +97,7 @@ pub fn write_dso_debug_stream(
.get(&at_phdr)
.ok_or(SectionDsoDebugError::CouldNotFind("AT_PHDR in auxv"))? as usize;
- let ph = LinuxPtraceDumper::copy_from_process(
+ let ph = PtraceDumper::copy_from_process(
blamed_thread,
phdr as *mut libc::c_void,
SIZEOF_PHDR * phnum_max,
@@ -145,7 +147,7 @@ pub fn write_dso_debug_stream(
// DSOs loaded into the program. If this information is indeed available,
// dump it to a MD_LINUX_DSO_DEBUG stream.
loop {
- let dyn_data = LinuxPtraceDumper::copy_from_process(
+ let dyn_data = PtraceDumper::copy_from_process(
blamed_thread,
(dyn_addr as usize + dynamic_length) as *mut libc::c_void,
dyn_size,
@@ -178,7 +180,7 @@ pub fn write_dso_debug_stream(
// See for a more detailed discussion of the how the dynamic
// loader communicates with debuggers.
- let debug_entry_data = LinuxPtraceDumper::copy_from_process(
+ let debug_entry_data = PtraceDumper::copy_from_process(
blamed_thread,
r_debug as *mut libc::c_void,
std::mem::size_of::(),
@@ -193,7 +195,7 @@ pub fn write_dso_debug_stream(
let mut dso_vec = Vec::new();
let mut curr_map = debug_entry.r_map;
while curr_map != 0 {
- let link_map_data = LinuxPtraceDumper::copy_from_process(
+ let link_map_data = PtraceDumper::copy_from_process(
blamed_thread,
curr_map as *mut libc::c_void,
std::mem::size_of::(),
@@ -209,7 +211,7 @@ pub fn write_dso_debug_stream(
}
let mut linkmap_rva = u32::MAX;
- if dso_vec.len() > 0 {
+ if !dso_vec.is_empty() {
// If we have at least one DSO, create an array of MDRawLinkMap
// entries in the minidump file.
let mut linkmap = MemoryArrayWriter::::alloc_array(buffer, dso_vec.len())?;
@@ -219,7 +221,7 @@ pub fn write_dso_debug_stream(
for (idx, map) in dso_vec.iter().enumerate() {
let mut filename = String::new();
if map.l_name > 0 {
- let filename_data = LinuxPtraceDumper::copy_from_process(
+ let filename_data = PtraceDumper::copy_from_process(
blamed_thread,
map.l_name as *mut libc::c_void,
256,
@@ -258,12 +260,12 @@ pub fn write_dso_debug_stream(
};
dirent.location.data_size += dynamic_length as u32;
- let dso_debug_data = LinuxPtraceDumper::copy_from_process(
+ let dso_debug_data = PtraceDumper::copy_from_process(
blamed_thread,
dyn_addr as *mut libc::c_void,
dynamic_length,
)?;
- MemoryArrayWriter::::alloc_from_array(buffer, &dso_debug_data)?;
+ MemoryArrayWriter::write_bytes(buffer, &dso_debug_data);
Ok(dirent)
}
diff --git a/src/linux/dumper_cpu_info.rs b/src/linux/dumper_cpu_info.rs
new file mode 100644
index 00000000..bbe22e7c
--- /dev/null
+++ b/src/linux/dumper_cpu_info.rs
@@ -0,0 +1,45 @@
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_arch = "x86_64",
+ target_arch = "x86",
+ target_arch = "mips",
+ target_arch = "mips64"
+ ))]
+ {
+ pub mod x86_mips;
+ pub use x86_mips as imp;
+ } else if #[cfg(any(
+ target_arch = "arm",
+ target_arch = "aarch64",
+ ))]
+ {
+ pub mod arm;
+ pub use arm as imp;
+ }
+}
+
+pub use imp::write_cpu_information;
+
+use crate::minidump_format::PlatformId;
+use nix::sys::utsname::uname;
+
+/// Retrieves the [`MDOSPlatform`] and synthesized version information
+pub fn os_information() -> (PlatformId, String) {
+ let info = uname();
+ let vers = format!(
+ "{} {} {} {}",
+ info.sysname(),
+ info.release(),
+ info.version(),
+ info.machine()
+ );
+
+ (
+ if cfg!(target_os = "android") {
+ PlatformId::Android
+ } else {
+ PlatformId::Linux
+ },
+ vers,
+ )
+}
diff --git a/src/dumper_cpu_info/cpu_info_arm.rs b/src/linux/dumper_cpu_info/arm.rs
similarity index 76%
rename from src/dumper_cpu_info/cpu_info_arm.rs
rename to src/linux/dumper_cpu_info/arm.rs
index ab7da7ae..67568516 100644
--- a/src/dumper_cpu_info/cpu_info_arm.rs
+++ b/src/linux/dumper_cpu_info/arm.rs
@@ -86,47 +86,36 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
// The ELF hwcaps are listed in the "Features" entry as textual tags.
// This table is used to rebuild them.
- let cpu_features_entries;
#[cfg(target_arch = "arm")]
- {
- cpu_features_entries = [
- CpuFeaturesEntry::new("swp", MDCPUInformationARMElfHwCaps::Swp as u32),
- CpuFeaturesEntry::new("half", MDCPUInformationARMElfHwCaps::Half as u32),
- CpuFeaturesEntry::new("thumb", MDCPUInformationARMElfHwCaps::Thumb as u32),
- CpuFeaturesEntry::new("bit26", MDCPUInformationARMElfHwCaps::Bit26 as u32),
- CpuFeaturesEntry::new("fastmult", MDCPUInformationARMElfHwCaps::FastMult as u32),
- CpuFeaturesEntry::new("fpa", MDCPUInformationARMElfHwCaps::Fpa as u32),
- CpuFeaturesEntry::new("vfp", MDCPUInformationARMElfHwCaps::Vfp as u32),
- CpuFeaturesEntry::new("edsp", MDCPUInformationARMElfHwCaps::Edsp as u32),
- CpuFeaturesEntry::new("java", MDCPUInformationARMElfHwCaps::Java as u32),
- CpuFeaturesEntry::new("iwmmxt", MDCPUInformationARMElfHwCaps::Iwmmxt as u32),
- CpuFeaturesEntry::new("crunch", MDCPUInformationARMElfHwCaps::Crunch as u32),
- CpuFeaturesEntry::new("thumbee", MDCPUInformationARMElfHwCaps::Thumbee as u32),
- CpuFeaturesEntry::new("neon", MDCPUInformationARMElfHwCaps::Neon as u32),
- CpuFeaturesEntry::new("vfpv3", MDCPUInformationARMElfHwCaps::Vfpv3 as u32),
- CpuFeaturesEntry::new("vfpv3d16", MDCPUInformationARMElfHwCaps::Vfpv3d16 as u32),
- CpuFeaturesEntry::new("tls", MDCPUInformationARMElfHwCaps::Tls as u32),
- CpuFeaturesEntry::new("vfpv4", MDCPUInformationARMElfHwCaps::Vfpv4 as u32),
- CpuFeaturesEntry::new("idiva", MDCPUInformationARMElfHwCaps::Idiva as u32),
- CpuFeaturesEntry::new("idivt", MDCPUInformationARMElfHwCaps::Idivt as u32),
- CpuFeaturesEntry::new(
- "idiv",
- MDCPUInformationARMElfHwCaps::Idiva as u32
- | MDCPUInformationARMElfHwCaps::Idivt as u32,
- ),
- ];
- }
- #[cfg(target_arch = "aarch64")]
- {
- // No hwcaps on aarch64.
- cpu_features_entries = [];
- }
+ let cpu_features_entries = [
+ CpuFeaturesEntry::new("swp", MDCPUInformationARMElfHwCaps::HWCAP_SWP),
+ CpuFeaturesEntry::new("half", MDCPUInformationARMElfHwCaps::HWCAP_HALF),
+ CpuFeaturesEntry::new("thumb", MDCPUInformationARMElfHwCaps::HWCAP_THUMB),
+ CpuFeaturesEntry::new("bit26", MDCPUInformationARMElfHwCaps::HWCAP_26BIT),
+ CpuFeaturesEntry::new("fastmult", MDCPUInformationARMElfHwCaps::HWCAP_FAST_MULT),
+ CpuFeaturesEntry::new("fpa", MDCPUInformationARMElfHwCaps::HWCAP_FPA),
+ CpuFeaturesEntry::new("vfp", MDCPUInformationARMElfHwCaps::HWCAP_VFP),
+ CpuFeaturesEntry::new("edsp", MDCPUInformationARMElfHwCaps::HWCAP_EDSP),
+ CpuFeaturesEntry::new("java", MDCPUInformationARMElfHwCaps::HWCAP_JAVA),
+ CpuFeaturesEntry::new("iwmmxt", MDCPUInformationARMElfHwCaps::HWCAP_IWMMXT),
+ CpuFeaturesEntry::new("crunch", MDCPUInformationARMElfHwCaps::HWCAP_CRUNCH),
+ CpuFeaturesEntry::new("thumbee", MDCPUInformationARMElfHwCaps::HWCAP_THUMBEE),
+ CpuFeaturesEntry::new("neon", MDCPUInformationARMElfHwCaps::HWCAP_NEON),
+ CpuFeaturesEntry::new("vfpv3", MDCPUInformationARMElfHwCaps::HWCAP_VFPv3),
+ CpuFeaturesEntry::new("vfpv3d16", MDCPUInformationARMElfHwCaps::HWCAP_VFPv3D16),
+ CpuFeaturesEntry::new("tls", MDCPUInformationARMElfHwCaps::HWCAP_TLS),
+ CpuFeaturesEntry::new("vfpv4", MDCPUInformationARMElfHwCaps::HWCAP_VFPv4),
+ CpuFeaturesEntry::new("idiva", MDCPUInformationARMElfHwCaps::HWCAP_IDIVA),
+ CpuFeaturesEntry::new("idivt", MDCPUInformationARMElfHwCaps::HWCAP_IDIVT),
+ CpuFeaturesEntry::new("idiv", HWCAP_IDIV),
+ ];
// processor_architecture should always be set, do this first
if cfg!(target_arch = "aarch64") {
- sys_info.processor_architecture = MDCPUArchitecture::Arm64Old as u16;
+ sys_info.processor_architecture =
+ MDCPUArchitecture::PROCESSOR_ARCHITECTURE_ARM64_OLD as u16;
} else {
- sys_info.processor_architecture = MDCPUArchitecture::Arm as u16;
+ sys_info.processor_architecture = MDCPUArchitecture::PROCESSOR_ARCHITECTURE_ARM as u16;
}
// /proc/cpuinfo is not readable under various sandboxed environments
@@ -139,8 +128,9 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
sys_info.number_of_processors = 0;
sys_info.processor_level = 1; // There is no ARMv1
sys_info.processor_revision = 42;
- sys_info.cpu.cpuid = 0;
- sys_info.cpu.elf_hwcaps = 0;
+
+ //sys_info.cpu.cpuid = 0;
+ //sys_info.cpu.elf_hwcaps = 0;
// Counting the number of CPUs involves parsing two sysfs files,
// because the content of /proc/cpuinfo will only mirror the number
@@ -174,6 +164,8 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
}
};
+ let mut cpuid = 0;
+
for line in BufReader::new(cpuinfo_file).lines() {
let line = line?;
// Expected format: + ':'
@@ -211,7 +203,7 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
};
result &= (1 << entry.bit_length) - 1;
result <<= entry.bit_lshift;
- sys_info.cpu.cpuid |= result as u32;
+ cpuid |= result as u32;
}
if cfg!(target_arch = "arm") {
@@ -246,20 +238,50 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
}
}
- // Rebuild the ELF hwcaps from the 'Features' field.
- if field == "Features" {
- if let Some(val) = value {
- // Parse each space-separated tag.
- for tag in val.split_whitespace() {
- for entry in &cpu_features_entries {
- if entry.tag == tag {
- sys_info.cpu.elf_hwcaps |= entry.hwcaps;
- break;
+ let elf_hwcaps = {
+ let mut elf_hwcaps = 0;
+ #[cfg(target_arch = "arm")]
+ {
+ // Rebuild the ELF hwcaps from the 'Features' field.
+ if field == "Features" {
+ if let Some(val) = value {
+ // Parse each space-separated tag.
+ for tag in val.split_whitespace() {
+ for entry in &cpu_features_entries {
+ if entry.tag == tag {
+ elf_hwcaps |= entry.hwcaps;
+ break;
+ }
+ }
}
}
}
}
- }
+
+ elf_hwcaps
+ };
+
+ // The sys_info.cpu field is just a byte array, but in arm's case it is
+ // actually
+ // minidump_common::format::ARMCpuInfo {
+ // pub cpuid: u32,
+ // pub elf_hwcaps: u32,
+ // }
+ use scroll::Pwrite;
+ sys_info
+ .cpu
+ .data
+ .pwrite_with(cpuid, 0, scroll::Endian::Little)
+ .expect("impossible");
+ sys_info
+ .cpu
+ .data
+ .pwrite_with(
+ elf_hwcaps,
+ std::mem::size_of::(),
+ scroll::Endian::Little,
+ )
+ .expect("impossible");
}
Ok(())
}
diff --git a/src/linux/dumper_cpu_info/x86_mips.rs b/src/linux/dumper_cpu_info/x86_mips.rs
new file mode 100644
index 00000000..05e7fc95
--- /dev/null
+++ b/src/linux/dumper_cpu_info/x86_mips.rs
@@ -0,0 +1,110 @@
+use crate::errors::CpuInfoError;
+use crate::minidump_format::*;
+use std::io::{BufRead, BufReader};
+use std::path;
+
+type Result = std::result::Result;
+
+pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
+ // processor_architecture should always be set, do this first
+ sys_info.processor_architecture = if cfg!(target_arch = "mips") {
+ MDCPUArchitecture::PROCESSOR_ARCHITECTURE_MIPS
+ } else if cfg!(target_arch = "mips64") {
+ MDCPUArchitecture::PROCESSOR_ARCHITECTURE_MIPS64
+ } else if cfg!(target_arch = "x86") {
+ MDCPUArchitecture::PROCESSOR_ARCHITECTURE_INTEL
+ } else {
+ MDCPUArchitecture::PROCESSOR_ARCHITECTURE_AMD64
+ } as u16;
+
+ let cpuinfo_file = std::fs::File::open(path::PathBuf::from("/proc/cpuinfo"))?;
+
+ let mut processor = None;
+ // x86/_64 specific
+ let mut vendor_id = None;
+ let mut model = None;
+ let mut stepping = None;
+ let mut family = None;
+ //
+
+ for line in BufReader::new(cpuinfo_file).lines() {
+ let line = line?;
+ // Expected format: + ':'
+ // Note that:
+ // - empty lines happen.
+ // - can contain spaces.
+ // - some fields have an empty
+ if line.trim().is_empty() {
+ continue;
+ }
+
+ let mut liter = line.split(':').map(|x| x.trim());
+ let field = liter.next().unwrap(); // guaranteed to have at least one item
+ let value = if let Some(val) = liter.next() {
+ val
+ } else {
+ continue;
+ };
+
+ let entry = match field {
+ "processor" => &mut processor,
+ "model" => &mut model,
+ "stepping" => &mut stepping,
+ "cpu family" => &mut family,
+ "vendor_id" => {
+ if vendor_id.is_none() && !value.is_empty() {
+ vendor_id = Some(value.to_owned());
+ }
+ continue;
+ }
+ _ => continue,
+ };
+
+ if entry.is_some() && field != "processor" {
+ continue;
+ }
+
+ if let Ok(v) = value.parse::() {
+ *entry = Some(v);
+ }
+ }
+
+ // This holds the highest processor id which start from 0 so add 1 to get the actual count
+ // This field is only a u8 which means it will not work great in high (artificially or otherwise)
+ // contexts
+ sys_info.number_of_processors = std::cmp::max(
+ (processor.ok_or(CpuInfoError::NotAllProcEntriesFound)? + 1) as u8,
+ u8::MAX,
+ );
+
+ #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
+ {
+ sys_info.processor_level = family.ok_or(CpuInfoError::NotAllProcEntriesFound)? as u16;
+ sys_info.processor_revision = (model.ok_or(CpuInfoError::NotAllProcEntriesFound)? << 8
+ | stepping.ok_or(CpuInfoError::NotAllProcEntriesFound)?)
+ as u16;
+
+ if let Some(vendor_id) = vendor_id {
+ let mut slice = vendor_id.as_bytes();
+
+ // SAFETY: CPU_INFORMATION is a block of bytes, which is actually
+ // a union, including the X86 information that we actually want to
+ // set
+ let cpu_info: &mut MDCPUInformation =
+ unsafe { &mut *sys_info.cpu.data.as_mut_ptr().cast() };
+
+ for id_part in cpu_info.vendor_id.iter_mut() {
+ let (int_bytes, rest) = slice.split_at(std::mem::size_of::());
+ slice = rest;
+ *id_part = match int_bytes.try_into() {
+ Ok(x) => u32::from_ne_bytes(x),
+ Err(_) => {
+ continue;
+ }
+ };
+ }
+ }
+ }
+
+ Ok(())
+}
diff --git a/src/errors.rs b/src/linux/errors.rs
similarity index 97%
rename from src/errors.rs
rename to src/linux/errors.rs
index 4a540916..2ce25037 100644
--- a/src/errors.rs
+++ b/src/linux/errors.rs
@@ -1,6 +1,4 @@
-use crate::maps_reader::MappingInfo;
-use crate::thread_info::Pid;
-use goblin;
+use crate::{maps_reader::MappingInfo, thread_info::Pid};
use thiserror::Error;
#[derive(Debug, Error)]
@@ -11,6 +9,7 @@ pub enum InitError {
NoAuxvEntryFound(Pid),
#[error("crash thread does not reference principal mapping")]
PrincipalMappingNotReferenced,
+ #[cfg(target_os = "android")]
#[error("Failed Android specific late init")]
AndroidLateInitError(#[from] AndroidError),
}
@@ -78,6 +77,7 @@ pub enum ThreadInfoError {
InvalidProcStatusFile(Pid, String),
}
+#[cfg(target_os = "android")]
#[derive(Debug, Error)]
pub enum AndroidError {
#[error("Failed to copy memory from process")]
@@ -126,6 +126,8 @@ pub enum MemoryWriterError {
IOError(#[from] std::io::Error),
#[error("Failed integer conversion")]
TryFromIntError(#[from] std::num::TryFromIntError),
+ #[error("Failed to write to buffer")]
+ Scroll(#[from] scroll::Error),
}
#[derive(Debug, Error)]
diff --git a/src/maps_reader.rs b/src/linux/maps_reader.rs
similarity index 93%
rename from src/maps_reader.rs
rename to src/linux/maps_reader.rs
index ec0b4543..f31bca3f 100644
--- a/src/maps_reader.rs
+++ b/src/linux/maps_reader.rs
@@ -1,13 +1,8 @@
-use crate::auxv_reader::AuxvType;
-use crate::errors::MapsReaderError;
-use crate::thread_info::Pid;
+use crate::linux::{auxv_reader::AuxvType, errors::MapsReaderError, thread_info::Pid};
use byteorder::{NativeEndian, ReadBytesExt};
use goblin::elf;
use memmap2::{Mmap, MmapOptions};
-use std::convert::TryInto;
-use std::fs::File;
-use std::mem::size_of;
-use std::path::PathBuf;
+use std::{fs::File, mem::size_of, path::PathBuf};
pub const LINUX_GATE_LIBRARY_NAME: &str = "linux-gate.so";
pub const DELETED_SUFFIX: &str = " (deleted)";
@@ -58,6 +53,7 @@ pub enum MappingInfoParsingResult {
Success(MappingInfo),
}
+#[inline]
fn is_mapping_a_path(pathname: Option<&str>) -> bool {
match pathname {
Some(x) => x.contains('/'),
@@ -185,7 +181,7 @@ impl MappingInfo {
}
pub fn get_mmap(name: &Option, offset: usize) -> Result {
- if !MappingInfo::is_mapped_file_safe_to_open(&name) {
+ if !MappingInfo::is_mapped_file_safe_to_open(name) {
return Err(MapsReaderError::NotSafeToOpenMapping(
name.clone().unwrap_or_default(),
));
@@ -301,7 +297,6 @@ impl MappingInfo {
pub fn get_mapping_effective_name_and_path(&self) -> Result<(String, String)> {
let mut file_path = self.name.clone().unwrap_or_default();
- let file_name;
// Tools such as minidump_stackwalk use the name of the module to look up
// symbols produced by dump_syms. dump_syms will prefer to use a module's
@@ -309,15 +304,13 @@ impl MappingInfo {
// filesystem name of the module.
// Just use the filesystem name if no SONAME is present.
- let file_name = match self.elf_file_so_name() {
- Ok(name) => name,
- Err(_) => {
- // file_path := /path/to/libname.so
- // file_name := libname.so
- let split: Vec<_> = file_path.rsplitn(2, '/').collect();
- file_name = split.first().unwrap().to_string();
- return Ok((file_path, file_name));
- }
+ let file_name = if let Ok(name) = self.elf_file_so_name() {
+ name
+ } else {
+ // file_path := /path/to/libname.so
+ // file_name := libname.so
+ let file_name = file_path.rsplit('/').next().unwrap().to_owned();
+ return Ok((file_path, file_name));
};
if self.executable && self.offset != 0 {
@@ -424,10 +417,11 @@ mod tests {
let (lines, linux_gate_loc) = get_lines_and_loc();
// Only /usr/bin/cat and [heap]
for line in lines {
- match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
- Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
- Ok(MappingInfoParsingResult::SkipLine) => continue,
- Err(_) => assert!(false),
+ match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
+ .expect("unexpected parse failure")
+ {
+ MappingInfoParsingResult::Success(map) => mappings.push(map),
+ MappingInfoParsingResult::SkipLine => continue,
}
}
assert_eq!(mappings.len(), 23);
@@ -440,10 +434,11 @@ mod tests {
let (lines, linux_gate_loc) = get_lines_and_loc();
// Only /usr/bin/cat and [heap]
for line in lines[0..=6].iter() {
- match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
- Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
- Ok(MappingInfoParsingResult::SkipLine) => continue,
- Err(_) => assert!(false),
+ match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
+ .expect("unexpected parse failure")
+ {
+ MappingInfoParsingResult::Success(map) => mappings.push(map),
+ MappingInfoParsingResult::SkipLine => continue,
}
}
@@ -581,10 +576,11 @@ mod tests {
let linux_gate_loc = 0x7ffe091bf000;
let mut mappings: Vec = Vec::new();
for line in lines {
- match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
- Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
- Ok(MappingInfoParsingResult::SkipLine) => continue,
- Err(_) => assert!(false),
+ match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
+ .expect("unexpected parse failure")
+ {
+ MappingInfoParsingResult::Success(map) => mappings.push(map),
+ MappingInfoParsingResult::SkipLine => continue,
}
}
assert_eq!(mappings.len(), 1);
@@ -606,7 +602,7 @@ mod tests {
let linux_gate_loc = 0x7ffe091bf000;
let mut mappings: Vec = Vec::new();
for line in lines {
- match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
+ match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut()) {
Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
Ok(MappingInfoParsingResult::SkipLine) => continue,
Err(x) => panic!("{:?}", x),
@@ -640,10 +636,11 @@ mod tests {
let linux_gate_loc = 0x7ffe091bf000;
let mut mappings: Vec = Vec::new();
for line in lines {
- match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
- Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
- Ok(MappingInfoParsingResult::SkipLine) => continue,
- Err(_) => assert!(false),
+ match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
+ .expect("unexpected parse failure")
+ {
+ MappingInfoParsingResult::Success(map) => mappings.push(map),
+ MappingInfoParsingResult::SkipLine => continue,
}
}
assert_eq!(mappings.len(), 4);
diff --git a/src/minidump_writer.rs b/src/linux/minidump_writer.rs
similarity index 84%
rename from src/minidump_writer.rs
rename to src/linux/minidump_writer.rs
index 644843d9..86bbeaba 100644
--- a/src/minidump_writer.rs
+++ b/src/linux/minidump_writer.rs
@@ -1,15 +1,19 @@
-use crate::app_memory::AppMemoryList;
-use crate::crash_context::CrashContext;
-use crate::dso_debug;
-use crate::errors::{FileWriterError, InitError, MemoryWriterError, WriterError};
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::maps_reader::{MappingInfo, MappingList};
-use crate::minidump_format::*;
-use crate::sections::*;
-use crate::thread_info::Pid;
-use std::io::{Cursor, Read, Seek, SeekFrom, Write};
-
-pub type DumpBuf = Cursor>;
+use crate::{
+ linux::{
+ app_memory::AppMemoryList,
+ dso_debug,
+ errors::{FileWriterError, InitError, MemoryWriterError, WriterError},
+ maps_reader::{MappingInfo, MappingList},
+ ptrace_dumper::PtraceDumper,
+ sections::*,
+ thread_info::Pid,
+ },
+ minidump_format::*,
+};
+use crash_context::{CpuContext, CrashContext};
+use std::io::{Seek, SeekFrom, Write};
+
+pub type DumpBuf = Buffer;
#[derive(Debug)]
pub struct DirSection<'a, W>
@@ -33,17 +37,18 @@ where
index_length: u32,
destination: &'a mut W,
) -> std::result::Result {
- let dir_section =
+ let section =
MemoryArrayWriter::::alloc_array(buffer, index_length as usize)?;
Ok(DirSection {
curr_idx: 0,
- section: dir_section,
+ section,
destination_start_offset: destination.seek(SeekFrom::Current(0))?,
destination,
last_position_written_to_file: 0,
})
}
+ #[inline]
fn position(&self) -> u32 {
self.section.position
}
@@ -62,16 +67,15 @@ where
let idx_pos = self.section.location_of_index(self.curr_idx);
self.curr_idx += 1;
- self.destination.seek(std::io::SeekFrom::Start(
+ self.destination.seek(SeekFrom::Start(
self.destination_start_offset + idx_pos.rva as u64,
))?;
let start = idx_pos.rva as usize;
let end = (idx_pos.rva + idx_pos.data_size) as usize;
- self.destination.write_all(&buffer.get_ref()[start..end])?;
+ self.destination.write_all(&buffer[start..end])?;
// Reset file-position
- self.destination
- .seek(std::io::SeekFrom::Start(curr_file_pos))?;
+ self.destination.seek(SeekFrom::Start(curr_file_pos))?;
Ok(())
}
@@ -89,7 +93,7 @@ where
}
let start_pos = self.last_position_written_to_file as usize;
- self.destination.write_all(&buffer.get_ref()[start_pos..])?;
+ self.destination.write_all(&buffer[start_pos..])?;
self.last_position_written_to_file = buffer.position();
Ok(())
}
@@ -165,8 +169,6 @@ impl MinidumpWriter {
self
}
- // Has to be deactivated for ARM for now, as libc doesn't include ucontext_t for ARM yet
- #[cfg(not(target_arch = "arm"))]
pub fn set_crash_context(&mut self, crash_context: CrashContext) -> &mut Self {
self.crash_context = Some(crash_context);
self
@@ -185,7 +187,7 @@ impl MinidumpWriter {
/// Generates a minidump and writes to the destination provided. Returns the in-memory
/// version of the minidump as well.
pub fn dump(&mut self, destination: &mut (impl Write + Seek)) -> Result> {
- let mut dumper = LinuxPtraceDumper::new(self.process_id)?;
+ let mut dumper = PtraceDumper::new(self.process_id)?;
dumper.suspend_threads()?;
dumper.late_init()?;
@@ -199,17 +201,17 @@ impl MinidumpWriter {
}
}
- let mut buffer = Cursor::new(Vec::new());
+ let mut buffer = Buffer::with_capacity(4 * 1024);
self.generate_dump(&mut buffer, &mut dumper, destination)?;
// dumper would resume threads in drop() automatically,
// but in case there is an error, we want to catch it
dumper.resume_threads()?;
- Ok(buffer.into_inner())
+ Ok(buffer.into())
}
- fn crash_thread_references_principal_mapping(&self, dumper: &LinuxPtraceDumper) -> bool {
+ fn crash_thread_references_principal_mapping(&self, dumper: &PtraceDumper) -> bool {
if self.crash_context.is_none() || self.principal_mapping.is_none() {
return false;
}
@@ -227,12 +229,8 @@ impl MinidumpWriter {
.system_mapping_info
.end_address;
- let pc = self
- .crash_context
- .as_ref()
- .unwrap()
- .get_instruction_pointer();
- let stack_pointer = self.crash_context.as_ref().unwrap().get_stack_pointer();
+ let pc = self.crash_context.as_ref().unwrap().instruction_pointer();
+ let stack_pointer = self.crash_context.as_ref().unwrap().stack_pointer();
if pc >= low_addr && pc < high_addr {
return true;
@@ -244,7 +242,7 @@ impl MinidumpWriter {
return false;
}
};
- let stack_copy = match LinuxPtraceDumper::copy_from_process(
+ let stack_copy = match PtraceDumper::copy_from_process(
self.blamed_thread,
stack_ptr as *mut libc::c_void,
stack_len,
@@ -265,7 +263,7 @@ impl MinidumpWriter {
fn generate_dump(
&mut self,
buffer: &mut DumpBuf,
- dumper: &mut LinuxPtraceDumper,
+ dumper: &mut PtraceDumper,
destination: &mut (impl Write + Seek),
) -> Result<()> {
// A minidump file contains a number of tagged streams. This is the number
@@ -280,7 +278,6 @@ impl MinidumpWriter {
signature: MD_HEADER_SIGNATURE,
version: MD_HEADER_VERSION,
stream_count: num_writers,
- // header.get()->stream_directory_rva = dir.position();
stream_directory_rva: dir_section.position(),
checksum: 0, /* Can be 0. In fact, that's all that's
* been found in minidump files. */
@@ -295,28 +292,22 @@ impl MinidumpWriter {
// we should have a mostly-intact dump
dir_section.write_to_file(buffer, None)?;
- let dirent = thread_list_stream::write(self, buffer, &dumper)?;
- // Write section to file
+ let dirent = thread_list_stream::write(self, buffer, dumper)?;
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = mappings::write(self, buffer, dumper)?;
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
- let _ = app_memory::write(self, buffer)?;
- // Write section to file
+ app_memory::write(self, buffer)?;
dir_section.write_to_file(buffer, None)?;
let dirent = memory_list_stream::write(self, buffer)?;
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = exception_stream::write(self, buffer)?;
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = systeminfo_stream::write(buffer)?;
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, "/proc/cpuinfo") {
@@ -326,7 +317,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, &format!("/proc/{}/status", self.blamed_thread))
@@ -337,7 +327,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self
@@ -350,7 +339,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, &format!("/proc/{}/cmdline", self.blamed_thread))
@@ -361,7 +349,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, &format!("/proc/{}/environ", self.blamed_thread))
@@ -372,7 +359,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, &format!("/proc/{}/auxv", self.blamed_thread)) {
@@ -382,7 +368,6 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = match self.write_file(buffer, &format!("/proc/{}/maps", self.blamed_thread)) {
@@ -392,16 +377,13 @@ impl MinidumpWriter {
},
Err(_) => Default::default(),
};
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = dso_debug::write_dso_debug_stream(buffer, self.blamed_thread, &dumper.auxv)
.unwrap_or_default();
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = thread_names_stream::write(buffer, dumper)?;
- // Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
// If you add more directory entries, don't forget to update kNumWriters,
@@ -409,16 +391,15 @@ impl MinidumpWriter {
Ok(())
}
+ #[allow(clippy::unused_self)]
fn write_file(
&self,
buffer: &mut DumpBuf,
filename: &str,
) -> std::result::Result {
- let mut file = std::fs::File::open(std::path::PathBuf::from(filename))?;
- let mut content = Vec::new();
- file.read_to_end(&mut content)?;
+ let content = std::fs::read(filename)?;
- let section = MemoryArrayWriter::::alloc_from_array(buffer, &content)?;
+ let section = MemoryArrayWriter::write_bytes(buffer, &content);
Ok(section.location())
}
}
diff --git a/src/linux_ptrace_dumper.rs b/src/linux/ptrace_dumper.rs
similarity index 84%
rename from src/linux_ptrace_dumper.rs
rename to src/linux/ptrace_dumper.rs
index f1e249b0..3f192ed5 100644
--- a/src/linux_ptrace_dumper.rs
+++ b/src/linux/ptrace_dumper.rs
@@ -1,21 +1,25 @@
// use libc::c_void;
#[cfg(target_os = "android")]
-use crate::android::late_process_mappings;
-use crate::auxv_reader::{AuxvType, ProcfsAuxvIter};
-use crate::errors::{DumperError, InitError, ThreadInfoError};
-use crate::maps_reader::{MappingInfo, MappingInfoParsingResult, DELETED_SUFFIX};
-use crate::minidump_format::MDGUID;
-use crate::thread_info::{Pid, ThreadInfo};
-use crate::LINUX_GATE_LIBRARY_NAME;
+use crate::linux::android::late_process_mappings;
+use crate::{
+ linux::{
+ auxv_reader::{AuxvType, ProcfsAuxvIter},
+ errors::{DumperError, InitError, ThreadInfoError},
+ maps_reader::{MappingInfo, MappingInfoParsingResult, DELETED_SUFFIX},
+ thread_info::{Pid, ThreadInfo},
+ LINUX_GATE_LIBRARY_NAME,
+ },
+ minidump_format::GUID,
+};
use goblin::elf;
-use nix::errno::Errno;
use nix::sys::{ptrace, wait};
-use std::collections::HashMap;
-use std::convert::TryInto;
-use std::ffi::c_void;
-use std::io::{BufRead, BufReader};
-use std::path;
-use std::result::Result;
+use std::{
+ collections::HashMap,
+ ffi::c_void,
+ io::{BufRead, BufReader},
+ path,
+ result::Result,
+};
#[derive(Debug, Clone)]
pub struct Thread {
@@ -24,7 +28,7 @@ pub struct Thread {
}
#[derive(Debug)]
-pub struct LinuxPtraceDumper {
+pub struct PtraceDumper {
pub pid: Pid,
threads_suspended: bool,
pub threads: Vec,
@@ -37,18 +41,18 @@ pub const AT_SYSINFO_EHDR: u32 = 33;
#[cfg(target_pointer_width = "64")]
pub const AT_SYSINFO_EHDR: u64 = 33;
-impl Drop for LinuxPtraceDumper {
+impl Drop for PtraceDumper {
fn drop(&mut self) {
// Always try to resume all threads (e.g. in case of error)
let _ = self.resume_threads();
}
}
-impl LinuxPtraceDumper {
+impl PtraceDumper {
/// Constructs a dumper for extracting information of a given process
/// with a process ID of |pid|.
pub fn new(pid: Pid) -> Result {
- let mut dumper = LinuxPtraceDumper {
+ let mut dumper = Self {
pid,
threads_suspended: false,
threads: Vec::new(),
@@ -67,6 +71,7 @@ impl LinuxPtraceDumper {
Ok(())
}
+ #[cfg_attr(not(target_os = "android"), allow(clippy::unused_self))]
pub fn late_init(&mut self) -> Result<(), InitError> {
#[cfg(target_os = "android")]
{
@@ -107,8 +112,8 @@ impl LinuxPtraceDumper {
loop {
match wait::waitpid(pid, Some(wait::WaitPidFlag::__WALL)) {
Ok(_) => break,
- Err(e @ nix::Error::Sys(Errno::EINTR)) => {
- ptrace::detach(pid).map_err(|e| DetachErr(child, e))?;
+ Err(e @ nix::Error::EINTR) => {
+ ptrace::detach(pid, None).map_err(|e| DetachErr(child, e))?;
return Err(DumperError::WaitPidError(child, e));
}
Err(_) => continue,
@@ -138,7 +143,7 @@ impl LinuxPtraceDumper {
skip_thread = true;
}
if skip_thread {
- ptrace::detach(pid).map_err(|e| DetachErr(child, e))?;
+ ptrace::detach(pid, None).map_err(|e| DetachErr(child, e))?;
return Err(DumperError::DetachSkippedThread(child));
}
}
@@ -149,7 +154,7 @@ impl LinuxPtraceDumper {
pub fn resume_thread(child: Pid) -> Result<(), DumperError> {
use DumperError::PtraceDetachError as DetachErr;
let pid = nix::unistd::Pid::from_raw(child);
- ptrace::detach(pid).map_err(|e| DetachErr(child, e))?;
+ ptrace::detach(pid, None).map_err(|e| DetachErr(child, e))?;
Ok(())
}
@@ -209,7 +214,7 @@ impl LinuxPtraceDumper {
.ok();
(tid, name)
})
- .for_each(|(tid, name)| self.threads.push(Thread { tid, name }))
+ .for_each(|(tid, name)| self.threads.push(Thread { tid, name }));
}
Ok(())
}
@@ -267,8 +272,7 @@ impl LinuxPtraceDumper {
let line = line.map_err(errmap)?;
match MappingInfo::parse_from_line(&line, linux_gate_loc, self.mappings.last_mut()) {
Ok(MappingInfoParsingResult::Success(map)) => self.mappings.push(map),
- Ok(MappingInfoParsingResult::SkipLine) => continue,
- Err(_) => continue,
+ Ok(MappingInfoParsingResult::SkipLine) | Err(_) => continue,
}
}
@@ -347,12 +351,12 @@ impl LinuxPtraceDumper {
let defaced;
#[cfg(target_pointer_width = "64")]
{
- defaced = 0x0defaced0defacedusize.to_ne_bytes()
+ defaced = 0x0defaced0defacedusize.to_ne_bytes();
}
#[cfg(target_pointer_width = "32")]
{
- defaced = 0x0defacedusize.to_ne_bytes()
- };
+ defaced = 0x0defacedusize.to_ne_bytes();
+ }
// the bitfield length is 2^test_bits long.
let test_bits = 11;
// byte length of the corresponding array.
@@ -439,28 +443,24 @@ impl LinuxPtraceDumper {
Ok(())
}
- // Find the mapping which the given memory address falls in.
+ /// Find the mapping which the given memory address falls in.
+ #[inline]
pub fn find_mapping(&self, address: usize) -> Option<&MappingInfo> {
- for map in &self.mappings {
- if address >= map.start_address && address - map.start_address < map.size {
- return Some(&map);
- }
- }
- None
+ self.mappings
+ .iter()
+ .find(|map| address >= map.start_address && address - map.start_address < map.size)
}
- // Find the mapping which the given memory address falls in. Uses the
- // unadjusted mapping address range from the kernel, rather than the
- // biased range.
+ /// Find the mapping which the given memory address falls in.
+ ///
+ /// Uses the unadjusted mapping address range from the kernel, rather than
+ /// the biased range.
+ #[inline]
pub fn find_mapping_no_bias(&self, address: usize) -> Option<&MappingInfo> {
- for map in &self.mappings {
- if address >= map.system_mapping_info.start_address
+ self.mappings.iter().find(|map| {
+ address >= map.system_mapping_info.start_address
&& address < map.system_mapping_info.end_address
- {
- return Some(&map);
- }
- }
- None
+ })
}
fn parse_build_id<'data>(
@@ -486,41 +486,40 @@ impl LinuxPtraceDumper {
pub fn elf_file_identifier_from_mapped_file(mem_slice: &[u8]) -> Result, DumperError> {
let elf_obj = elf::Elf::parse(mem_slice)?;
- match Self::parse_build_id(&elf_obj, mem_slice) {
+ if let Some(build_id) = Self::parse_build_id(&elf_obj, mem_slice) {
// Look for a build id note first.
- Some(build_id) => Ok(build_id.to_vec()),
+ Ok(build_id.to_vec())
+ } else {
// Fall back on hashing the first page of the text section.
- None => {
- // Attempt to locate the .text section of an ELF binary and generate
- // a simple hash by XORing the first page worth of bytes into |result|.
- for section in elf_obj.section_headers {
- if section.sh_type != elf::section_header::SHT_PROGBITS {
- continue;
- }
- if section.sh_flags & u64::from(elf::section_header::SHF_ALLOC) != 0 {
- if section.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0 {
- let text_section = &mem_slice[section.sh_offset as usize..]
- [..section.sh_size as usize];
- // Only provide mem::size_of(MDGUID) bytes to keep identifiers produced by this
- // function backwards-compatible.
- let max_len = std::cmp::min(text_section.len(), 4096);
- let mut result = vec![0u8; std::mem::size_of::()];
- let mut offset = 0;
- while offset < max_len {
- for idx in 0..std::mem::size_of::() {
- if offset + idx >= text_section.len() {
- break;
- }
- result[idx] ^= text_section[offset + idx];
- }
- offset += std::mem::size_of::();
+ // Attempt to locate the .text section of an ELF binary and generate
+ // a simple hash by XORing the first page worth of bytes into |result|.
+ for section in elf_obj.section_headers {
+ if section.sh_type != elf::section_header::SHT_PROGBITS {
+ continue;
+ }
+ if section.sh_flags & elf::section_header::SHF_ALLOC as u64 != 0
+ && section.sh_flags & elf::section_header::SHF_EXECINSTR as u64 != 0
+ {
+ let text_section =
+ &mem_slice[section.sh_offset as usize..][..section.sh_size as usize];
+ // Only provide mem::size_of(MDGUID) bytes to keep identifiers produced by this
+ // function backwards-compatible.
+ let max_len = std::cmp::min(text_section.len(), 4096);
+ let mut result = vec![0u8; std::mem::size_of::()];
+ let mut offset = 0;
+ while offset < max_len {
+ for idx in 0..std::mem::size_of::() {
+ if offset + idx >= text_section.len() {
+ break;
}
- return Ok(result);
+ result[idx] ^= text_section[offset + idx];
}
+ offset += std::mem::size_of::();
}
+ return Ok(result);
}
- Err(DumperError::NoBuildIDFound)
}
+ Err(DumperError::NoBuildIDFound)
}
}
@@ -557,7 +556,7 @@ impl LinuxPtraceDumper {
}
}
let new_name = MappingInfo::handle_deleted_file_in_mapping(
- &mapping.name.as_ref().unwrap_or(&String::new()),
+ mapping.name.as_deref().unwrap_or(""),
pid,
)?;
diff --git a/src/linux/sections.rs b/src/linux/sections.rs
new file mode 100644
index 00000000..b7850ac0
--- /dev/null
+++ b/src/linux/sections.rs
@@ -0,0 +1,276 @@
+pub mod app_memory;
+pub mod exception_stream;
+pub mod mappings;
+pub mod memory_list_stream;
+pub mod systeminfo_stream;
+pub mod thread_list_stream;
+pub mod thread_names_stream;
+
+use crate::{
+ errors::{self, MemoryWriterError},
+ linux::{
+ minidump_writer::{self, DumpBuf, MinidumpWriter},
+ ptrace_dumper::PtraceDumper,
+ },
+ minidump_format::*,
+};
+use scroll::ctx::{SizeWith, TryIntoCtx};
+
+type WriteResult = std::result::Result;
+
+macro_rules! size {
+ ($t:ty) => {
+ <$t>::size_with(&scroll::Endian::Little)
+ };
+}
+
+pub struct Buffer {
+ inner: Vec,
+}
+
+impl Buffer {
+ pub fn with_capacity(cap: usize) -> Self {
+ Self {
+ inner: Vec::with_capacity(cap),
+ }
+ }
+
+ #[inline]
+ pub fn position(&self) -> u64 {
+ self.inner.len() as u64
+ }
+
+ #[inline]
+ #[must_use]
+ fn reserve(&mut self, len: usize) -> usize {
+ let mark = self.inner.len();
+ self.inner.resize(self.inner.len() + len, 0);
+ mark
+ }
+
+ #[inline]
+ fn write(&mut self, val: N) -> Result
+ where
+ N: TryIntoCtx + SizeWith,
+ E: From,
+ {
+ self.write_at(self.inner.len(), val)
+ }
+
+ fn write_at(&mut self, offset: usize, val: N) -> Result
+ where
+ N: TryIntoCtx + SizeWith,
+ E: From,
+ {
+ let to_write = size!(N);
+ let remainder = self.inner.len() - offset;
+ if remainder < to_write {
+ self.inner
+ .resize(self.inner.len() + to_write - remainder, 0);
+ }
+
+ let dst = &mut self.inner[offset..offset + to_write];
+ val.try_into_ctx(dst, scroll::Endian::Little)
+ }
+
+ #[inline]
+ pub fn write_all(&mut self, buffer: &[u8]) {
+ self.inner.extend_from_slice(buffer);
+ }
+}
+
+impl From for Vec {
+ fn from(b: Buffer) -> Self {
+ b.inner
+ }
+}
+
+impl std::ops::Deref for Buffer {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub struct MemoryWriter {
+ pub position: MDRVA,
+ pub size: usize,
+ phantom: std::marker::PhantomData,
+}
+
+impl MemoryWriter
+where
+ T: TryIntoCtx + SizeWith,
+{
+ /// Create a slot for a type T in the buffer, we can fill right now with real values.
+ pub fn alloc_with_val(buffer: &mut Buffer, val: T) -> WriteResult {
+ // Mark the position as we may overwrite later
+ let position = buffer.position();
+ let size = buffer.write(val)?;
+
+ Ok(Self {
+ position: position as u32,
+ size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Create a slot for a type T in the buffer, we can fill later with real values.
+ pub fn alloc(buffer: &mut Buffer) -> WriteResult {
+ let size = size!(T);
+ let position = buffer.reserve(size) as u32;
+
+ Ok(Self {
+ position: position as u32,
+ size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Write actual values in the buffer-slot we got during `alloc()`
+ #[inline]
+ pub fn set_value(&mut self, buffer: &mut Buffer, val: T) -> WriteResult<()> {
+ Ok(buffer.write_at(self.position as usize, val).map(|_sz| ())?)
+ }
+
+ #[inline]
+ pub fn location(&self) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: size!(T) as u32,
+ rva: self.position,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub struct MemoryArrayWriter {
+ pub position: MDRVA,
+ array_size: usize,
+ phantom: std::marker::PhantomData,
+}
+
+impl MemoryArrayWriter {
+ #[inline]
+ pub fn write_bytes(buffer: &mut Buffer, slice: &[u8]) -> Self {
+ let position = buffer.position();
+ buffer.write_all(slice);
+
+ Self {
+ position: position as u32,
+ array_size: slice.len(),
+ phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+impl MemoryArrayWriter
+where
+ T: TryIntoCtx + SizeWith + Copy,
+{
+ pub fn alloc_from_array(buffer: &mut Buffer, array: &[T]) -> WriteResult {
+ let array_size = array.len();
+ let position = buffer.reserve(array_size * size!(T));
+
+ for (idx, val) in array.iter().enumerate() {
+ buffer.write_at(position + idx * size!(T), *val)?;
+ }
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+}
+
+impl MemoryArrayWriter
+where
+ T: TryIntoCtx + SizeWith,
+{
+ /// Create a slot for a type T in the buffer, we can fill in the values in one go.
+ pub fn alloc_from_iter(
+ buffer: &mut Buffer,
+ iter: impl IntoIterator- ,
+ ) -> WriteResult
+ where
+ I: std::iter::ExactSizeIterator
- ,
+ {
+ let iter = iter.into_iter();
+ let array_size = iter.len();
+ let size = size!(T);
+ let position = buffer.reserve(array_size * size);
+
+ for (idx, val) in iter.enumerate() {
+ buffer.write_at(position + idx * size, val)?;
+ }
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Create a slot for a type T in the buffer, we can fill later with real values.
+ /// This function fills it with `Default::default()`, which is less performant than
+ /// using uninitialized memory, but safe.
+ pub fn alloc_array(buffer: &mut Buffer, array_size: usize) -> WriteResult {
+ let position = buffer.reserve(array_size * size!(T));
+
+ Ok(Self {
+ position: position as u32,
+ array_size,
+ phantom: std::marker::PhantomData,
+ })
+ }
+
+ /// Write actual values in the buffer-slot we got during `alloc()`
+ #[inline]
+ pub fn set_value_at(&mut self, buffer: &mut Buffer, val: T, index: usize) -> WriteResult<()> {
+ Ok(buffer
+ .write_at(self.position as usize + size!(T) * index, val)
+ .map(|_sz| ())?)
+ }
+
+ #[inline]
+ pub fn location(&self) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: (self.array_size * size!(T)) as u32,
+ rva: self.position,
+ }
+ }
+
+ #[inline]
+ pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor {
+ MDLocationDescriptor {
+ data_size: size!(T) as u32,
+ rva: self.position + (size!(T) * idx) as u32,
+ }
+ }
+}
+
+pub fn write_string_to_location(
+ buffer: &mut Buffer,
+ text: &str,
+) -> WriteResult {
+ let letters: Vec = text.encode_utf16().collect();
+
+ // First write size of the string (x letters in u16, times the size of u16)
+ let text_header = MemoryWriter::::alloc_with_val(
+ buffer,
+ (letters.len() * std::mem::size_of::()).try_into()?,
+ )?;
+
+ // Then write utf-16 letters after that
+ let mut text_section = MemoryArrayWriter::::alloc_array(buffer, letters.len())?;
+ for (index, letter) in letters.iter().enumerate() {
+ text_section.set_value_at(buffer, *letter, index)?;
+ }
+
+ let mut location = text_header.location();
+ location.data_size += text_section.location().data_size;
+
+ Ok(location)
+}
diff --git a/src/linux/sections/app_memory.rs b/src/linux/sections/app_memory.rs
new file mode 100644
index 00000000..824e0cf5
--- /dev/null
+++ b/src/linux/sections/app_memory.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+/// Write application-provided memory regions.
+pub fn write(
+ config: &mut MinidumpWriter,
+ buffer: &mut DumpBuf,
+) -> std::result::Result<(), crate::linux::errors::SectionAppMemoryError> {
+ for app_memory in &config.app_memory {
+ let data_copy = PtraceDumper::copy_from_process(
+ config.blamed_thread,
+ app_memory.ptr as *mut libc::c_void,
+ app_memory.length,
+ )?;
+
+ let section = MemoryArrayWriter::write_bytes(buffer, &data_copy);
+ let desc = MDMemoryDescriptor {
+ start_of_memory_range: app_memory.ptr as u64,
+ memory: section.location(),
+ };
+ config.memory_blocks.push(desc);
+ }
+ Ok(())
+}
diff --git a/src/sections/exception_stream.rs b/src/linux/sections/exception_stream.rs
similarity index 88%
rename from src/sections/exception_stream.rs
rename to src/linux/sections/exception_stream.rs
index 65e14119..4cddc3e8 100644
--- a/src/sections/exception_stream.rs
+++ b/src/linux/sections/exception_stream.rs
@@ -1,9 +1,5 @@
-use crate::errors::SectionExceptionStreamError;
-use crate::minidump_format::*;
-use crate::minidump_writer::{CrashingThreadContext, DumpBuf, MinidumpWriter};
-use crate::sections::MemoryWriter;
-
-type Result = std::result::Result;
+use super::minidump_writer::CrashingThreadContext;
+use super::*;
#[allow(non_camel_case_types, unused)]
#[repr(u32)]
@@ -45,7 +41,10 @@ enum MDExceptionCodeLinux {
dump requested. */
}
-pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result {
+pub fn write(
+ config: &mut MinidumpWriter,
+ buffer: &mut DumpBuf,
+) -> std::result::Result {
let exception = if let Some(context) = &config.crash_context {
let sig_addr;
#[cfg(target_arch = "arm")]
@@ -67,11 +66,11 @@ pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result Result ctx,
- CrashingThreadContext::CrashContext(ctx) => ctx,
+ CrashingThreadContext::CrashContextPlusAddress((ctx, _))
+ | CrashingThreadContext::CrashContext(ctx) => ctx,
CrashingThreadContext::None => MDLocationDescriptor {
data_size: 0,
rva: 0,
diff --git a/src/sections/mappings.rs b/src/linux/sections/mappings.rs
similarity index 79%
rename from src/sections/mappings.rs
rename to src/linux/sections/mappings.rs
index d95741d0..6fb875fc 100644
--- a/src/sections/mappings.rs
+++ b/src/linux/sections/mappings.rs
@@ -1,11 +1,7 @@
-use crate::errors::SectionMappingsError;
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::maps_reader::MappingInfo;
-use crate::minidump_format::*;
-use crate::minidump_writer::{DumpBuf, MinidumpWriter};
-use crate::sections::{write_string_to_location, MemoryArrayWriter, MemoryWriter};
+use super::*;
+use crate::linux::maps_reader::MappingInfo;
-type Result = std::result::Result;
+type Result = std::result::Result;
/// Write information about the mappings in effect. Because we are using the
/// minidump format, the information about the mappings is pretty limited.
@@ -14,7 +10,7 @@ type Result = std::result::Result;
pub fn write(
config: &mut MinidumpWriter,
buffer: &mut DumpBuf,
- dumper: &mut LinuxPtraceDumper,
+ dumper: &mut PtraceDumper,
) -> Result {
let mut modules = Vec::new();
@@ -47,7 +43,7 @@ pub fn write(
for user in &config.user_mapping_list {
// GUID was provided by caller.
let module = fill_raw_module(buffer, &user.mapping, &user.identifier)?;
- modules.push(module)
+ modules.push(module);
}
let list_header = MemoryWriter::::alloc_with_val(buffer, modules.len() as u32)?;
@@ -58,7 +54,7 @@ pub fn write(
};
if !modules.is_empty() {
- let mapping_list = MemoryArrayWriter::::alloc_from_array(buffer, &modules)?;
+ let mapping_list = MemoryArrayWriter::::alloc_from_iter(buffer, modules)?;
dirent.location.data_size += mapping_list.location().data_size;
}
@@ -70,12 +66,11 @@ fn fill_raw_module(
mapping: &MappingInfo,
identifier: &[u8],
) -> Result {
- let cv_record: MDLocationDescriptor;
- if identifier.is_empty() {
+ let cv_record = if identifier.is_empty() {
// Just zeroes
- cv_record = Default::default();
+ Default::default()
} else {
- let cv_signature = MD_CVINFOELF_SIGNATURE;
+ let cv_signature = minidump_common::format::CvSignature::Elf as u32;
let array_size = std::mem::size_of_val(&cv_signature) + identifier.len();
let mut sig_section = MemoryArrayWriter::::alloc_array(buffer, array_size)?;
@@ -87,12 +82,12 @@ fn fill_raw_module(
{
sig_section.set_value_at(buffer, *val, index)?;
}
- cv_record = sig_section.location();
- }
+ sig_section.location()
+ };
let (file_path, _) = mapping
.get_mapping_effective_name_and_path()
- .map_err(|e| SectionMappingsError::GetEffectivePathError(mapping.clone(), e))?;
+ .map_err(|e| errors::SectionMappingsError::GetEffectivePathError(mapping.clone(), e))?;
let name_header = write_string_to_location(buffer, &file_path)?;
Ok(MDRawModule {
diff --git a/src/sections/memory_list_stream.rs b/src/linux/sections/memory_list_stream.rs
similarity index 69%
rename from src/sections/memory_list_stream.rs
rename to src/linux/sections/memory_list_stream.rs
index 4f090831..572049ea 100644
--- a/src/sections/memory_list_stream.rs
+++ b/src/linux/sections/memory_list_stream.rs
@@ -1,9 +1,6 @@
-use crate::errors::SectionMemListError;
-use crate::minidump_format::*;
-use crate::minidump_writer::{DumpBuf, MinidumpWriter};
-use crate::sections::{MemoryArrayWriter, MemoryWriter};
+use super::*;
-type Result = std::result::Result;
+type Result = std::result::Result;
pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result {
let list_header =
diff --git a/src/linux/sections/systeminfo_stream.rs b/src/linux/sections/systeminfo_stream.rs
new file mode 100644
index 00000000..5fc68eff
--- /dev/null
+++ b/src/linux/sections/systeminfo_stream.rs
@@ -0,0 +1,25 @@
+use super::*;
+use crate::linux::dumper_cpu_info::{os_information, write_cpu_information};
+
+type Result = std::result::Result;
+
+pub fn write(buffer: &mut DumpBuf) -> Result {
+ let mut info_section = MemoryWriter::::alloc(buffer)?;
+ let dirent = MDRawDirectory {
+ stream_type: MDStreamType::SystemInfoStream as u32,
+ location: info_section.location(),
+ };
+
+ let (platform_id, os_version) = os_information();
+ let os_version_loc = write_string_to_location(buffer, &os_version)?;
+
+ // SAFETY: POD
+ let mut info = unsafe { std::mem::zeroed::() };
+ info.platform_id = platform_id as u32;
+ info.csd_version_rva = os_version_loc.rva;
+
+ write_cpu_information(&mut info)?;
+
+ info_section.set_value(buffer, info)?;
+ Ok(dirent)
+}
diff --git a/src/sections/thread_list_stream.rs b/src/linux/sections/thread_list_stream.rs
similarity index 91%
rename from src/sections/thread_list_stream.rs
rename to src/linux/sections/thread_list_stream.rs
index d3672d99..3d6ac0ef 100644
--- a/src/sections/thread_list_stream.rs
+++ b/src/linux/sections/thread_list_stream.rs
@@ -1,13 +1,8 @@
-use crate::errors::SectionThreadListError;
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::minidump_cpu::RawContextCPU;
-use crate::minidump_format::*;
-use crate::minidump_writer::{CrashingThreadContext, DumpBuf, MinidumpWriter};
-use crate::sections::{MemoryArrayWriter, MemoryWriter};
-use std::convert::TryInto;
-use std::io::Write;
+use super::*;
+use crate::{minidump_cpu::RawContextCPU, minidump_writer::CrashingThreadContext};
+use crash_context::CpuContext;
-type Result = std::result::Result;
+type Result = std::result::Result;
// The following kLimit* constants are for when minidump_size_limit_ is set
// and the minidump size might exceed it.
@@ -34,7 +29,7 @@ enum MaxStackLen {
pub fn write(
config: &mut MinidumpWriter,
buffer: &mut DumpBuf,
- dumper: &LinuxPtraceDumper,
+ dumper: &PtraceDumper,
) -> Result {
let num_threads = dumper.threads.len();
// Memory looks like this:
@@ -68,7 +63,12 @@ pub fn write(
for (idx, item) in dumper.threads.clone().iter().enumerate() {
let mut thread = MDRawThread {
thread_id: item.tid.try_into()?,
- ..Default::default()
+ suspend_count: 0,
+ priority_class: 0,
+ priority: 0,
+ teb: 0,
+ stack: MDMemoryDescriptor::default(),
+ thread_context: MDLocationDescriptor::default(),
};
// We have a different source of information for the crashing thread. If
@@ -77,8 +77,8 @@ pub fn write(
// unhelpful.
if config.crash_context.is_some() && thread.thread_id == config.blamed_thread as u32 {
let crash_context = config.crash_context.as_ref().unwrap();
- let instruction_ptr = crash_context.get_instruction_pointer() as usize;
- let stack_pointer = crash_context.get_stack_pointer() as usize;
+ let instruction_ptr = crash_context.instruction_pointer();
+ let stack_pointer = crash_context.stack_pointer();
fill_thread_stack(
config,
buffer,
@@ -116,7 +116,7 @@ pub fn write(
ip_memory_d.memory.data_size =
(end_of_range - ip_memory_d.start_of_memory_range) as u32;
- let memory_copy = LinuxPtraceDumper::copy_from_process(
+ let memory_copy = PtraceDumper::copy_from_process(
thread.thread_id as i32,
ip_memory_d.start_of_memory_range as *mut libc::c_void,
ip_memory_d.memory.data_size as usize,
@@ -178,7 +178,7 @@ pub fn write(
fn fill_thread_stack(
config: &mut MinidumpWriter,
buffer: &mut DumpBuf,
- dumper: &LinuxPtraceDumper,
+ dumper: &PtraceDumper,
thread: &mut MDRawThread,
instruction_ptr: usize,
stack_ptr: usize,
@@ -203,7 +203,7 @@ fn fill_thread_stack(
}
}
- let mut stack_bytes = LinuxPtraceDumper::copy_from_process(
+ let mut stack_bytes = PtraceDumper::copy_from_process(
thread.thread_id.try_into()?,
stack as *mut libc::c_void,
stack_len,
@@ -232,7 +232,7 @@ fn fill_thread_stack(
data_size: stack_bytes.len() as u32,
rva: buffer.position() as u32,
};
- buffer.write_all(&stack_bytes)?;
+ buffer.write_all(&stack_bytes);
thread.stack.start_of_memory_range = stack as u64;
thread.stack.memory = stack_location;
config.memory_blocks.push(thread.stack.clone());
diff --git a/src/sections/thread_names_stream.rs b/src/linux/sections/thread_names_stream.rs
similarity index 65%
rename from src/sections/thread_names_stream.rs
rename to src/linux/sections/thread_names_stream.rs
index decb1674..2e97f3cf 100644
--- a/src/sections/thread_names_stream.rs
+++ b/src/linux/sections/thread_names_stream.rs
@@ -1,14 +1,8 @@
-use crate::errors::SectionThreadNamesError;
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::minidump_format::*;
-use crate::minidump_writer::DumpBuf;
-use crate::sections::write_string_to_location;
-use crate::sections::{MemoryArrayWriter, MemoryWriter};
-use std::convert::TryInto;
+use super::*;
-type Result = std::result::Result;
+type Result = std::result::Result;
-pub fn write(buffer: &mut DumpBuf, dumper: &LinuxPtraceDumper) -> Result {
+pub fn write(buffer: &mut DumpBuf, dumper: &PtraceDumper) -> Result {
// Only count threads that have a name
let num_threads = dumper.threads.iter().filter(|t| t.name.is_some()).count();
// Memory looks like this:
@@ -26,7 +20,7 @@ pub fn write(buffer: &mut DumpBuf, dumper: &LinuxPtraceDumper) -> Result = std::result::Result;
pub type Pid = i32;
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-#[path = "thread_info_x86.rs"]
-mod imp;
-#[cfg(target_arch = "arm")]
-#[path = "thread_info_arm.rs"]
-mod imp;
-#[cfg(target_arch = "aarch64")]
-#[path = "thread_info_aarch64.rs"]
-mod imp;
-#[cfg(target_arch = "mips")]
-#[path = "thread_info_mips.rs"]
-mod imp;
-
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-pub type ThreadInfo = imp::ThreadInfoX86;
-#[cfg(target_arch = "arm")]
-pub type ThreadInfo = imp::ThreadInfoArm;
-#[cfg(target_arch = "aarch64")]
-pub type ThreadInfo = imp::ThreadInfoAarch64;
-#[cfg(target_arch = "mips")]
-pub type ThreadInfo = imp::ThreadInfoMips;
+cfg_if::cfg_if! {
+ if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
+ mod x86;
+ pub type ThreadInfo = x86::ThreadInfoX86;
+ } else if #[cfg(target_arch = "arm")] {
+ mod arm;
+ pub type ThreadInfo = arm::ThreadInfoArm;
+ } else if #[cfg(target_arch = "aarch64")] {
+ mod aarch64;
+ pub type ThreadInfo = aarch64::ThreadInfoAarch64;
+ } else if #[cfg(target_arch = "mips")] {
+ mod mips;
+ pub type ThreadInfo = mips::ThreadInfoMips;
+ }
+}
#[derive(Debug)]
#[allow(non_camel_case_types)]
enum NT_Elf {
NT_NONE = 0,
NT_PRSTATUS = 1,
- NT_PRFPREG = 2,
+ NT_PRFPREGSET = 2,
//NT_PRPSINFO = 3,
//NT_TASKSTRUCT = 4,
//NT_AUXV = 6,
}
-pub fn to_u128(slice: &[u32]) -> Vec {
- let mut res = Vec::new();
- for chunk in slice.chunks_exact(4) {
- let value = u128::from_ne_bytes(
- chunk
- .iter()
- .map(|x| x.to_ne_bytes().to_vec())
- .flatten()
- .collect::>()
- .as_slice()
- .try_into() // Make slice into fixed size array
- .unwrap(), // Which has to work as we know the numbers work out
- );
- res.push(value)
- }
- res
+#[inline]
+pub fn to_u128(slice: &[u32]) -> &[u128] {
+ unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), slice.len().saturating_div(4)) }
+}
+
+#[inline]
+pub fn copy_registers(dst: &mut [u128], src: &[u128]) {
+ let to_copy = std::cmp::min(dst.len(), src.len());
+ dst[..to_copy].copy_from_slice(&src[..to_copy]);
+}
+
+#[inline]
+pub fn copy_u32_registers(dst: &mut [u128], src: &[u32]) {
+ copy_registers(dst, to_u128(src));
}
trait CommonThreadInfo {
@@ -78,25 +69,26 @@ trait CommonThreadInfo {
tgid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
- .parse::()?
+ .parse::()?;
}
"PPid:\t" => {
ppid = l
.get(6..)
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
- .parse::()?
+ .parse::()?;
}
_ => continue,
}
}
if ppid == -1 || tgid == -1 {
- return Err(ThreadInfoError::InvalidPid(
+ Err(ThreadInfoError::InvalidPid(
format!("/proc/{}/status", tid),
ppid,
tgid,
- ));
+ ))
+ } else {
+ Ok((ppid, tgid))
}
- Ok((ppid, tgid))
}
/// SLIGHTLY MODIFIED COPY FROM CRATE nix
@@ -109,13 +101,13 @@ trait CommonThreadInfo {
flag: Option,
pid: nix::unistd::Pid,
) -> Result {
- let mut data = std::mem::MaybeUninit::uninit();
+ let mut data = std::mem::MaybeUninit::::uninit();
let res = unsafe {
libc::ptrace(
request as ptrace::RequestType,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
- data.as_mut_ptr() as *const _ as *const libc::c_void,
+ data.as_mut_ptr(),
)
};
Errno::result(res)?;
@@ -132,9 +124,9 @@ trait CommonThreadInfo {
flag: Option,
pid: nix::unistd::Pid,
) -> Result {
- let mut data = std::mem::MaybeUninit::uninit();
+ let mut data = std::mem::MaybeUninit::::uninit();
let io = libc::iovec {
- iov_base: data.as_mut_ptr() as *mut libc::c_void,
+ iov_base: data.as_mut_ptr().cast(),
iov_len: std::mem::size_of::(),
};
let res = unsafe {
@@ -142,7 +134,7 @@ trait CommonThreadInfo {
request as ptrace::RequestType,
libc::pid_t::from(pid),
flag.unwrap_or(NT_Elf::NT_NONE),
- &io as *const _ as *const libc::c_void,
+ (&io as *const libc::iovec).cast::(),
)
};
Errno::result(res)?;
@@ -166,13 +158,14 @@ trait CommonThreadInfo {
)
};
match Errno::result(ret) {
- Ok(..) | Err(nix::Error::Sys(Errno::UnknownErrno)) => Ok(ret),
+ Ok(..) | Err(nix::Error::UnknownErrno) => Ok(ret),
err @ Err(..) => err,
}
}
}
impl ThreadInfo {
- pub fn create(pid: Pid, tid: Pid) -> std::result::Result {
- Self::create_impl(pid, tid)
+ pub fn create(_pid: Pid, tid: Pid) -> std::result::Result {
+ let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
+ Self::create_impl(tid, ppid, tgid)
}
}
diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs
new file mode 100644
index 00000000..878ed7cb
--- /dev/null
+++ b/src/linux/thread_info/aarch64.rs
@@ -0,0 +1,129 @@
+use super::{CommonThreadInfo, Pid};
+use crate::errors::ThreadInfoError;
+use crate::minidump_cpu::RawContextCPU;
+use nix::sys::ptrace;
+
+pub const MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT: usize = 32;
+pub const MD_CONTEXT_ARM64_GPR_COUNT: usize = 33;
+
+/* Indices into iregs for registers with a dedicated or conventional
+ * purpose.
+ */
+pub enum MDARM64RegisterNumbers {
+ Fp = 29,
+ Lr = 30,
+ Sp = 31,
+ Pc = 32,
+}
+
+/// https://github.com/rust-lang/libc/pull/2719
+#[derive(Debug)]
+#[allow(non_camel_case_types)]
+pub struct user_fpsimd_struct {
+ pub vregs: [u128; 32],
+ pub fpsr: u32,
+ pub fpcr: u32,
+}
+
+type Result = std::result::Result;
+
+#[cfg(target_arch = "aarch64")]
+#[derive(Debug)]
+pub struct ThreadInfoAarch64 {
+ pub stack_pointer: usize,
+ pub tgid: Pid, // thread group id
+ pub ppid: Pid, // parent process
+ pub regs: libc::user_regs_struct,
+ pub fpregs: user_fpsimd_struct,
+}
+
+impl CommonThreadInfo for ThreadInfoAarch64 {}
+
+impl ThreadInfoAarch64 {
+ pub fn get_instruction_pointer(&self) -> usize {
+ self.regs.pc as usize
+ }
+
+ // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
+ fn getfpregs(pid: Pid) -> Result {
+ Self::ptrace_get_data_via_io::(
+ ptrace::Request::PTRACE_GETREGSET,
+ Some(super::NT_Elf::NT_PRFPREGSET),
+ nix::unistd::Pid::from_raw(pid),
+ )
+ .or_else(|_err| {
+ // TODO: nix restricts PTRACE_GETFPREGS to arm android for some reason
+ let mut data = std::mem::MaybeUninit::::uninit();
+ let res = unsafe {
+ libc::ptrace(
+ 14,
+ libc::pid_t::from(pid),
+ super::NT_Elf::NT_NONE,
+ data.as_mut_ptr(),
+ )
+ };
+ nix::errno::Errno::result(res)?;
+ Ok(unsafe { data.assume_init() })
+ })
+ }
+
+ fn getregs(pid: Pid) -> Result {
+ Self::ptrace_get_data_via_io::(
+ ptrace::Request::PTRACE_GETREGSET,
+ Some(super::NT_Elf::NT_PRSTATUS),
+ nix::unistd::Pid::from_raw(pid),
+ )
+ .or_else(|_err| {
+ // TODO: nix restricts PTRACE_GETREGS to arm android for some reason
+ let mut data = std::mem::MaybeUninit::::uninit();
+ let res = unsafe {
+ libc::ptrace(
+ 12,
+ libc::pid_t::from(pid),
+ super::NT_Elf::NT_NONE,
+ data.as_mut_ptr(),
+ )
+ };
+ nix::errno::Errno::result(res)?;
+ Ok(unsafe { data.assume_init() })
+ })
+ }
+
+ pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
+ out.context_flags =
+ minidump_common::format::ContextFlagsArm64Old::CONTEXT_ARM64_FULL_OLD.bits() as u64;
+
+ /// This is the number of general purpose registers _not_ counting
+ /// the stack pointer
+ const GP_REG_COUNT: usize = 31;
+ /// The number of floating point registers in the floating point save area
+ const FP_REG_COUNT: usize = 32;
+
+ out.cpsr = self.regs.pstate as u32;
+ out.iregs[..GP_REG_COUNT].copy_from_slice(&self.regs.regs[..GP_REG_COUNT]);
+ out.iregs[MDARM64RegisterNumbers::Sp as usize] = self.regs.sp;
+ // Note that in breakpad this was the last member of the iregs field
+ // which was 33 in length, but in rust-minidump it is its own separate
+ // field instead
+ out.pc = self.regs.pc;
+
+ out.float_save.fpsr = self.fpregs.fpsr;
+ out.float_save.fpcr = self.fpregs.fpcr;
+ out.float_save.regs[..FP_REG_COUNT].copy_from_slice(&self.fpregs.vregs[..FP_REG_COUNT]);
+ }
+
+ pub fn create_impl(tid: Pid, ppid: Pid, tgid: Pid) -> Result {
+ let regs = Self::getregs(tid)?;
+ let fpregs = Self::getfpregs(tid)?;
+
+ let stack_pointer = regs.regs[13] as usize;
+
+ Ok(Self {
+ stack_pointer,
+ tgid,
+ ppid,
+ regs,
+ fpregs,
+ })
+ }
+}
diff --git a/src/thread_info/thread_info_arm.rs b/src/linux/thread_info/arm.rs
similarity index 95%
rename from src/thread_info/thread_info_arm.rs
rename to src/linux/thread_info/arm.rs
index a66670ce..4ac955df 100644
--- a/src/thread_info/thread_info_arm.rs
+++ b/src/linux/thread_info/arm.rs
@@ -97,14 +97,13 @@ impl ThreadInfoArm {
}
}
- pub fn create_impl(_pid: Pid, tid: Pid) -> Result {
- let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
+ pub fn create_impl(tid: Pid, ppid: Pid, tgid: Pid) -> Result {
let regs = Self::getregs(tid)?;
let fpregs = Self::getfpregs(tid)?;
let stack_pointer = regs.uregs[13] as usize;
- Ok(ThreadInfoArm {
+ Ok(Self {
stack_pointer,
tgid,
ppid,
diff --git a/src/thread_info/thread_info_mips.rs b/src/linux/thread_info/mips.rs
similarity index 100%
rename from src/thread_info/thread_info_mips.rs
rename to src/linux/thread_info/mips.rs
diff --git a/src/thread_info/thread_info_x86.rs b/src/linux/thread_info/x86.rs
similarity index 57%
rename from src/thread_info/thread_info_x86.rs
rename to src/linux/thread_info/x86.rs
index 6dbf6c2d..eab420b7 100644
--- a/src/thread_info/thread_info_x86.rs
+++ b/src/linux/thread_info/x86.rs
@@ -1,17 +1,12 @@
use super::{CommonThreadInfo, NT_Elf, Pid};
use crate::errors::ThreadInfoError;
-use crate::minidump_cpu::imp::*;
use crate::minidump_cpu::RawContextCPU;
#[cfg(target_arch = "x86_64")]
-use crate::thread_info::to_u128;
+use crate::thread_info::copy_u32_registers;
use core::mem::size_of_val;
-use libc;
-use libc::user;
+use libc::{self, user};
use memoffset;
-use nix::sys::ptrace;
-use nix::unistd;
-#[cfg(target_arch = "x86")]
-use std::convert::TryInto;
+use nix::{sys::ptrace, unistd};
type Result = std::result::Result;
@@ -47,7 +42,7 @@ impl ThreadInfoX86 {
fn getfpregset(pid: Pid) -> Result {
Self::ptrace_get_data_via_io::(
ptrace::Request::PTRACE_GETREGSET,
- Some(NT_Elf::NT_PRFPREG),
+ Some(NT_Elf::NT_PRFPREGSET),
nix::unistd::Pid::from_raw(pid),
)
}
@@ -80,8 +75,7 @@ impl ThreadInfoX86 {
)
}
- pub fn create_impl(_pid: Pid, tid: Pid) -> Result {
- let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?;
+ pub fn create_impl(tid: Pid, ppid: Pid, tgid: Pid) -> Result {
let regs = Self::getregset(tid).or_else(|_| ptrace::getregs(unistd::Pid::from_raw(tid)))?;
let fpregs = Self::getfpregset(tid).or_else(|_| Self::getfpregs(tid))?;
#[cfg(target_arch = "x86")]
@@ -102,18 +96,18 @@ impl ThreadInfoX86 {
let debug_offset = memoffset::offset_of!(user, u_debugreg);
let elem_offset = size_of_val(&dregs[0]);
- for idx in 0..NUM_DEBUG_REGISTERS {
+ for (idx, dreg) in dregs.iter_mut().enumerate() {
let chunk = Self::peek_user(
tid,
(debug_offset + idx * elem_offset) as ptrace::AddressType,
)?;
#[cfg(target_arch = "x86_64")]
{
- dregs[idx] = chunk as u64; // libc / ptrace is very messy wrt int types used...
+ *dreg = chunk as u64; // libc / ptrace is very messy wrt int types used...
}
#[cfg(target_arch = "x86")]
{
- dregs[idx] = chunk as i32; // libc / ptrace is very messy wrt int types used...
+ *dreg = chunk as i32; // libc / ptrace is very messy wrt int types used...
}
}
@@ -122,7 +116,7 @@ impl ThreadInfoX86 {
#[cfg(target_arch = "x86")]
let stack_pointer = regs.esp as usize;
- Ok(ThreadInfoX86 {
+ Ok(Self {
stack_pointer,
tgid,
ppid,
@@ -146,17 +140,20 @@ impl ThreadInfoX86 {
#[cfg(target_arch = "x86_64")]
pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- out.context_flags = MD_CONTEXT_AMD64_FULL | MD_CONTEXT_AMD64_SEGMENTS;
+ out.context_flags = crate::minidump_format::format::ContextFlagsAmd64::CONTEXT_AMD64_FULL
+ .bits()
+ | crate::minidump_format::format::ContextFlagsAmd64::CONTEXT_AMD64_SEGMENTS.bits();
- out.cs = self.regs.cs as u16; // TODO: This is u64, do we loose information by doing this?
+ // TODO: These are all u64, do we lose information by doing this?
+ out.cs = self.regs.cs as u16;
- out.ds = self.regs.ds as u16; // TODO: This is u64, do we loose information by doing this?
- out.es = self.regs.es as u16; // TODO: This is u64, do we loose information by doing this?
- out.fs = self.regs.fs as u16; // TODO: This is u64, do we loose information by doing this?
- out.gs = self.regs.gs as u16; // TODO: This is u64, do we loose information by doing this?
+ out.ds = self.regs.ds as u16;
+ out.es = self.regs.es as u16;
+ out.fs = self.regs.fs as u16;
+ out.gs = self.regs.gs as u16;
- out.ss = self.regs.ss as u16; // TODO: This is u64, do we loose information by doing this?
- out.eflags = self.regs.eflags as u32; // TODO: This is u64, do we loose information by doing this?
+ out.ss = self.regs.ss as u16;
+ out.eflags = self.regs.eflags as u32;
out.dr0 = self.dregs[0];
out.dr1 = self.dregs[1];
@@ -188,33 +185,35 @@ impl ThreadInfoX86 {
out.rip = self.regs.rip;
- out.flt_save.control_word = self.fpregs.cwd;
- out.flt_save.status_word = self.fpregs.swd;
- out.flt_save.tag_word = self.fpregs.ftw as u8; // TODO: This is u16, do we loose information by doing this?
- out.flt_save.error_opcode = self.fpregs.fop;
- out.flt_save.error_offset = self.fpregs.rip as u32; // TODO: This is u64, do we loose information by doing this?
- out.flt_save.error_selector = 0; // We don't have this.
- out.flt_save.data_offset = self.fpregs.rdp as u32; // TODO: This is u64, do we loose information by doing this?
- out.flt_save.data_selector = 0; // We don't have this.
- out.flt_save.mx_csr = self.fpregs.mxcsr;
- out.flt_save.mx_csr_mask = self.fpregs.mxcr_mask;
-
- let data = to_u128(&self.fpregs.st_space);
- for idx in 0..data.len() {
- out.flt_save.float_registers[idx] = data[idx];
- }
-
- let data = to_u128(&self.fpregs.xmm_space);
- for idx in 0..data.len() {
- out.flt_save.xmm_registers[idx] = data[idx];
+ {
+ let fs = self.fpregs;
+ let mut float_save = crate::minidump_cpu::FloatStateCPU {
+ control_word: fs.cwd,
+ status_word: fs.swd,
+ tag_word: fs.ftw as u8,
+ error_opcode: fs.fop,
+ error_offset: fs.rip as u32,
+ data_offset: fs.rdp as u32,
+ error_selector: 0, // We don't have this.
+ data_selector: 0, // We don't have this.
+ mx_csr: fs.mxcsr,
+ mx_csr_mask: fs.mxcr_mask,
+ ..Default::default()
+ };
+
+ copy_u32_registers(&mut float_save.float_registers, &fs.st_space);
+ copy_u32_registers(&mut float_save.xmm_registers, &fs.xmm_space);
+
+ use scroll::Pwrite;
+ out.float_save
+ .pwrite_with(float_save, 0, scroll::Endian::Little)
+ .expect("this is impossible");
}
- // my_memcpy(&out.flt_save.float_registers, &self.fpregs.st_space, 8 * 16);
- // my_memcpy(&out.flt_save.xmm_registers, &self.fpregs.xmm_space, 16 * 16);
}
#[cfg(target_arch = "x86")]
pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- out.context_flags = MD_CONTEXT_X86_ALL;
+ out.context_flags = crate::minidump_format::format::ContextFlagsX86::CONTEXT_X86_ALL.bits();
out.dr0 = self.dregs[0] as u32;
out.dr3 = self.dregs[3] as u32;
@@ -252,74 +251,54 @@ impl ThreadInfoX86 {
out.float_save.data_offset = self.fpregs.foo as u32;
out.float_save.data_selector = self.fpregs.fos as u32;
- // 8 registers * 10 bytes per register.
- // my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
- out.float_save.register_area = self
- .fpregs
- .st_space
- .iter()
- .map(|x| x.to_ne_bytes().to_vec())
- .flatten()
- .take(MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE)
- .collect::>()
- .as_slice()
- .try_into() // Make slice into fixed size array
- .unwrap(); // Which has to work as we know the numbers work out
-
- // This matches the Intel fpsave format.
- let mut idx = 0;
- for val in &(self.fpregs.cwd as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpregs.swd as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpregs.twd as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpxregs.fop as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpxregs.fip as u32).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpxregs.fcs as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpregs.foo as u32).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpregs.fos as u16).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
- }
- for val in &(self.fpxregs.mxcsr as u32).to_ne_bytes() {
- out.extended_registers[idx] = *val;
- idx += 1;
+ use scroll::Pwrite;
+ {
+ let ra = &mut out.float_save.register_area;
+ // 8 registers * 10 bytes per register.
+ for (idx, block) in self.fpregs.st_space.iter().enumerate() {
+ let offset = idx * std::mem::size_of::();
+ if offset >= ra.len() {
+ break;
+ }
+
+ ra.pwrite_with(block, offset, scroll::Endian::Little)
+ .expect("checked");
+ }
}
- // my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
- idx = 32;
- for val in &self.fpxregs.st_space {
- for byte in &val.to_ne_bytes() {
- out.extended_registers[idx] = *byte;
- idx += 1;
+ #[allow(unused_assignments)]
+ {
+ let mut offset = 0;
+ macro_rules! write_er {
+ ($reg:expr) => {
+ offset += out
+ .extended_registers
+ .pwrite_with($reg, offset, scroll::Endian::Little)
+ .unwrap()
+ };
}
- }
- // my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
- idx = 160;
- for val in &self.fpxregs.xmm_space {
- for byte in &val.to_ne_bytes() {
- out.extended_registers[idx] = *byte;
- idx += 1;
+ // This matches the Intel fpsave format.
+ write_er!(self.fpregs.cwd as u16);
+ write_er!(self.fpregs.swd as u16);
+ write_er!(self.fpregs.twd as u16);
+ write_er!(self.fpxregs.fop);
+ write_er!(self.fpxregs.fip);
+ write_er!(self.fpxregs.fcs);
+ write_er!(self.fpregs.foo);
+ write_er!(self.fpregs.fos);
+ write_er!(self.fpxregs.mxcsr);
+
+ offset = 32;
+
+ for val in &self.fpxregs.st_space {
+ write_er!(val);
+ }
+
+ debug_assert_eq!(offset, 160);
+
+ for val in &self.fpxregs.xmm_space {
+ write_er!(val);
}
}
}
diff --git a/src/minidump_cpu.rs b/src/minidump_cpu.rs
new file mode 100644
index 00000000..51884ebd
--- /dev/null
+++ b/src/minidump_cpu.rs
@@ -0,0 +1,20 @@
+cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86_64")] {
+ pub type RawContextCPU = minidump_common::format::CONTEXT_AMD64;
+ pub type FloatStateCPU = minidump_common::format::XMM_SAVE_AREA32;
+ } else if #[cfg(target_arch = "x86")] {
+ pub type RawContextCPU = minidump_common::format::CONTEXT_X86;
+ pub type FloatStateCPU = minidump_common::format::FLOATING_SAVE_AREA_X86;
+ } else if #[cfg(target_arch = "arm")] {
+ pub mod arm;
+ pub use arm as imp;
+ pub type RawContextCPU = arm::MDRawContextARM;
+ } else if #[cfg(target_arch = "aarch64")] {
+ pub type RawContextCPU = minidump_common::format::CONTEXT_ARM64_OLD;
+ pub type FloatStateCPU = minidump_common::format::FLOATING_SAVE_AREA_ARM64_OLD;
+ } else if #[cfg(target_arch = "mips")] {
+ compile_error!("flesh me out");
+ } else {
+ compile_error!("unsupported target architecture");
+ }
+}
diff --git a/src/minidump_cpu/minidump_cpu_arm.rs b/src/minidump_cpu/arm.rs
similarity index 100%
rename from src/minidump_cpu/minidump_cpu_arm.rs
rename to src/minidump_cpu/arm.rs
diff --git a/src/minidump_cpu/minidump_cpu_aarch64.rs b/src/minidump_cpu/minidump_cpu_aarch64.rs
deleted file mode 100644
index c35e2e3c..00000000
--- a/src/minidump_cpu/minidump_cpu_aarch64.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-pub const MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT: usize = 32;
-pub const MD_CONTEXT_ARM64_GPR_COUNT: usize = 33;
-
-/* Indices into iregs for registers with a dedicated or conventional
- * purpose.
- */
-pub enum MDARM64RegisterNumbers {
- MD_CONTEXT_ARM64_REG_FP = 29,
- MD_CONTEXT_ARM64_REG_LR = 30,
- MD_CONTEXT_ARM64_REG_SP = 31,
- MD_CONTEXT_ARM64_REG_PC = 32,
-}
diff --git a/src/minidump_cpu/minidump_cpu_amd64.rs b/src/minidump_cpu/minidump_cpu_amd64.rs
deleted file mode 100644
index 3924d792..00000000
--- a/src/minidump_cpu/minidump_cpu_amd64.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-#[repr(C)]
-pub struct MDXmmSaveArea32AMD64 {
- pub control_word: u16,
- pub status_word: u16,
- pub tag_word: u8,
- pub reserved1: u8,
- pub error_opcode: u16,
- pub error_offset: u32,
- pub error_selector: u16,
- pub reserved2: u16,
- pub data_offset: u32,
- pub data_selector: u16,
- pub reserved3: u16,
- pub mx_csr: u32,
- pub mx_csr_mask: u32,
- pub float_registers: [u128; 8],
- pub xmm_registers: [u128; 16],
- pub reserved4: [u8; 96],
-}
-
-// The std library doesn't provide "Default" for all
-// array-lengths. Only up to 32. So we have to implement
-// our own default, because of `reserved4: [u8; 96]`
-impl Default for MDXmmSaveArea32AMD64 {
- #[inline]
- fn default() -> Self {
- MDXmmSaveArea32AMD64 {
- control_word: 0,
- status_word: 0,
- tag_word: 0,
- reserved1: 0,
- error_opcode: 0,
- error_offset: 0,
- error_selector: 0,
- reserved2: 0,
- data_offset: 0,
- data_selector: 0,
- reserved3: 0,
- mx_csr: 0,
- mx_csr_mask: 0,
- float_registers: [0; 8],
- xmm_registers: [0; 16],
- reserved4: [0; 96],
- }
- }
-}
-
-const MD_CONTEXT_AMD64_VR_COUNT: usize = 26;
-
-#[repr(C)]
-#[derive(Default)]
-pub struct MDRawContextAMD64 {
- /*
- * Register parameter home addresses.
- */
- pub p1_home: u64,
- pub p2_home: u64,
- pub p3_home: u64,
- pub p4_home: u64,
- pub p5_home: u64,
- pub p6_home: u64,
-
- /* The next field determines the layout of the structure, and which parts
- * of it are populated */
- pub context_flags: u32,
- pub mx_csr: u32,
-
- /* The next register is included with MD_CONTEXT_AMD64_CONTROL */
- pub cs: u16,
-
- /* The next 4 registers are included with MD_CONTEXT_AMD64_SEGMENTS */
- pub ds: u16,
- pub es: u16,
- pub fs: u16,
- pub gs: u16,
-
- /* The next 2 registers are included with MD_CONTEXT_AMD64_CONTROL */
- pub ss: u16,
- pub eflags: u32,
-
- /* The next 6 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */
- pub dr0: u64,
- pub dr1: u64,
- pub dr2: u64,
- pub dr3: u64,
- pub dr6: u64,
- pub dr7: u64,
-
- /* The next 4 registers are included with MD_CONTEXT_AMD64_INTEGER */
- pub rax: u64,
- pub rcx: u64,
- pub rdx: u64,
- pub rbx: u64,
-
- /* The next register is included with MD_CONTEXT_AMD64_CONTROL */
- pub rsp: u64,
-
- /* The next 11 registers are included with MD_CONTEXT_AMD64_INTEGER */
- pub rbp: u64,
- pub rsi: u64,
- pub rdi: u64,
- pub r8: u64,
- pub r9: u64,
- pub r10: u64,
- pub r11: u64,
- pub r12: u64,
- pub r13: u64,
- pub r14: u64,
- pub r15: u64,
-
- /* The next register is included with MD_CONTEXT_AMD64_CONTROL */
- pub rip: u64,
-
- /* The next set of registers are included with
- * MD_CONTEXT_AMD64_FLOATING_POINT
- */
- pub flt_save: MDXmmSaveArea32AMD64,
- // union {
- // MDXmmSaveArea32AMD64 flt_save;
- // struct {
- // uint128_struct header[2];
- // uint128_struct legacy[8];
- // uint128_struct xmm0;
- // uint128_struct xmm1;
- // uint128_struct xmm2;
- // uint128_struct xmm3;
- // uint128_struct xmm4;
- // uint128_struct xmm5;
- // uint128_struct xmm6;
- // uint128_struct xmm7;
- // uint128_struct xmm8;
- // uint128_struct xmm9;
- // uint128_struct xmm10;
- // uint128_struct xmm11;
- // uint128_struct xmm12;
- // uint128_struct xmm13;
- // uint128_struct xmm14;
- // uint128_struct xmm15;
- // } sse_registers;
- // };
- pub vector_register: [u128; MD_CONTEXT_AMD64_VR_COUNT],
- pub vector_control: u64,
-
- /* The next 5 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */
- pub debug_control: u64,
- pub last_branch_to_rip: u64,
- pub last_branch_from_rip: u64,
- pub last_exception_to_rip: u64,
- pub last_exception_from_rip: u64,
-}
-
-/* For (MDRawContextAMD64).context_flags. These values indicate the type of
-* context stored in the structure. The high 24 bits identify the CPU, the
-* low 8 bits identify the type of context saved. */
-pub const MD_CONTEXT_AMD64: u32 = 0x00100000; /* CONTEXT_AMD64 */
-pub const MD_CONTEXT_AMD64_CONTROL: u32 = MD_CONTEXT_AMD64 | 0x00000001;
-/* CONTEXT_CONTROL */
-pub const MD_CONTEXT_AMD64_INTEGER: u32 = MD_CONTEXT_AMD64 | 0x00000002;
-/* CONTEXT_INTEGER */
-pub const MD_CONTEXT_AMD64_SEGMENTS: u32 = MD_CONTEXT_AMD64 | 0x00000004;
-/* CONTEXT_SEGMENTS */
-pub const MD_CONTEXT_AMD64_FLOATING_POINT: u32 = MD_CONTEXT_AMD64 | 0x00000008;
-/* CONTEXT_FLOATING_POINT */
-pub const MD_CONTEXT_AMD64_DEBUG_REGISTERS: u32 = MD_CONTEXT_AMD64 | 0x00000010;
-/* CONTEXT_DEBUG_REGISTERS */
-pub const MD_CONTEXT_AMD64_XSTATE: u32 = MD_CONTEXT_AMD64 | 0x00000040;
-/* CONTEXT_XSTATE */
-
-/* WinNT.h refers to CONTEXT_MMX_REGISTERS but doesn't appear to define it
-* I think it really means CONTEXT_FLOATING_POINT.
-*/
-
-pub const MD_CONTEXT_AMD64_FULL: u32 =
- MD_CONTEXT_AMD64_CONTROL | MD_CONTEXT_AMD64_INTEGER | MD_CONTEXT_AMD64_FLOATING_POINT;
-/* CONTEXT_FULL */
-
-pub const MD_CONTEXT_AMD64_ALL: u32 =
- MD_CONTEXT_AMD64_FULL | MD_CONTEXT_AMD64_SEGMENTS | MD_CONTEXT_AMD64_DEBUG_REGISTERS;
-/* CONTEXT_ALL */
diff --git a/src/minidump_cpu/minidump_cpu_mips.rs b/src/minidump_cpu/minidump_cpu_mips.rs
deleted file mode 100644
index 8b137891..00000000
--- a/src/minidump_cpu/minidump_cpu_mips.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/minidump_cpu/minidump_cpu_ppc.rs b/src/minidump_cpu/minidump_cpu_ppc.rs
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/minidump_cpu/minidump_cpu_ppc64.rs b/src/minidump_cpu/minidump_cpu_ppc64.rs
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/minidump_cpu/minidump_cpu_sparc.rs b/src/minidump_cpu/minidump_cpu_sparc.rs
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/minidump_cpu/minidump_cpu_x86.rs b/src/minidump_cpu/minidump_cpu_x86.rs
deleted file mode 100644
index b0b073ff..00000000
--- a/src/minidump_cpu/minidump_cpu_x86.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-pub const MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE: usize = 80;
-
-#[repr(C)]
-#[derive(Debug, PartialEq)]
-pub struct MDFloatingSaveAreaX86 {
- pub control_word: u32,
- pub status_word: u32,
- pub tag_word: u32,
- pub error_offset: u32,
- pub error_selector: u32,
- pub data_offset: u32,
- pub data_selector: u32,
-
- /* register_area contains eight 80-bit (x87 "long double") quantities for
- * floating-point registers %st0 (%mm0) through %st7 (%mm7). */
- pub register_area: [u8; MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE],
- pub cr0_npx_state: u32,
-}
-
-// The std library doesn't provide "Default" for all
-// array-lengths. Only up to 32. So we have to implement
-// our own default, because of `reserved4: [u8; 96]`
-impl Default for MDFloatingSaveAreaX86 {
- #[inline]
- fn default() -> Self {
- MDFloatingSaveAreaX86 {
- control_word: 0,
- status_word: 0,
- tag_word: 0,
- error_offset: 0,
- error_selector: 0,
- data_offset: 0,
- data_selector: 0,
- register_area: [0; MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE],
- cr0_npx_state: 0,
- }
- }
-}
-
-const MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE: usize = 512;
-/* MAXIMUM_SUPPORTED_EXTENSION */
-
-#[repr(C)]
-#[derive(Debug, PartialEq)]
-pub struct MDRawContextX86 {
- /* The next field determines the layout of the structure, and which parts
- * of it are populated */
- pub context_flags: u32,
-
- /* The next 6 registers are included with MD_CONTEXT_X86_DEBUG_REGISTERS */
- pub dr0: u32,
- pub dr1: u32,
- pub dr2: u32,
- pub dr3: u32,
- pub dr6: u32,
- pub dr7: u32,
-
- /* The next field is included with MD_CONTEXT_X86_FLOATING_POINT */
- pub float_save: MDFloatingSaveAreaX86,
-
- /* The next 4 registers are included with MD_CONTEXT_X86_SEGMENTS */
- pub gs: u32,
- pub fs: u32,
- pub es: u32,
- pub ds: u32,
- /* The next 6 registers are included with MD_CONTEXT_X86_INTEGER */
- pub edi: u32,
- pub esi: u32,
- pub ebx: u32,
- pub edx: u32,
- pub ecx: u32,
- pub eax: u32,
-
- /* The next 6 registers are included with MD_CONTEXT_X86_CONTROL */
- pub ebp: u32,
- pub eip: u32,
- pub cs: u32, /* WinNT.h says "must be sanitized" */
- pub eflags: u32, /* WinNT.h says "must be sanitized" */
- pub esp: u32,
- pub ss: u32,
-
- /* The next field is included with MD_CONTEXT_X86_EXTENDED_REGISTERS.
- * It contains vector (MMX/SSE) registers. It it laid out in the
- * format used by the fxsave and fsrstor instructions, so it includes
- * a copy of the x87 floating-point registers as well. See FXSAVE in
- * "Intel Architecture Software Developer's Manual, Volume 2." */
- pub extended_registers: [u8; MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE],
-}
-
-impl Default for MDRawContextX86 {
- #[inline]
- fn default() -> Self {
- MDRawContextX86 {
- context_flags: 0,
- dr0: 0,
- dr1: 0,
- dr2: 0,
- dr3: 0,
- dr6: 0,
- dr7: 0,
- float_save: Default::default(),
- gs: 0,
- fs: 0,
- es: 0,
- ds: 0,
- edi: 0,
- esi: 0,
- ebx: 0,
- edx: 0,
- ecx: 0,
- eax: 0,
- ebp: 0,
- eip: 0,
- cs: 0,
- eflags: 0,
- esp: 0,
- ss: 0,
- extended_registers: [0; MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE],
- }
- }
-}
-
-/* For (MDRawContextX86).context_flags. These values indicate the type of
- * context stored in the structure. The high 24 bits identify the CPU, the
- * low 8 bits identify the type of context saved. */
-pub const MD_CONTEXT_X86: u32 = 0x00010000;
-/* CONTEXT_i386, CONTEXT_i486: identifies CPU */
-pub const MD_CONTEXT_X86_CONTROL: u32 = MD_CONTEXT_X86 | 0x00000001;
-/* CONTEXT_CONTROL */
-pub const MD_CONTEXT_X86_INTEGER: u32 = MD_CONTEXT_X86 | 0x00000002;
-/* CONTEXT_INTEGER */
-pub const MD_CONTEXT_X86_SEGMENTS: u32 = MD_CONTEXT_X86 | 0x00000004;
-/* CONTEXT_SEGMENTS */
-pub const MD_CONTEXT_X86_FLOATING_POINT: u32 = MD_CONTEXT_X86 | 0x00000008;
-/* CONTEXT_FLOATING_POINT */
-pub const MD_CONTEXT_X86_DEBUG_REGISTERS: u32 = MD_CONTEXT_X86 | 0x00000010;
-/* CONTEXT_DEBUG_REGISTERS */
-pub const MD_CONTEXT_X86_EXTENDED_REGISTERS: u32 = MD_CONTEXT_X86 | 0x00000020;
-/* CONTEXT_EXTENDED_REGISTERS */
-pub const MD_CONTEXT_X86_XSTATE: u32 = MD_CONTEXT_X86 | 0x00000040;
-/* CONTEXT_XSTATE */
-
-pub const MD_CONTEXT_X86_FULL: u32 =
- MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_SEGMENTS;
-/* CONTEXT_FULL */
-
-pub const MD_CONTEXT_X86_ALL: u32 = MD_CONTEXT_X86_FULL
- | MD_CONTEXT_X86_FLOATING_POINT
- | MD_CONTEXT_X86_DEBUG_REGISTERS
- | MD_CONTEXT_X86_EXTENDED_REGISTERS;
-/* CONTEXT_ALL */
diff --git a/src/minidump_cpu/mod.rs b/src/minidump_cpu/mod.rs
deleted file mode 100644
index ebce0ad5..00000000
--- a/src/minidump_cpu/mod.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-#[cfg(target_arch = "x86_64")]
-#[path = "minidump_cpu_amd64.rs"]
-pub mod imp;
-#[cfg(target_arch = "x86")]
-#[path = "minidump_cpu_x86.rs"]
-pub mod imp;
-#[cfg(target_arch = "arm")]
-#[path = "minidump_cpu_arm.rs"]
-pub mod imp;
-#[cfg(target_arch = "aarch64")]
-#[path = "minidump_cpu_aarch64.rs"]
-pub mod imp;
-#[cfg(target_arch = "mips")]
-#[path = "minidump_cpu_mips.rs"]
-pub mod imp;
-
-#[cfg(target_arch = "x86_64")]
-pub type RawContextCPU = imp::MDRawContextAMD64;
-#[cfg(target_arch = "x86")]
-pub type RawContextCPU = imp::MDRawContextX86;
-#[cfg(target_arch = "arm")]
-pub type RawContextCPU = imp::MDRawContextARM;
-#[cfg(target_arch = "aarch64")]
-pub type RawContextCPU = imp::MDRawContextX86;
-#[cfg(target_arch = "mips")]
-pub type RawContextCPU = i32;
diff --git a/src/minidump_format.rs b/src/minidump_format.rs
index cd6910c8..8ffa40fc 100644
--- a/src/minidump_format.rs
+++ b/src/minidump_format.rs
@@ -1,437 +1,40 @@
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDGUID {
- data1: u32,
- data2: u16,
- data3: u16,
- data4: [u8; 8],
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq, Clone, Copy)]
-pub struct MDVSFixedFileInfo {
- pub signature: u32,
- pub struct_version: u32,
- pub file_version_hi: u32,
- pub file_version_lo: u32,
- pub product_version_hi: u32,
- pub product_version_lo: u32,
- pub file_flags_mask: u32, /* Identifies valid bits in fileFlags */
- pub file_flags: u32,
- pub file_os: u32,
- pub file_type: u32,
- pub file_subtype: u32,
- pub file_date_hi: u32,
- pub file_date_lo: u32,
-}
+pub use minidump_common::format::{
+ self, ArmElfHwCaps as MDCPUInformationARMElfHwCaps, PlatformId,
+ ProcessorArchitecture as MDCPUArchitecture, GUID, MINIDUMP_DIRECTORY as MDRawDirectory,
+ MINIDUMP_EXCEPTION as MDException, MINIDUMP_EXCEPTION_STREAM as MDRawExceptionStream,
+ MINIDUMP_HEADER as MDRawHeader, MINIDUMP_LOCATION_DESCRIPTOR as MDLocationDescriptor,
+ MINIDUMP_MEMORY_DESCRIPTOR as MDMemoryDescriptor, MINIDUMP_MODULE as MDRawModule,
+ MINIDUMP_SIGNATURE as MD_HEADER_SIGNATURE, MINIDUMP_STREAM_TYPE as MDStreamType,
+ MINIDUMP_SYSTEM_INFO as MDRawSystemInfo, MINIDUMP_THREAD as MDRawThread,
+ MINIDUMP_THREAD_NAME as MDRawThreadName, MINIDUMP_VERSION as MD_HEADER_VERSION,
+ VS_FIXEDFILEINFO as MDVSFixedFileInfo,
+};
/* An MDRVA is an offset into the minidump file. The beginning of the
* MDRawHeader is at offset 0. */
pub type MDRVA = u32;
-#[repr(C)]
-#[derive(Debug, Default, Clone, Copy, PartialEq)]
-pub struct MDLocationDescriptor {
- pub data_size: u32,
- pub rva: MDRVA,
-}
-
-#[repr(C)]
-#[derive(Debug, Default, Clone, PartialEq)]
-pub struct MDMemoryDescriptor {
- /* The base address of the memory range on the host that produced the
- * minidump. */
- pub start_of_memory_range: u64,
- pub memory: MDLocationDescriptor,
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawHeader {
- pub signature: u32,
- pub version: u32,
- pub stream_count: u32,
- pub stream_directory_rva: MDRVA, /* A |stream_count|-sized array of
- * MDRawDirectory structures. */
- pub checksum: u32, /* Can be 0. In fact, that's all that's
- * been found in minidump files. */
- pub time_date_stamp: u32, /* time_t */
- pub flags: u64,
-}
-
-/* For (MDRawHeader).signature and (MDRawHeader).version. Note that only the
- * low 16 bits of (MDRawHeader).version are MD_HEADER_VERSION. Per the
- * documentation, the high 16 bits are implementation-specific. */
-pub const MD_HEADER_SIGNATURE: u32 = 0x504d444d; /* 'PMDM' */
-/* MINIDUMP_SIGNATURE */
-pub const MD_HEADER_VERSION: u32 = 0x0000a793; /* 42899 */
-/* MINIDUMP_VERSION */
-
-/// The name of a thread, found in the ThreadNamesStream.
-#[repr(C, packed)]
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub struct MDRawThreadName {
- /// The id of the thread.
- pub thread_id: u32,
- /// Where the name of the thread is stored (yes, the legendary RVA64 is real!!).
- pub thread_name_rva: u64,
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawThread {
- pub thread_id: u32,
- pub suspend_count: u32,
- pub priority_class: u32,
- pub priority: u32,
- pub teb: u64, /* Thread environment block */
- pub stack: MDMemoryDescriptor,
- pub thread_context: MDLocationDescriptor, /* MDRawContext[CPU] */
-}
-
pub type MDRawThreadList = Vec;
-/* The inclusion of a 64-bit type in MINIDUMP_MODULE forces the struct to
- * be tail-padded out to a multiple of 64 bits under some ABIs (such as PPC).
- * This doesn't occur on systems that don't tail-pad in this manner. Define
- * this macro to be the usable size of the MDRawModule struct, and use it in
- * place of sizeof(MDRawModule). */
-// pub const MD_MODULE_SIZE: usize = 108;
-// NOTE: We use "packed" here instead, to size_of::() == 108
-// "packed" should be safe here, as we don't address reserved{0,1} at all
-// and padding should happen only at the tail
-#[repr(C, packed)]
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub struct MDRawModule {
- pub base_of_image: u64,
- pub size_of_image: u32,
- pub checksum: u32, /* 0 if unknown */
- pub time_date_stamp: u32, /* time_t */
- pub module_name_rva: MDRVA, /* MDString, pathname or filename */
- pub version_info: MDVSFixedFileInfo,
-
- /* The next field stores a CodeView record and is populated when a module's
- * debug information resides in a PDB file. It identifies the PDB file. */
- pub cv_record: MDLocationDescriptor,
-
- /* The next field is populated when a module's debug information resides
- * in a DBG file. It identifies the DBG file. This field is effectively
- * obsolete with modules built by recent toolchains. */
- pub misc_record: MDLocationDescriptor,
-
- /* Alignment problem: reserved0 and reserved1 are defined by the platform
- * SDK as 64-bit quantities. However, that results in a structure whose
- * alignment is unpredictable on different CPUs and ABIs. If the ABI
- * specifies full alignment of 64-bit quantities in structures (as ppc
- * does), there will be padding between miscRecord and reserved0. If
- * 64-bit quantities can be aligned on 32-bit boundaries (as on x86),
- * this padding will not exist. (Note that the structure up to this point
- * contains 1 64-bit member followed by 21 32-bit members.)
- * As a workaround, reserved0 and reserved1 are instead defined here as
- * four 32-bit quantities. This should be harmless, as there are
- * currently no known uses for these fields. */
- pub reserved0: [u32; 2],
- pub reserved1: [u32; 2],
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq, Clone)]
-pub struct MDRawDirectory {
- pub stream_type: u32,
- pub location: MDLocationDescriptor,
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDException {
- pub exception_code: u32, /* Windows: MDExceptionCodeWin,
- * Mac OS X: MDExceptionMac,
- * Linux: MDExceptionCodeLinux. */
- pub exception_flags: u32, /* Windows: 1 if noncontinuable,
- Mac OS X: MDExceptionCodeMac. */
- pub exception_record: u64, /* Address (in the minidump-producing host's
- * memory) of another MDException, for
- * nested exceptions. */
- pub exception_address: u64, /* The address that caused the exception.
- * Mac OS X: exception subcode (which is
- * typically the address). */
- pub number_parameters: u32, /* Number of valid elements in
- * exception_information. */
- pub __align: u32,
- pub exception_information: [u64; 15],
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawExceptionStream {
- pub thread_id: u32, /* Thread in which the exception
- * occurred. Corresponds to
- * (MDRawThread).thread_id. */
- pub __align: u32,
- pub exception_record: MDException,
- pub thread_context: MDLocationDescriptor, /* MDRawContext[CPU] */
-}
-
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDCPUInformation {
- pub vendor_id: [u32; 3], /* cpuid 0: ebx, edx, ecx */
- pub version_information: u32, /* cpuid 1: eax */
- pub feature_information: u32, /* cpuid 1: edx */
- pub amd_extended_cpu_features: u32, /* cpuid 0x80000001, ebx */
-}
-
-#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDCPUInformation {
- pub cpuid: u32,
- pub elf_hwcaps: u32, /* linux specific, 0 otherwise */
- _padding: [u32; 4],
-}
-
-#[cfg(target_arch = "mips")]
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDCPUInformation {
- pub cpuid: [u64; 2],
- _padding: [u32; 2],
-}
-
-/* For (MDCPUInformation).arm_cpu_info.elf_hwcaps.
- * This matches the Linux kernel definitions from */
-#[repr(u32)]
-pub enum MDCPUInformationARMElfHwCaps {
- Swp = 1 << 0,
- Half = 1 << 1,
- Thumb = 1 << 2,
- Bit26 = 1 << 3,
- FastMult = 1 << 4,
- Fpa = 1 << 5,
- Vfp = 1 << 6,
- Edsp = 1 << 7,
- Java = 1 << 8,
- Iwmmxt = 1 << 9,
- Crunch = 1 << 10,
- Thumbee = 1 << 11,
- Neon = 1 << 12,
- Vfpv3 = 1 << 13,
- Vfpv3d16 = 1 << 14,
- Tls = 1 << 15,
- Vfpv4 = 1 << 16,
- Idiva = 1 << 17,
- Idivt = 1 << 18,
-}
-
-#[repr(C)]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawSystemInfo {
- /* The next 3 fields and numberOfProcessors are from the SYSTEM_INFO
- * structure as returned by GetSystemInfo */
- pub processor_architecture: u16,
- pub processor_level: u16, /* x86: 5 = 586, 6 = 686, ... */
- /* ARM: 6 = ARMv6, 7 = ARMv7 ... */
- pub processor_revision: u16, /* x86: 0xMMSS, where MM=model,
- * SS=stepping */
- /* ARM: 0 */
- pub number_of_processors: u8,
- pub product_type: u8, /* Windows: VER_NT_* from WinNT.h */
-
- /* The next 5 fields are from the OSVERSIONINFO structure as returned
- * by GetVersionEx */
- pub major_version: u32,
- pub minor_version: u32,
- pub build_number: u32,
- pub platform_id: u32,
- pub csd_version_rva: MDRVA, /* MDString further identifying the
- * host OS.
- * Windows: name of the installed OS
- * service pack.
- * Mac OS X: the Apple OS build number
- * (sw_vers -buildVersion).
- * Linux: uname -srvmo */
-
- pub suite_mask: u16, /* Windows: VER_SUITE_* from WinNT.h */
- pub reserved2: u16,
-
- pub cpu: MDCPUInformation,
-}
-
-#[cfg(target_pointer_width = "64")]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawLinkMap {
- pub addr: u64,
- pub name: MDRVA,
- pub ld: u64,
-}
-
-#[cfg(target_pointer_width = "64")]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawDebug {
- pub version: u32,
- pub map: MDRVA, /* array of MDRawLinkMap64 */
- pub dso_count: u32,
- pub brk: u64,
- pub ldbase: u64,
- pub dynamic: u64,
-}
-
-#[cfg(target_pointer_width = "32")]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawLinkMap {
- pub addr: u32,
- pub name: MDRVA,
- pub ld: u32,
-}
-
-#[cfg(target_pointer_width = "32")]
-#[derive(Debug, Default, PartialEq)]
-pub struct MDRawDebug {
- pub version: u32,
- pub map: MDRVA, /* array of MDRawLinkMap32 */
- pub dso_count: u32,
- pub brk: u32,
- pub ldbase: u32,
- pub dynamic: u32,
-}
-
-/* For (MDRawSystemInfo).processor_architecture: */
-#[repr(u16)]
-pub enum MDCPUArchitecture {
- X86 = 0, /* PROCESSOR_ARCHITECTURE_INTEL */
- Mips = 1, /* PROCESSOR_ARCHITECTURE_MIPS */
- Alpha = 2, /* PROCESSOR_ARCHITECTURE_ALPHA */
- Ppc = 3, /* PROCESSOR_ARCHITECTURE_PPC */
- Shx = 4, /* PROCESSOR_ARCHITECTURE_SHX
- * (Super-H) */
- Arm = 5, /* PROCESSOR_ARCHITECTURE_ARM */
- Ia64 = 6, /* PROCESSOR_ARCHITECTURE_IA64 */
- Alpha64 = 7, /* PROCESSOR_ARCHITECTURE_ALPHA64 */
- Msil = 8, /* PROCESSOR_ARCHITECTURE_MSIL
- * (Microsoft Intermediate Language) */
- Amd64 = 9, /* PROCESSOR_ARCHITECTURE_AMD64 */
- X86Win64 = 10,
- /* PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 (WoW64) */
- Arm64 = 12, /* PROCESSOR_ARCHITECTURE_ARM64 */
- Sparc = 0x8001, /* Breakpad-defined value for SPARC */
- Ppc64 = 0x8002, /* Breakpad-defined value for PPC64 */
- Arm64Old = 0x8003, /* Breakpad-defined value for ARM64 */
- Mips64 = 0x8004, /* Breakpad-defined value for MIPS64 */
- Unknown = 0xffff, /* PROCESSOR_ARCHITECTURE_UNKNOWN */
-}
-
-/* For (MDRawSystemInfo).platform_id: */
-#[repr(u32)]
-pub enum MDOSPlatform {
- Win32s = 0, /* VER_PLATFORM_WIN32s (Windows 3.1) */
- Win32Windows = 1, /* VER_PLATFORM_WIN32_WINDOWS (Windows 95-98-Me) */
- Win32Nt = 2, /* VER_PLATFORM_WIN32_NT (Windows NT, 2000+) */
- Win32Ce = 3, /* VER_PLATFORM_WIN32_CE, VER_PLATFORM_WIN32_HH
- * (Windows CE, Windows Mobile, "Handheld") */
- /* The following values are Breakpad-defined. */
- Unix = 0x8000, /* Generic Unix-ish */
- MacOsX = 0x8101, /* Mac OS X/Darwin */
- Ios = 0x8102, /* iOS */
- Linux = 0x8201, /* Linux */
- Solaris = 0x8202, /* Solaris */
- Android = 0x8203, /* Android */
- Ps3 = 0x8204, /* PS3 */
- Nacl = 0x8205, /* Native Client (NaCl) */
- Fuchsia = 0x8206, /* Fuchsia */
-}
-
-/*
- * Modern ELF toolchains insert a "build id" into the ELF headers that
- * usually contains a hash of some ELF headers + sections to uniquely
- * identify a binary.
- *
- * https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Developer_Guide/compiling-build-id.html
- * https://sourceware.org/binutils/docs-2.26/ld/Options.html#index-g_t_002d_002dbuild_002did-292
- */
-pub const MD_CVINFOELF_SIGNATURE: u32 = 0x4270454c; /* cvSignature = 'BpEL' */
-/* Signature is followed by the bytes of the
- * build id from GNU_BUILD_ID ELF note.
- * This is variable-length, but usually 20 bytes
- * as the binutils ld default is a SHA-1 hash. */
-
-/* For (MDRawHeader).flags: */
-pub enum MDType {
- /* MD_NORMAL is the standard type of minidump. It includes full
- * streams for the thread list, module list, exception, system info,
- * and miscellaneous info. A memory list stream is also present,
- * pointing to the same stack memory contained in the thread list,
- * as well as a 256-byte region around the instruction address that
- * was executing when the exception occurred. Stack memory is from
- * 4 bytes below a thread's stack pointer up to the top of the
- * memory region encompassing the stack. */
- Normal = 0x00000000,
- WithDataSegs = 0x00000001,
- WithFullMemory = 0x00000002,
- WithHandleData = 0x00000004,
- FilterMemory = 0x00000008,
- ScanMemory = 0x00000010,
- WithUnloadedModules = 0x00000020,
- WithIndirectlyReferencedMemory = 0x00000040,
- FilterModulePaths = 0x00000080,
- WithProcessThreadData = 0x00000100,
- WithPrivateReadWriteMemory = 0x00000200,
- WithoutOptionalData = 0x00000400,
- WithFullMemoryInfo = 0x00000800,
- WithThreadInfo = 0x00001000,
- WithCodeSegs = 0x00002000,
- WithoutAuxilliarySegs = 0x00004000,
- WithFullAuxilliaryState = 0x00008000,
- WithPrivateWriteCopyMemory = 0x00010000,
- IgnoreInaccessibleMemory = 0x00020000,
- WithTokenInformation = 0x00040000,
-}
-
-/* For (MDRawDirectory).stream_type */
-#[repr(u32)]
-pub enum MDStreamType {
- UnusedStream = 0,
- ReservedStream0 = 1,
- ReservedStream1 = 2,
- ThreadListStream = 3, /* MDRawThreadList */
- ModuleListStream = 4, /* MDRawModuleList */
- MemoryListStream = 5, /* MDRawMemoryList */
- ExceptionStream = 6, /* MDRawExceptionStream */
- SystemInfoStream = 7, /* MDRawSystemInfo */
- ThreadExListStream = 8,
- Memory64ListStream = 9,
- CommentStreamA = 10,
- CommentStreamW = 11,
- HandleDataStream = 12,
- FunctionTableStream = 13,
- UnloadedModuleListStream = 14,
- MiscInfoStream = 15, /* MDRawMiscInfo */
- MemoryInfoListStream = 16, /* MDRawMemoryInfoList */
- ThreadInfoListStream = 17,
- HandleOperationListStream = 18,
- TokenStream = 19,
- JavascriptDataStream = 20,
- SystemMemoryInfoStream = 21,
- ProcessVmCountersStream = 22,
- IptTraceStream = 23,
- ThreadNamesStream = 24,
- LastReservedStream = 0x0000ffff,
-
- /* Breakpad extension types. 0x4767 = "Gg" */
- BreakpadInfoStream = 0x47670001, /* MDRawBreakpadInfo */
- AssertionInfoStream = 0x47670002, /* MDRawAssertionInfo */
- /* These are additional minidump stream values which are specific to
- * the linux breakpad implementation. */
- LinuxCpuInfo = 0x47670003, /* /proc/cpuinfo */
- LinuxProcStatus = 0x47670004, /* /proc/$x/status */
- LinuxLsbRelease = 0x47670005, /* /etc/lsb-release */
- LinuxCmdLine = 0x47670006, /* /proc/$x/cmdline */
- LinuxEnviron = 0x47670007, /* /proc/$x/environ */
- LinuxAuxv = 0x47670008, /* /proc/$x/auxv */
- LinuxMaps = 0x47670009, /* /proc/$x/maps */
- LinuxDsoDebug = 0x4767000A, /* MDRawDebug{32,64} */
-
- /* Crashpad extension types. 0x4350 = "CP"
- * See Crashpad's minidump/minidump_extensions.h. */
- CrashpadInfoStream = 0x43500001, /* MDRawCrashpadInfo */
+cfg_if::cfg_if! {
+ if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
+ pub use format::X86CpuInfo as MDCPUInformation;
+ } else if #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] {
+ pub use format::ARMCpuInfo as MDCPUInformation;
+ } else if #[cfg(target_arch = "mips")] {
+ pub struct MDCPUInformation {
+ pub cpuid: [u64; 2],
+ _padding: [u32; 2],
+ }
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(target_pointer_width = "64")] {
+ pub use format::LINK_MAP_64 as MDRawLinkMap;
+ pub use format::DSO_DEBUG_64 as MDRawDebug;
+ } else if #[cfg(target_pointer_width = "32")] {
+ pub use format::LINK_MAP_32 as MDRawLinkMap;
+ pub use format::DSO_DEBUG_32 as MDRawDebug;
+ }
}
diff --git a/src/sections/app_memory.rs b/src/sections/app_memory.rs
deleted file mode 100644
index cbc28ba2..00000000
--- a/src/sections/app_memory.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use crate::errors::SectionAppMemoryError;
-use crate::linux_ptrace_dumper::LinuxPtraceDumper;
-use crate::minidump_format::*;
-use crate::minidump_writer::{DumpBuf, MinidumpWriter};
-use crate::sections::MemoryArrayWriter;
-
-type Result = std::result::Result;
-
-/// Write application-provided memory regions.
-pub fn write(config: &mut MinidumpWriter, buffer: &mut DumpBuf) -> Result<()> {
- for app_memory in &config.app_memory {
- let data_copy = LinuxPtraceDumper::copy_from_process(
- config.blamed_thread,
- app_memory.ptr as *mut libc::c_void,
- app_memory.length,
- )?;
-
- let section = MemoryArrayWriter::::alloc_from_array(buffer, &data_copy)?;
- let desc = MDMemoryDescriptor {
- start_of_memory_range: app_memory.ptr as u64,
- memory: section.location(),
- };
- config.memory_blocks.push(desc);
- }
- Ok(())
-}
diff --git a/src/sections/mod.rs b/src/sections/mod.rs
deleted file mode 100644
index 086e7e23..00000000
--- a/src/sections/mod.rs
+++ /dev/null
@@ -1,194 +0,0 @@
-pub mod app_memory;
-pub mod exception_stream;
-pub mod mappings;
-pub mod memory_list_stream;
-pub mod systeminfo_stream;
-pub mod thread_list_stream;
-pub mod thread_names_stream;
-
-use crate::errors::MemoryWriterError;
-use crate::minidump_format::*;
-use std::convert::TryInto;
-use std::io::{Cursor, Write};
-
-type Result = std::result::Result;
-
-#[derive(Debug, PartialEq)]
-pub struct MemoryWriter {
- pub position: MDRVA,
- pub size: usize,
- phantom: std::marker::PhantomData,
-}
-
-impl MemoryWriter
-where
- T: Default + Sized,
-{
- /// Create a slot for a type T in the buffer, we can fill right now with real values.
- pub fn alloc_with_val(buffer: &mut Cursor>, val: T) -> Result {
- // Get position of this value (e.g. before we add ourselves there)
- let position = buffer.position();
- let size = std::mem::size_of::();
- let bytes = unsafe { std::slice::from_raw_parts(&val as *const T as *const u8, size) };
- buffer.write_all(bytes)?;
-
- Ok(MemoryWriter {
- position: position as u32,
- size,
- phantom: std::marker::PhantomData:: {},
- })
- }
-
- /// Create a slot for a type T in the buffer, we can fill later with real values.
- /// This function fills it with `Default::default()`, which is less performant than
- /// using uninitialized memory, but safe.
- pub fn alloc(buffer: &mut Cursor>) -> Result {
- // Filling out the buffer with default-values
- let val: T = Default::default();
- Self::alloc_with_val(buffer, val)
- }
-
- /// Write actual values in the buffer-slot we got during `alloc()`
- pub fn set_value(&mut self, buffer: &mut Cursor>, val: T) -> Result<()> {
- // Save whereever the current cursor stands in the buffer
- let curr_pos = buffer.position();
-
- // Write the actual value we want at our position that
- // was determined by `alloc()` into the buffer
- buffer.set_position(self.position as u64);
- let bytes = unsafe {
- std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::())
- };
- let res = buffer.write_all(bytes);
-
- // Resetting whereever we were before updating this
- // regardless of the write-result
- buffer.set_position(curr_pos);
-
- res?;
- Ok(())
- }
-
- pub fn location(&self) -> MDLocationDescriptor {
- MDLocationDescriptor {
- data_size: std::mem::size_of::() as u32,
- rva: self.position,
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct MemoryArrayWriter {
- pub position: MDRVA,
- array_size: usize,
- phantom: std::marker::PhantomData,
-}
-
-impl MemoryArrayWriter
-where
- T: Default + Sized,
-{
- /// Create a slot for a type T in the buffer, we can fill in the values in one go.
- pub fn alloc_from_array(buffer: &mut Cursor>, array: &[T]) -> Result {
- // Get position of this value (e.g. before we add ourselves there)
- let position = buffer.position();
- for val in array {
- let bytes = unsafe {
- std::slice::from_raw_parts(val as *const T as *const u8, std::mem::size_of::())
- };
- buffer.write_all(bytes)?;
- }
-
- Ok(MemoryArrayWriter {
- position: position as u32,
- array_size: array.len(),
- phantom: std::marker::PhantomData:: {},
- })
- }
-
- /// Create a slot for a type T in the buffer, we can fill later with real values.
- /// This function fills it with `Default::default()`, which is less performant than
- /// using uninitialized memory, but safe.
- pub fn alloc_array(buffer: &mut Cursor>, array_size: usize) -> Result {
- // Get position of this value (e.g. before we add ourselves there)
- let position = buffer.position();
- for _ in 0..array_size {
- // Filling out the buffer with default-values
- let val: T = Default::default();
- let bytes = unsafe {
- std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::())
- };
- buffer.write_all(bytes)?;
- }
-
- Ok(MemoryArrayWriter {
- position: position as u32,
- array_size,
- phantom: std::marker::PhantomData:: {},
- })
- }
-
- /// Write actual values in the buffer-slot we got during `alloc()`
- pub fn set_value_at(
- &mut self,
- buffer: &mut Cursor>,
- val: T,
- index: usize,
- ) -> Result<()> {
- // Save whereever the current cursor stands in the buffer
- let curr_pos = buffer.position();
-
- // Write the actual value we want at our position that
- // was determined by `alloc()` into the buffer
- buffer.set_position(self.position as u64 + (std::mem::size_of::() * index) as u64);
- let bytes = unsafe {
- std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::())
- };
- let res = buffer.write_all(bytes);
-
- // Resetting whereever we were before updating this
- // regardless of the write-result
- buffer.set_position(curr_pos);
-
- res?;
- Ok(())
- }
-
- pub fn location(&self) -> MDLocationDescriptor {
- MDLocationDescriptor {
- data_size: (self.array_size * std::mem::size_of::()) as u32,
- rva: self.position,
- }
- }
-
- pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor {
- MDLocationDescriptor {
- data_size: std::mem::size_of::() as u32,
- rva: self.position + (std::mem::size_of::() * idx) as u32,
- }
- }
-}
-
-pub fn write_string_to_location(
- buffer: &mut Cursor>,
- text: &str,
-) -> Result {
- let letters: Vec = text.encode_utf16().collect();
-
- // First write size of the string (x letters in u16, times the size of u16)
- let text_header = MemoryWriter::::alloc_with_val(
- buffer,
- (letters.len() * std::mem::size_of::()).try_into()?,
- )?;
-
- // Then write utf-16 letters after that
- let mut text_section = MemoryArrayWriter::::alloc_array(buffer, letters.len())?;
- for (index, letter) in letters.iter().enumerate() {
- text_section.set_value_at(buffer, *letter, index)?;
- }
-
- let mut location = text_header.location();
- location.data_size += text_section.location().data_size;
-
- Ok(location)
-}
diff --git a/src/sections/systeminfo_stream.rs b/src/sections/systeminfo_stream.rs
deleted file mode 100644
index dabd7793..00000000
--- a/src/sections/systeminfo_stream.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use crate::dumper_cpu_info::{write_cpu_information, write_os_information};
-use crate::errors::SectionSystemInfoError;
-use crate::minidump_format::*;
-use crate::minidump_writer::DumpBuf;
-use crate::sections::MemoryWriter;
-
-type Result = std::result::Result;
-
-pub fn write(buffer: &mut DumpBuf) -> Result {
- let mut info_section = MemoryWriter::::alloc(buffer)?;
- let dirent = MDRawDirectory {
- stream_type: MDStreamType::SystemInfoStream as u32,
- location: info_section.location(),
- };
- let mut info: MDRawSystemInfo = Default::default();
- write_cpu_information(&mut info)?;
- write_os_information(buffer, &mut info)?;
-
- info_section.set_value(buffer, info)?;
- Ok(dirent)
-}
diff --git a/src/thread_info/thread_info_aarch64.rs b/src/thread_info/thread_info_aarch64.rs
deleted file mode 100644
index cca218f4..00000000
--- a/src/thread_info/thread_info_aarch64.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use super::Pid;
-use crate::errors::ThreadInfoError;
-use crate::minidump_cpu::imp::{MDARM64RegisterNumbers, MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT};
-use libc;
-
-type Result = std::result::Result;
-
-#[cfg(target_arch = "aarch64")]
-#[derive(Debug)]
-pub struct ThreadInfoAarch64 {
- pub stack_pointer: libc::c_ulonglong,
- pub tgid: Pid, // thread group id
- pub ppid: Pid, // parent process
- pub regs: libc::user_regs_struct,
- pub fpregs: libc::user_fpsimd_struct,
-}
-
-impl ThreadInfoAarch64 {
- pub fn get_instruction_pointer(&self) -> libc::c_ulonglong {
- self.regs.pc
- }
-
- pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- // out->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
- out.cpsr = self.regs.pstate as u32;
- for idx in 0..MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP {
- out.iregs[idx] = self.regs.regs[idx];
- }
- out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP] = self.regs.sp;
- out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_PC] = self.regs.pc;
- out.float_save.fpsr = self.fpregs.fpsr;
- out.float_save.fpcr = self.fpregs.fpcr;
-
- // my_memcpy(&out->float_save.regs, &fpregs.vregs,
- // MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
- out.float_save.regs = self
- .fpregs
- .vregs
- .iter()
- .map(|x| x.to_ne_bytes().to_vec())
- .flatten()
- .take(MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16)
- .collect::>()
- .as_slice()
- .try_into() // Make slice into fixed size array
- .unwrap(); // Which has to work as we know the numbers work out
- }
-}
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index 3775bcc3..bb262601 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -86,7 +86,7 @@ pub fn start_child_and_return(command: &str) -> Child {
.arg("--bin")
.arg("test")
.arg("--")
- .arg(format!("{}", command))
+ .arg(command)
.stdout(Stdio::piped())
.spawn()
.expect("failed to execute child");
diff --git a/tests/minidump_writer.rs b/tests/minidump_writer.rs
index 95c26fb3..4f4d907b 100644
--- a/tests/minidump_writer.rs
+++ b/tests/minidump_writer.rs
@@ -1,22 +1,21 @@
+use crash_context::CrashContext;
use minidump::*;
use minidump_common::format::{GUID, MINIDUMP_STREAM_TYPE::*};
-use minidump_writer_linux::app_memory::AppMemory;
-#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
-use minidump_writer_linux::crash_context::fpstate_t;
-use minidump_writer_linux::crash_context::CrashContext;
-use minidump_writer_linux::errors::*;
-use minidump_writer_linux::linux_ptrace_dumper::LinuxPtraceDumper;
-use minidump_writer_linux::maps_reader::{MappingEntry, MappingInfo, SystemMappingInfo};
-use minidump_writer_linux::minidump_writer::MinidumpWriter;
-use minidump_writer_linux::thread_info::Pid;
-use nix::errno::Errno;
-use nix::sys::signal::Signal;
-use std::collections::HashSet;
-use std::convert::TryInto;
-use std::io::{BufRead, BufReader};
-use std::os::unix::process::ExitStatusExt;
-use std::process::{Command, Stdio};
-use std::str::FromStr;
+use minidump_writer::{
+ app_memory::AppMemory,
+ errors::*,
+ maps_reader::{MappingEntry, MappingInfo, SystemMappingInfo},
+ minidump_writer::MinidumpWriter,
+ ptrace_dumper::PtraceDumper,
+ thread_info::Pid,
+};
+use nix::{errno::Errno, sys::signal::Signal};
+use std::{
+ collections::HashSet,
+ io::{BufRead, BufReader},
+ os::unix::process::ExitStatusExt,
+ process::{Command, Stdio},
+};
mod common;
use common::*;
@@ -28,18 +27,21 @@ enum Context {
}
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
-fn get_ucontext() -> Result {
- let mut context = std::mem::MaybeUninit::::uninit();
- let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
- Errno::result(res)?;
- unsafe { Ok(context.assume_init()) }
+fn get_ucontext() -> Result {
+ let mut context = std::mem::MaybeUninit::uninit();
+ unsafe {
+ let res = crash_context::crash_context_getcontext(context.as_mut_ptr());
+ Errno::result(res)?;
+
+ Ok(context.assume_init())
+ }
}
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
fn get_crash_context(tid: Pid) -> CrashContext {
- let siginfo: libc::siginfo_t = unsafe { std::mem::zeroed() };
+ let siginfo = unsafe { std::mem::zeroed() };
let context = get_ucontext().expect("Failed to get ucontext");
- let float_state: fpstate_t = unsafe { std::mem::zeroed() };
+ let float_state = unsafe { std::mem::zeroed() };
CrashContext {
siginfo,
tid,
@@ -84,6 +86,7 @@ fn test_write_dump_helper(context: Context) {
fn test_write_dump() {
test_write_dump_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_write_dump_with_context() {
@@ -105,8 +108,16 @@ fn test_write_and_read_dump_from_parent_helper(context: Context) {
.read_line(&mut buf)
.expect("Couldn't read address provided by child");
let mut output = buf.split_whitespace();
- let mmap_addr = usize::from_str(output.next().unwrap()).expect("unable to parse mmap_addr");
- let memory_size = usize::from_str(output.next().unwrap()).expect("unable to parse memory_size");
+ let mmap_addr: usize = output
+ .next()
+ .unwrap()
+ .parse()
+ .expect("unable to parse mmap_addr");
+ let memory_size: usize = output
+ .next()
+ .unwrap()
+ .parse()
+ .expect("unable to parse memory_size");
// Add information about the mapped memory.
let mapping = MappingInfo {
start_address: mmap_addr,
@@ -159,7 +170,7 @@ fn test_write_and_read_dump_from_parent_helper(context: Context) {
assert_eq!(module.code_file(), "a fake mapping");
assert_eq!(
module.debug_identifier(),
- Some("33221100554477668899AABBCCDDEEFF0".into())
+ Some("33221100554477668899AABBCCDDEEFF0".parse().unwrap())
);
let _: MinidumpException = dump.get_stream().expect("Couldn't find MinidumpException");
@@ -189,10 +200,12 @@ fn test_write_and_read_dump_from_parent_helper(context: Context) {
.get_raw_stream(LinuxDsoDebug)
.expect("Couldn't find LinuxDsoDebug");
}
+
#[test]
fn test_write_and_read_dump_from_parent() {
test_write_and_read_dump_from_parent_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_write_and_read_dump_from_parent_with_context() {
@@ -216,7 +229,11 @@ fn test_write_with_additional_memory_helper(context: Context) {
let mut output = buf.split_whitespace();
let memory_addr = usize::from_str_radix(output.next().unwrap().trim_start_matches("0x"), 16)
.expect("unable to parse mmap_addr");
- let memory_size = usize::from_str(output.next().unwrap()).expect("unable to parse memory_size");
+ let memory_size: usize = output
+ .next()
+ .unwrap()
+ .parse()
+ .expect("unable to parse memory_size");
let app_memory = AppMemory {
ptr: memory_addr,
@@ -260,10 +277,12 @@ fn test_write_with_additional_memory_helper(context: Context) {
// Verify memory contents.
assert_eq!(region.bytes, values);
}
+
#[test]
fn test_write_with_additional_memory() {
test_write_with_additional_memory_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_write_with_additional_memory_with_context() {
@@ -429,7 +448,7 @@ fn test_with_deleted_binary() {
let pid = child.id() as i32;
- let build_id = LinuxPtraceDumper::elf_file_identifier_from_mapped_file(&mem_slice)
+ let build_id = PtraceDumper::elf_file_identifier_from_mapped_file(&mem_slice)
.expect("Failed to get build_id");
let guid = GUID {
@@ -492,7 +511,7 @@ fn test_with_deleted_binary() {
assert_eq!(main_module.code_file(), binary_copy.to_string_lossy());
assert_eq!(
main_module.debug_identifier(),
- Some(std::borrow::Cow::from(filtered.as_str()))
+ Some(filtered.parse().unwrap())
);
}
@@ -536,10 +555,12 @@ fn test_skip_if_requested_helper(context: Context) {
assert!(res.is_err());
}
+
#[test]
fn test_skip_if_requested() {
test_skip_if_requested_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_skip_if_requested_with_context() {
@@ -594,16 +615,15 @@ fn test_sanitized_stacks_helper(context: Context) {
let start = mem.rva as usize;
let end = (mem.rva + mem.data_size) as usize;
let slice = &dump_array.as_slice()[start..end];
- assert!(slice
- .windows(defaced.len())
- .position(|window| window == defaced)
- .is_some());
+ assert!(slice.windows(defaced.len()).any(|window| window == defaced));
}
}
+
#[test]
fn test_sanitized_stacks() {
test_sanitized_stacks_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_sanitized_stacks_with_context() {
@@ -630,7 +650,11 @@ fn test_write_early_abort_helper(context: Context) {
let _ = usize::from_str_radix(output.next().unwrap().trim_start_matches("0x"), 16)
.expect("unable to parse mmap_addr");
let memory_addr = 0;
- let memory_size = usize::from_str(output.next().unwrap()).expect("unable to parse memory_size");
+ let memory_size: usize = output
+ .next()
+ .unwrap()
+ .parse()
+ .expect("unable to parse memory_size");
let app_memory = AppMemory {
ptr: memory_addr,
@@ -666,10 +690,12 @@ fn test_write_early_abort_helper(context: Context) {
// Should be missing:
assert!(dump.get_stream::().is_err());
}
+
#[test]
fn test_write_early_abort() {
test_write_early_abort_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_write_early_abort_with_context() {
@@ -723,10 +749,12 @@ fn test_named_threads_helper(context: Context) {
}
assert_eq!(expected, names);
}
+
#[test]
fn test_named_threads() {
test_named_threads_helper(Context::Without)
}
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_named_threads_with_context() {
diff --git a/tests/ptrace_dumper.rs b/tests/ptrace_dumper.rs
index c2e8fccf..8d6fb648 100644
--- a/tests/ptrace_dumper.rs
+++ b/tests/ptrace_dumper.rs
@@ -1,7 +1,6 @@
-use minidump_writer_linux::linux_ptrace_dumper;
+use minidump_writer::ptrace_dumper;
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
use nix::sys::signal::Signal;
-use std::convert::TryInto;
use std::io::{BufRead, BufReader};
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
@@ -26,8 +25,7 @@ fn test_thread_list_from_parent() {
let num_of_threads = 5;
let mut child = start_child_and_wait_for_threads(num_of_threads);
let pid = child.id() as i32;
- let mut dumper =
- linux_ptrace_dumper::LinuxPtraceDumper::new(pid).expect("Couldn't init dumper");
+ let mut dumper = ptrace_dumper::PtraceDumper::new(pid).expect("Couldn't init dumper");
assert_eq!(dumper.threads.len(), num_of_threads);
dumper.suspend_threads().expect("Could not suspend threads");
@@ -196,8 +194,7 @@ fn test_sanitize_stack_copy() {
let heap_addr = usize::from_str_radix(output.next().unwrap().trim_start_matches("0x"), 16)
.expect("unable to parse mmap_addr");
- let mut dumper =
- linux_ptrace_dumper::LinuxPtraceDumper::new(pid).expect("Couldn't init dumper");
+ let mut dumper = ptrace_dumper::PtraceDumper::new(pid).expect("Couldn't init dumper");
assert_eq!(dumper.threads.len(), num_of_threads);
dumper.suspend_threads().expect("Could not suspend threads");
let thread_info = dumper