From 40dcabdd6093d9853754a7c32014b80aac81ac75 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 14 Mar 2022 20:49:48 +0100 Subject: [PATCH 1/4] Refactor to use minidump types --- Cargo.toml | 9 +- src/linux/crash_context/x86_64.rs | 120 +++--- src/linux/dso_debug.rs | 2 +- src/linux/dumper_cpu_info.rs | 38 +- src/linux/dumper_cpu_info/x86_mips.rs | 28 +- src/linux/errors.rs | 2 + src/linux/minidump_writer.rs | 18 +- src/linux/ptrace_dumper.rs | 8 +- src/linux/sections.rs | 238 ++++++++---- src/linux/sections/app_memory.rs | 2 +- src/linux/sections/mappings.rs | 4 +- src/linux/sections/systeminfo_stream.rs | 11 +- src/linux/sections/thread_list_stream.rs | 9 +- src/linux/thread_info.rs | 41 +- src/linux/thread_info/x86.rs | 169 ++++----- src/minidump_cpu.rs | 25 +- src/minidump_cpu/aarch64.rs | 12 - src/minidump_cpu/amd64.rs | 179 --------- src/minidump_cpu/x86.rs | 151 -------- src/minidump_format.rs | 461 ++--------------------- tests/minidump_writer.rs | 7 +- 21 files changed, 434 insertions(+), 1100 deletions(-) delete mode 100644 src/minidump_cpu/aarch64.rs delete mode 100644 src/minidump_cpu/amd64.rs delete mode 100644 src/minidump_cpu/x86.rs diff --git a/Cargo.toml b/Cargo.toml index eab595c2..2ea8b109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ license = "MIT" byteorder = "1.3.2" cfg-if = "1.0" memoffset = "0.5.1" +minidump-common = "0.10" +scroll = "0.11" tempfile = "3.1.0" thiserror = "1.0.21" @@ -21,5 +23,8 @@ memmap2 = "0.2.2" nix = "0.23" [dev-dependencies] -minidump = { git = "https://github.com/luser/rust-minidump", rev = "9652b3b" } -minidump-common = { git = "https://github.com/luser/rust-minidump", rev = "9652b3b" } +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/linux/crash_context/x86_64.rs b/src/linux/crash_context/x86_64.rs index dd82d29a..fa1b4ca9 100644 --- a/src/linux/crash_context/x86_64.rs +++ b/src/linux/crash_context/x86_64.rs @@ -1,7 +1,6 @@ use super::CrashContext; -use crate::minidump_cpu::imp::*; use crate::minidump_cpu::RawContextCPU; -use crate::thread_info::to_u128; +use crate::minidump_format::format; 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, @@ -17,53 +16,80 @@ impl CrashContext { } 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]; + out.context_flags = format::ContextFlagsAmd64::CONTEXT_AMD64_FULL.bits(); + + { + let gregs = &self.context.uc_mcontext.gregs; + out.cs = (gregs[REG_CSGSFS as usize] & 0xffff) as u16; + + out.fs = ((gregs[REG_CSGSFS as usize] >> 32) & 0xffff) as u16; + out.gs = ((gregs[REG_CSGSFS as usize] >> 16) & 0xffff) as u16; + + out.eflags = gregs[REG_EFL as usize] as u32; + + out.rax = gregs[REG_RAX as usize] as u64; + out.rcx = gregs[REG_RCX as usize] as u64; + out.rdx = gregs[REG_RDX as usize] as u64; + out.rbx = gregs[REG_RBX as usize] as u64; + + out.rsp = gregs[REG_RSP as usize] as u64; + out.rbp = gregs[REG_RBP as usize] as u64; + out.rsi = gregs[REG_RSI as usize] as u64; + out.rdi = gregs[REG_RDI as usize] as u64; + out.r8 = gregs[REG_R8 as usize] as u64; + out.r9 = gregs[REG_R9 as usize] as u64; + out.r10 = gregs[REG_R10 as usize] as u64; + out.r11 = gregs[REG_R11 as usize] as u64; + out.r12 = gregs[REG_R12 as usize] as u64; + out.r13 = gregs[REG_R13 as usize] as u64; + out.r14 = gregs[REG_R14 as usize] as u64; + out.r15 = gregs[REG_R15 as usize] as u64; + + out.rip = gregs[REG_RIP as usize] as u64; } - let data = to_u128(&self.float_state.xmm_space); - for idx in 0..data.len() { - out.flt_save.xmm_registers[idx] = data[idx]; + { + let fs = &self.float_state; + + let mut float_save = format::XMM_SAVE_AREA32 { + 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() + }; + + #[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)); + } + + 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"); } } } diff --git a/src/linux/dso_debug.rs b/src/linux/dso_debug.rs index d2e0459d..da00b68b 100644 --- a/src/linux/dso_debug.rs +++ b/src/linux/dso_debug.rs @@ -271,7 +271,7 @@ pub fn write_dso_debug_stream( 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 index 6577bf07..bbe22e7c 100644 --- a/src/linux/dumper_cpu_info.rs +++ b/src/linux/dumper_cpu_info.rs @@ -20,34 +20,26 @@ cfg_if::cfg_if! { pub use imp::write_cpu_information; -use crate::errors::MemoryWriterError; -use crate::minidump_format::{MDOSPlatform, MDRawSystemInfo}; -use crate::sections::write_string_to_location; +use crate::minidump_format::PlatformId; 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<()> { +/// Retrieves the [`MDOSPlatform`] and synthesized version information +pub fn os_information() -> (PlatformId, String) { 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![ + let vers = format!( + "{} {} {} {}", info.sysname(), info.release(), info.version(), - info.machine(), - ] - .join(" "); - - let location = write_string_to_location(buffer, &merged)?; - sys_info.csd_version_rva = location.rva; + info.machine() + ); - Ok(()) + ( + if cfg!(target_os = "android") { + PlatformId::Android + } else { + PlatformId::Linux + }, + vers, + ) } diff --git a/src/linux/dumper_cpu_info/x86_mips.rs b/src/linux/dumper_cpu_info/x86_mips.rs index c37fa94f..cd36dbc6 100644 --- a/src/linux/dumper_cpu_info/x86_mips.rs +++ b/src/linux/dumper_cpu_info/x86_mips.rs @@ -1,6 +1,5 @@ use crate::errors::CpuInfoError; use crate::minidump_format::*; -use std::convert::TryInto; use std::io::{BufRead, BufReader}; use std::path; @@ -35,15 +34,15 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> { ]; // processor_architecture should always be set, do this first - if cfg!(target_arch = "mips") { - sys_info.processor_architecture = MDCPUArchitecture::Mips as u16; + sys_info.processor_architecture = if cfg!(target_arch = "mips") { + MDCPUArchitecture::PROCESSOR_ARCHITECTURE_MIPS } else if cfg!(target_arch = "mips64") { - sys_info.processor_architecture = MDCPUArchitecture::Mips64 as u16; + MDCPUArchitecture::PROCESSOR_ARCHITECTURE_MIPS64 } else if cfg!(target_arch = "x86") { - sys_info.processor_architecture = MDCPUArchitecture::X86 as u16; + MDCPUArchitecture::PROCESSOR_ARCHITECTURE_INTEL } else { - sys_info.processor_architecture = MDCPUArchitecture::Amd64 as u16; - } + MDCPUArchitecture::PROCESSOR_ARCHITECTURE_AMD64 + } as u16; let cpuinfo_file = std::fs::File::open(path::PathBuf::from("/proc/cpuinfo"))?; @@ -106,17 +105,10 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> { (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; - } - }; - } + let vendor_id = vendor_id.as_bytes(); + // The vendor_id is the first 12 (3 * size_of::()) bytes + let vendor_len = std::cmp::min(3 * std::mem::size_of::(), vendor_id.len()); + sys_info.cpu.data[..vendor_len].copy_from_slice(&vendor_id[..vendor_len]); } Ok(()) diff --git a/src/linux/errors.rs b/src/linux/errors.rs index c6a3505a..ca9d83f4 100644 --- a/src/linux/errors.rs +++ b/src/linux/errors.rs @@ -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/linux/minidump_writer.rs b/src/linux/minidump_writer.rs index 622181fb..2ec12a0a 100644 --- a/src/linux/minidump_writer.rs +++ b/src/linux/minidump_writer.rs @@ -11,9 +11,9 @@ use crate::{ }, minidump_format::*, }; -use std::io::{Cursor, Read, Seek, SeekFrom, Write}; +use std::io::{Seek, SeekFrom, Write}; -pub type DumpBuf = Cursor>; +pub type DumpBuf = Buffer; #[derive(Debug)] pub struct DirSection<'a, W> @@ -71,7 +71,7 @@ where ))?; 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 @@ -93,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(()) } @@ -203,14 +203,14 @@ 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: &PtraceDumper) -> bool { @@ -418,11 +418,9 @@ impl MinidumpWriter { 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 index 50efd493..987e94de 100644 --- a/src/linux/ptrace_dumper.rs +++ b/src/linux/ptrace_dumper.rs @@ -8,7 +8,7 @@ use crate::{ thread_info::{Pid, ThreadInfo}, LINUX_GATE_LIBRARY_NAME, }, - minidump_format::MDGUID, + minidump_format::GUID, }; use goblin::elf; use nix::{ @@ -511,16 +511,16 @@ impl PtraceDumper { // 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 result = vec![0u8; std::mem::size_of::()]; let mut offset = 0; while offset < max_len { - for idx in 0..std::mem::size_of::() { + 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::(); + offset += std::mem::size_of::(); } return Ok(result); } diff --git a/src/linux/sections.rs b/src/linux/sections.rs index d4e0b26d..b7850ac0 100644 --- a/src/linux/sections.rs +++ b/src/linux/sections.rs @@ -14,14 +14,87 @@ use crate::{ }, minidump_format::*, }; -use std::convert::TryInto; -use std::io::Write; +use scroll::ctx::{SizeWith, TryIntoCtx}; -pub type Buffer = std::io::Cursor>; 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 struct MemoryWriter { pub position: MDRVA, pub size: usize, phantom: std::marker::PhantomData, @@ -29,87 +102,114 @@ pub struct MemoryWriter { impl MemoryWriter where - T: Default + Sized, + 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 { - // Get position of this value (e.g. before we add ourselves there) + // Mark the position as we may overwrite later 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)?; + let size = buffer.write(val)?; - Ok(MemoryWriter { + Ok(Self { position: position as u32, size, - phantom: std::marker::PhantomData:: {}, + 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 Buffer) -> WriteResult { - // Filling out the buffer with default-values - let val: T = Default::default(); - Self::alloc_with_val(buffer, val) + 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<()> { - // 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(()) + Ok(buffer.write_at(self.position as usize, val).map(|_sz| ())?) } + #[inline] pub fn location(&self) -> MDLocationDescriptor { MDLocationDescriptor { - data_size: std::mem::size_of::() as u32, + data_size: size!(T) as u32, rva: self.position, } } } #[derive(Debug, PartialEq)] -pub struct MemoryArrayWriter { +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: Default + Sized, + T: TryIntoCtx + SizeWith + Copy, { - /// 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 Buffer, array: &[T]) -> WriteResult { - // 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)?; + 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(MemoryArrayWriter { + Ok(Self { position: position as u32, - array_size: array.len(), - phantom: std::marker::PhantomData:: {}, + array_size, + phantom: std::marker::PhantomData, }) } @@ -117,56 +217,36 @@ where /// 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 { - // 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)?; - } + let position = buffer.reserve(array_size * size!(T)); - Ok(MemoryArrayWriter { + Ok(Self { position: position as u32, array_size, - phantom: std::marker::PhantomData:: {}, + 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<()> { - // 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(()) + 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 * std::mem::size_of::()) as u32, + 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: std::mem::size_of::() as u32, - rva: self.position + (std::mem::size_of::() * idx) as u32, + data_size: size!(T) as u32, + rva: self.position + (size!(T) * idx) as u32, } } } diff --git a/src/linux/sections/app_memory.rs b/src/linux/sections/app_memory.rs index d2ae5a3c..6d4a2e90 100644 --- a/src/linux/sections/app_memory.rs +++ b/src/linux/sections/app_memory.rs @@ -12,7 +12,7 @@ pub fn write( app_memory.length, )?; - let section = MemoryArrayWriter::::alloc_from_array(buffer, &data_copy)?; + let section = MemoryArrayWriter::write_bytes(buffer, &data_copy); let desc = MDMemoryDescriptor { start_of_memory_range: app_memory.ptr as u64, memory: section.location(), diff --git a/src/linux/sections/mappings.rs b/src/linux/sections/mappings.rs index 1cc9b886..e2e96a9a 100644 --- a/src/linux/sections/mappings.rs +++ b/src/linux/sections/mappings.rs @@ -52,7 +52,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; } @@ -69,7 +69,7 @@ fn fill_raw_module( // Just zeroes cv_record = Default::default(); } else { - let cv_signature = MD_CVINFOELF_SIGNATURE; + let cv_signature = crate::minidump_format::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)?; diff --git a/src/linux/sections/systeminfo_stream.rs b/src/linux/sections/systeminfo_stream.rs index fd7aa576..a298c00d 100644 --- a/src/linux/sections/systeminfo_stream.rs +++ b/src/linux/sections/systeminfo_stream.rs @@ -7,9 +7,16 @@ pub fn write(buffer: &mut DumpBuf) -> Result() }; + info.platform_id = platform_id as u32; + info.csd_version_rva = os_version_loc.rva; + dci::write_cpu_information(&mut info)?; - dci::write_os_information(buffer, &mut info)?; info_section.set_value(buffer, info)?; Ok(dirent) diff --git a/src/linux/sections/thread_list_stream.rs b/src/linux/sections/thread_list_stream.rs index fc3bfc0a..ae333eed 100644 --- a/src/linux/sections/thread_list_stream.rs +++ b/src/linux/sections/thread_list_stream.rs @@ -60,7 +60,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 @@ -224,7 +229,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/linux/thread_info.rs b/src/linux/thread_info.rs index e5e3ed23..0943267a 100644 --- a/src/linux/thread_info.rs +++ b/src/linux/thread_info.rs @@ -1,10 +1,9 @@ use crate::errors::ThreadInfoError; -use nix::errno::Errno; -use nix::sys::ptrace; -use nix::unistd; -use std::convert::TryInto; -use std::io::{self, BufRead}; -use std::path; +use nix::{errno::Errno, sys::ptrace, unistd}; +use std::{ + io::{self, BufRead}, + path, +}; type Result = std::result::Result; @@ -37,22 +36,20 @@ enum NT_Elf { //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 { diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index f5c8532f..864d417d 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -1,15 +1,8 @@ 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::{errors::ThreadInfoError, minidump_cpu::RawContextCPU}; use core::mem::size_of_val; use libc::user; -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; @@ -120,7 +113,7 @@ impl ThreadInfoX86 { #[cfg(target_arch = "x86")] let stack_pointer = regs.esp as usize; - Ok(ThreadInfoX86 { + Ok(Self { stack_pointer, tgid, ppid, @@ -144,7 +137,9 @@ 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? @@ -186,28 +181,30 @@ 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() + }; + + super::copy_u32_registers(&mut float_save.float_registers, &fs.st_space); + super::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")] @@ -250,74 +247,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 index 8c314426..51884ebd 100644 --- a/src/minidump_cpu.rs +++ b/src/minidump_cpu.rs @@ -1,23 +1,18 @@ cfg_if::cfg_if! { if #[cfg(target_arch = "x86_64")] { - pub(crate) mod amd64; - pub(crate) use amd64 as imp; - - pub type RawContextCPU = imp::MDRawContextAMD64; + pub type RawContextCPU = minidump_common::format::CONTEXT_AMD64; + pub type FloatStateCPU = minidump_common::format::XMM_SAVE_AREA32; } else if #[cfg(target_arch = "x86")] { - pub(crate) mod x86; - pub(crate) pub use x86 as imp; - - pub type RawContextCPU = imp::MDRawContextX86; + 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(crate) mod arm; - pub(crate) pub use arm as imp; - - pub type RawContextCPU = imp::MDRawContextARM; + pub mod arm; + pub use arm as imp; + pub type RawContextCPU = arm::MDRawContextARM; } else if #[cfg(target_arch = "aarch64")] { - pub(crate) mod aarch64; - pub(crate) pub use aarch64 as imp; - + 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/aarch64.rs b/src/minidump_cpu/aarch64.rs deleted file mode 100644 index c35e2e3c..00000000 --- a/src/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/amd64.rs b/src/minidump_cpu/amd64.rs deleted file mode 100644 index cd445dae..00000000 --- a/src/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/x86.rs b/src/minidump_cpu/x86.rs deleted file mode 100644 index b0b073ff..00000000 --- a/src/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_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/tests/minidump_writer.rs b/tests/minidump_writer.rs index 3055c1d8..ad8d2bea 100644 --- a/tests/minidump_writer.rs +++ b/tests/minidump_writer.rs @@ -162,7 +162,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"); @@ -493,10 +493,7 @@ fn test_with_deleted_binary() { .main_module() .expect("Could not get main module"); 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())) - ); + assert_eq!(main_module.debug_identifier(), filtered.parse().ok()); } fn test_skip_if_requested_helper(context: Context) { From 83b97642b46499fdc7adc41ba13e49de868072e9 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 14 Mar 2022 21:14:17 +0100 Subject: [PATCH 2/4] Add x86 and aarch64 impls as well --- src/linux/crash_context.rs | 21 ++++- src/linux/crash_context/aarch64.rs | 18 +++- src/linux/crash_context/x86.rs | 68 +++++++++------ src/linux/crash_context/x86_64.rs | 3 +- src/linux/dumper_cpu_info/arm.rs | 131 ++++++++++++++++------------ src/linux/thread_info.rs | 2 +- src/linux/thread_info/aarch64.rs | 134 +++++++++++++++++++++++------ src/linux/thread_info/x86.rs | 4 +- 8 files changed, 263 insertions(+), 118 deletions(-) diff --git a/src/linux/crash_context.rs b/src/linux/crash_context.rs index 8c155834..97fe1e76 100644 --- a/src/linux/crash_context.rs +++ b/src/linux/crash_context.rs @@ -18,7 +18,26 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")] { pub(crate) mod aarch64; - pub type fpstate_t = libc::fpsimd_context; // Currently not part of libc! This will produce an error. + /// Magic value written by the kernel and our custom getcontext + pub const FPSIMD_MAGIC: u32 = 0x46508001; + + #[repr(C)] + #[derive(Clone)] + pub struct _aarch64_ctx { + pub magic: u32, + pub size: u32, + } + + #[repr(C)] + #[derive(Clone)] + pub struct fpsimd_context { + pub head: _aarch64_ctx, + pub fpsr: u32, + pub fpcr: u32, + pub vregs: [u128; 32], + } + + pub type fpstate_t = fpsimd_context; } } diff --git a/src/linux/crash_context/aarch64.rs b/src/linux/crash_context/aarch64.rs index e6fb3f9e..0d1796e0 100644 --- a/src/linux/crash_context/aarch64.rs +++ b/src/linux/crash_context/aarch64.rs @@ -1,11 +1,25 @@ use super::CrashContext; +use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; impl CrashContext { pub fn get_instruction_pointer(&self) -> usize { - self.context.uc_mcontext.sp as usize + self.context.uc_mcontext.pc as usize } pub fn get_stack_pointer(&self) -> usize { - self.context.uc_mcontext.pc as usize + self.context.uc_mcontext.sp as usize + } + + pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { + out.context_flags = format::ContextFlagsArm64Old::CONTEXT_ARM64_FULL_OLD.bits() as u64; + + out.cpsr = self.context.uc_mcontext.pstate as u32; + out.iregs[..31].copy_from_slice(&self.context.uc_mcontext.regs[..31]); + out.iregs[31] = self.context.uc_mcontext.sp; + out.pc = self.context.uc_mcontext.pc; + + out.float_save.fpsr = self.float_state.fpsr; + out.float_save.fpcr = self.float_state.fpcr; + out.float_save.regs[..16].copy_from_slice(&self.float_state.vregs[..16]); } } diff --git a/src/linux/crash_context/x86.rs b/src/linux/crash_context/x86.rs index 181deeae..406212be 100644 --- a/src/linux/crash_context/x86.rs +++ b/src/linux/crash_context/x86.rs @@ -1,6 +1,5 @@ use super::CrashContext; -use crate::minidump_cpu::imp::*; -use crate::minidump_cpu::RawContextCPU; +use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; 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, @@ -15,36 +14,49 @@ impl CrashContext { } pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { - out.context_flags = MD_CONTEXT_X86_FULL | MD_CONTEXT_X86_FLOATING_POINT; + out.context_flags = format::ContextFlagsX86::CONTEXT_X86_FULL.bits() + | format::ContextFlagsX86::CONTEXT_X86_FLOATING_POINT.bits(); - 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; + { + let gregs = &self.context.uc_mcontext.gregs; + out.gs = gregs[REG_GS as usize] as u32; + out.fs = gregs[REG_FS as usize] as u32; + out.es = gregs[REG_ES as usize] as u32; + out.ds = 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.edi = gregs[REG_EDI as usize] as u32; + out.esi = gregs[REG_ESI as usize] as u32; + out.ebx = gregs[REG_EBX as usize] as u32; + out.edx = gregs[REG_EDX as usize] as u32; + out.ecx = gregs[REG_ECX as usize] as u32; + out.eax = 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.ebp = gregs[REG_EBP as usize] as u32; + out.eip = gregs[REG_EIP as usize] as u32; + out.cs = gregs[REG_CS as usize] as u32; + out.eflags = gregs[REG_EFL as usize] as u32; + out.esp = gregs[REG_UESP as usize] as u32; + out.ss = 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; + { + let fs = &self.float_state; + let mut out = &mut out.float_save; + out.control_word = fs.cw; + out.status_word = fs.sw; + out.tag_word = fs.tag; + out.error_offset = fs.ipoff; + out.error_selector = fs.cssel; + out.data_offset = fs.dataoff; + out.data_selector = fs.datasel; - // 8 registers * 10 bytes per register. - // my_memcpy(out->float_save.register_area, fp->_st, 10 * 8); + debug_assert_eq!(fs._st.len() * std::mem::size_of::(), 80); + out.register_area.copy_from_slice(unsafe { + std::slice::from_raw_parts( + fs._st.as_ptr().cast(), + fs._st.len() * std::mem::size_of::(), + ) + }); + } } } diff --git a/src/linux/crash_context/x86_64.rs b/src/linux/crash_context/x86_64.rs index fa1b4ca9..d4ff7e3a 100644 --- a/src/linux/crash_context/x86_64.rs +++ b/src/linux/crash_context/x86_64.rs @@ -1,6 +1,5 @@ use super::CrashContext; -use crate::minidump_cpu::RawContextCPU; -use crate::minidump_format::format; +use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; 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, diff --git a/src/linux/dumper_cpu_info/arm.rs b/src/linux/dumper_cpu_info/arm.rs index ab7da7ae..b5f33b01 100644 --- a/src/linux/dumper_cpu_info/arm.rs +++ b/src/linux/dumper_cpu_info/arm.rs @@ -1,10 +1,10 @@ -use crate::errors::CpuInfoError; -use crate::minidump_format::*; -use std::collections::HashSet; -use std::fs::File; -use std::io::Read; -use std::io::{BufRead, BufReader}; -use std::path; +use crate::{errors::CpuInfoError, minidump_format::*}; +use std::{ + collections::HashSet, + fs::File, + io::{BufRead, BufReader, Read}, + path, +}; type Result = std::result::Result; @@ -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,6 @@ 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; // Counting the number of CPUs involves parsing two sysfs files, // because the content of /proc/cpuinfo will only mirror the number @@ -174,6 +161,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 +200,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 +235,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/thread_info.rs b/src/linux/thread_info.rs index 0943267a..e4fc4af1 100644 --- a/src/linux/thread_info.rs +++ b/src/linux/thread_info.rs @@ -30,7 +30,7 @@ cfg_if::cfg_if! { enum NT_Elf { NT_NONE = 0, NT_PRSTATUS = 1, - NT_PRFPREG = 2, + NT_PRFPREGSET = 2, //NT_PRPSINFO = 3, //NT_TASKSTRUCT = 4, //NT_AUXV = 6, diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs index cca218f4..44ae234a 100644 --- a/src/linux/thread_info/aarch64.rs +++ b/src/linux/thread_info/aarch64.rs @@ -1,48 +1,130 @@ -use super::Pid; +use super::{CommonThreadInfo, Pid}; use crate::errors::ThreadInfoError; -use crate::minidump_cpu::imp::{MDARM64RegisterNumbers, MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT}; -use libc; +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: libc::c_ulonglong, + pub stack_pointer: usize, pub tgid: Pid, // thread group id pub ppid: Pid, // parent process pub regs: libc::user_regs_struct, - pub fpregs: libc::user_fpsimd_struct, + pub fpregs: user_fpsimd_struct, } +impl CommonThreadInfo for ThreadInfoAarch64 {} + impl ThreadInfoAarch64 { - pub fn get_instruction_pointer(&self) -> libc::c_ulonglong { - self.regs.pc + 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 = MD_CONTEXT_ARM64_FULL_OLD; + 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; - 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.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(_pid: Pid, tid: Pid) -> Result { + let (ppid, tgid) = Self::get_ppid_and_tgid(tid)?; + let regs = Self::getregs(tid)?; + let fpregs = Self::getfpregs(tid)?; + + let stack_pointer = regs.regs[13] as usize; - // 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 + Ok(Self { + stack_pointer, + tgid, + ppid, + regs, + fpregs, + }) } } diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index 864d417d..1c6eff03 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -38,7 +38,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), ) } @@ -209,7 +209,7 @@ impl ThreadInfoX86 { #[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; From 298ea518db03818dc7ee6375e79e8a7b18bb60fc Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 15 Mar 2022 15:12:50 +0100 Subject: [PATCH 3/4] Update based on latest minidump changes --- src/linux/crash_context/aarch64.rs | 17 ++++++++++------- src/linux/thread_info/aarch64.rs | 25 +++++++++---------------- src/minidump_cpu.rs | 7 ++++++- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/linux/crash_context/aarch64.rs b/src/linux/crash_context/aarch64.rs index 0d1796e0..216d9441 100644 --- a/src/linux/crash_context/aarch64.rs +++ b/src/linux/crash_context/aarch64.rs @@ -1,5 +1,8 @@ use super::CrashContext; -use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; +use crate::{ + minidump_cpu::{RawContextCPU, FP_REG_COUNT, GP_REG_COUNT}, + minidump_format::format, +}; impl CrashContext { pub fn get_instruction_pointer(&self) -> usize { @@ -11,15 +14,15 @@ impl CrashContext { } pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { - out.context_flags = format::ContextFlagsArm64Old::CONTEXT_ARM64_FULL_OLD.bits() as u64; + out.context_flags = format::ContextFlagsArm64Old::CONTEXT_ARM64_OLD_FULL.bits() as u64; out.cpsr = self.context.uc_mcontext.pstate as u32; - out.iregs[..31].copy_from_slice(&self.context.uc_mcontext.regs[..31]); - out.iregs[31] = self.context.uc_mcontext.sp; + out.iregs[..GP_REG_COUNT].copy_from_slice(&self.context.uc_mcontext.regs[..GP_REG_COUNT]); + out.sp = self.context.uc_mcontext.sp; out.pc = self.context.uc_mcontext.pc; - out.float_save.fpsr = self.float_state.fpsr; - out.float_save.fpcr = self.float_state.fpcr; - out.float_save.regs[..16].copy_from_slice(&self.float_state.vregs[..16]); + out.fpsr = self.float_state.fpsr; + out.fpcr = self.float_state.fpcr; + out.float_regs[..FP_REG_COUNT].copy_from_slice(&self.float_state.vregs[..FP_REG_COUNT]); } } diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs index 44ae234a..c1cb613c 100644 --- a/src/linux/thread_info/aarch64.rs +++ b/src/linux/thread_info/aarch64.rs @@ -1,11 +1,10 @@ use super::{CommonThreadInfo, Pid}; -use crate::errors::ThreadInfoError; -use crate::minidump_cpu::RawContextCPU; +use crate::{ + errors::ThreadInfoError, + minidump_cpu::{RawContextCPU, FP_REG_COUNT, GP_REG_COUNT}, +}; 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. */ @@ -91,25 +90,19 @@ impl ThreadInfoAarch64 { 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; + minidump_common::format::ContextFlagsArm64Old::CONTEXT_ARM64_OLD_FULL.bits() as u64; 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; + out.sp = 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]); + out.fpsr = self.fpregs.fpsr; + out.fpcr = self.fpregs.fpcr; + out.float_regs[..FP_REG_COUNT].copy_from_slice(&self.fpregs.vregs[..FP_REG_COUNT]); } pub fn create_impl(_pid: Pid, tid: Pid) -> Result { diff --git a/src/minidump_cpu.rs b/src/minidump_cpu.rs index 51884ebd..8ddf29c3 100644 --- a/src/minidump_cpu.rs +++ b/src/minidump_cpu.rs @@ -10,8 +10,13 @@ cfg_if::cfg_if! { pub use arm as imp; pub type RawContextCPU = arm::MDRawContextARM; } else if #[cfg(target_arch = "aarch64")] { + /// This is the number of general purpose registers _not_ counting + /// the stack pointer + pub(crate) const GP_REG_COUNT: usize = 31; + /// The number of floating point registers in the floating point save area + pub(crate) const FP_REG_COUNT: usize = 32; + 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 { From 6c2d7ce01559401e5844f22738ddd1f16ca46304 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Tue, 15 Mar 2022 16:38:27 +0100 Subject: [PATCH 4/4] Address feedback --- Cargo.toml | 4 ++-- src/linux/crash_context/x86.rs | 6 +++--- src/linux/crash_context/x86_64.rs | 24 ++++-------------------- src/linux/dumper_cpu_info.rs | 15 +++++++-------- src/linux/minidump_writer.rs | 2 +- src/linux/thread_info/x86.rs | 17 +++++++++-------- 6 files changed, 26 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ea8b109..8fe64dfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,5 +26,5 @@ nix = "0.23" 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" } +minidump = { git = "https://github.com/luser/rust-minidump", branch = "master" } +minidump-common = { git = "https://github.com/luser/rust-minidump", branch = "master" } diff --git a/src/linux/crash_context/x86.rs b/src/linux/crash_context/x86.rs index 406212be..874a31f9 100644 --- a/src/linux/crash_context/x86.rs +++ b/src/linux/crash_context/x86.rs @@ -1,5 +1,5 @@ use super::CrashContext; -use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; +use crate::{minidump_cpu::RawContextCPU, minidump_format::format::ContextFlagsX86}; 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, @@ -14,8 +14,8 @@ impl CrashContext { } pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { - out.context_flags = format::ContextFlagsX86::CONTEXT_X86_FULL.bits() - | format::ContextFlagsX86::CONTEXT_X86_FLOATING_POINT.bits(); + out.context_flags = ContextFlagsX86::CONTEXT_X86_FULL.bits() + | ContextFlagsX86::CONTEXT_X86_FLOATING_POINT.bits(); { let gregs = &self.context.uc_mcontext.gregs; diff --git a/src/linux/crash_context/x86_64.rs b/src/linux/crash_context/x86_64.rs index d4ff7e3a..ffc5f8ad 100644 --- a/src/linux/crash_context/x86_64.rs +++ b/src/linux/crash_context/x86_64.rs @@ -1,9 +1,12 @@ use super::CrashContext; -use crate::{minidump_cpu::RawContextCPU, minidump_format::format}; +use crate::{ + minidump_cpu::RawContextCPU, minidump_format::format, thread_info::copy_u32_registers, +}; 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, }; +use scroll::Pwrite; impl CrashContext { pub fn get_instruction_pointer(&self) -> usize { @@ -64,28 +67,9 @@ impl CrashContext { ..Default::default() }; - #[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)); - } - 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"); diff --git a/src/linux/dumper_cpu_info.rs b/src/linux/dumper_cpu_info.rs index bbe22e7c..e753a141 100644 --- a/src/linux/dumper_cpu_info.rs +++ b/src/linux/dumper_cpu_info.rs @@ -34,12 +34,11 @@ pub fn os_information() -> (PlatformId, String) { info.machine() ); - ( - if cfg!(target_os = "android") { - PlatformId::Android - } else { - PlatformId::Linux - }, - vers, - ) + let platform_id = if cfg!(target_os = "android") { + PlatformId::Android + } else { + PlatformId::Linux + }; + + (platform_id, vers) } diff --git a/src/linux/minidump_writer.rs b/src/linux/minidump_writer.rs index 2ec12a0a..c3249607 100644 --- a/src/linux/minidump_writer.rs +++ b/src/linux/minidump_writer.rs @@ -203,7 +203,7 @@ impl MinidumpWriter { } } - let mut buffer = Buffer::with_capacity(4 * 1024); + let mut buffer = Buffer::with_capacity(0); self.generate_dump(&mut buffer, &mut dumper, destination)?; // dumper would resume threads in drop() automatically, diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index 1c6eff03..5e08bf52 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -1,8 +1,9 @@ use super::{CommonThreadInfo, NT_Elf, Pid}; -use crate::{errors::ThreadInfoError, minidump_cpu::RawContextCPU}; +use crate::{errors::ThreadInfoError, minidump_cpu::RawContextCPU, minidump_format::format}; use core::mem::size_of_val; use libc::user; use nix::{sys::ptrace, unistd}; +use scroll::Pwrite; type Result = std::result::Result; @@ -137,9 +138,10 @@ impl ThreadInfoX86 { #[cfg(target_arch = "x86_64")] pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { - out.context_flags = crate::minidump_format::format::ContextFlagsAmd64::CONTEXT_AMD64_FULL - .bits() - | crate::minidump_format::format::ContextFlagsAmd64::CONTEXT_AMD64_SEGMENTS.bits(); + use format::ContextFlagsAmd64; + + out.context_flags = ContextFlagsAmd64::CONTEXT_AMD64_FULL.bits() + | ContextFlagsAmd64::CONTEXT_AMD64_SEGMENTS.bits(); out.cs = self.regs.cs as u16; // TODO: This is u64, do we loose information by doing this? @@ -182,7 +184,7 @@ impl ThreadInfoX86 { out.rip = self.regs.rip; { - let fs = self.fpregs; + let fs = &self.fpregs; let mut float_save = crate::minidump_cpu::FloatStateCPU { control_word: fs.cwd, status_word: fs.swd, @@ -200,7 +202,6 @@ impl ThreadInfoX86 { super::copy_u32_registers(&mut float_save.float_registers, &fs.st_space); super::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"); @@ -209,7 +210,7 @@ impl ThreadInfoX86 { #[cfg(target_arch = "x86")] pub fn fill_cpu_context(&self, out: &mut RawContextCPU) { - out.context_flags = crate::minidump_format::format::ContextFlagsX86::CONTEXT_X86_ALL.bits(); + out.context_flags = format::ContextFlagsX86::CONTEXT_X86_ALL.bits(); out.dr0 = self.dregs[0] as u32; out.dr3 = self.dregs[3] as u32; @@ -258,7 +259,7 @@ impl ThreadInfoX86 { } ra.pwrite_with(block, offset, scroll::Endian::Little) - .expect("checked"); + .expect("this is impossible"); } }