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");
}
}