From 4909a1056bd8bde1064a5f8a804aa8b9fc681a8c Mon Sep 17 00:00:00 2001
From: Alexis Beingessner
Date: Wed, 2 Mar 2022 01:15:07 -0500
Subject: [PATCH] WIP android64 support
---
Cargo.toml | 2 +-
src/crash_context/crash_context_aarch64.rs | 16 ++++
src/crash_context/mod.rs | 4 +-
src/dumper_cpu_info/cpu_info_arm.rs | 8 +-
src/minidump_cpu/minidump_cpu_aarch64.rs | 102 +++++++++++++++++++++
src/minidump_cpu/mod.rs | 2 +-
src/sections/thread_list_stream.rs | 4 +-
src/thread_info/thread_info_aarch64.rs | 69 +++++++++-----
tests/minidump_writer.rs | 15 ++-
tests/ptrace_dumper.rs | 12 +--
10 files changed, 196 insertions(+), 38 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index a8eb7ce1..e6631294 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ license = "MIT"
[dependencies]
tempfile = "3.1.0"
nix = "0.23"
-libc = "0.2.74"
+libc = "0.2.117"
memoffset = "0.5.1"
byteorder = "1.3.2"
memmap2 = "0.2.2"
diff --git a/src/crash_context/crash_context_aarch64.rs b/src/crash_context/crash_context_aarch64.rs
index e6fb3f9e..af16fbf0 100644
--- a/src/crash_context/crash_context_aarch64.rs
+++ b/src/crash_context/crash_context_aarch64.rs
@@ -1,3 +1,5 @@
+use crate::minidump_cpu::{RawContextCPU, imp::{MD_CONTEXT_ARM64_ALL_OLD, MDARM64RegisterNumbers}};
+
use super::CrashContext;
impl CrashContext {
@@ -8,4 +10,18 @@ impl CrashContext {
pub fn get_stack_pointer(&self) -> usize {
self.context.uc_mcontext.pc as usize
}
+
+ pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
+ out.context_flags = MD_CONTEXT_ARM64_ALL_OLD;
+ out.cpsr = self.context.uc_mcontext.pstate as u32;
+ for idx in 0..MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP as usize {
+ out.iregs[idx] = self.context.uc_mcontext.regs[idx];
+ }
+ out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP as usize] = self.context.uc_mcontext.sp;
+ out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_PC as usize] = self.context.uc_mcontext.pc;
+ out.pc = self.context.uc_mcontext.pc;
+ out.float_save.fpcr = self.float_state.fpcr;
+ out.float_save.fpsr = self.float_state.fpsr;
+ out.float_save.regs = self.float_state.regs;
+ }
}
diff --git a/src/crash_context/mod.rs b/src/crash_context/mod.rs
index 2be8fb15..0f5f10bd 100644
--- a/src/crash_context/mod.rs
+++ b/src/crash_context/mod.rs
@@ -16,6 +16,7 @@ pub mod imp;
// pub mod imp;
#[cfg(target_arch = "arm")]
use crate::minidump_cpu::RawContextCPU;
+use crate::minidump_cpu::imp::libc_user_fpsimd_struct;
#[cfg(target_arch = "arm")]
impl CrashContext {
pub fn get_instruction_pointer(&self) -> usize {
@@ -37,7 +38,8 @@ pub mod imp;
pub mod imp;
#[cfg(target_arch = "aarch64")]
-pub type fpstate_t = libc::fpsimd_context; // Currently not part of libc! This will produce an error.
+#[allow(non_camel_case_types)]
+pub type fpstate_t = libc_user_fpsimd_struct; // Currently not part of libc! This will produce an error.
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "mips",
diff --git a/src/dumper_cpu_info/cpu_info_arm.rs b/src/dumper_cpu_info/cpu_info_arm.rs
index ab7da7ae..f943fc9b 100644
--- a/src/dumper_cpu_info/cpu_info_arm.rs
+++ b/src/dumper_cpu_info/cpu_info_arm.rs
@@ -86,10 +86,10 @@ 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;
+ let cpu_features_entries: &[CpuFeaturesEntry];
#[cfg(target_arch = "arm")]
{
- cpu_features_entries = [
+ cpu_features_entries = &[
CpuFeaturesEntry::new("swp", MDCPUInformationARMElfHwCaps::Swp as u32),
CpuFeaturesEntry::new("half", MDCPUInformationARMElfHwCaps::Half as u32),
CpuFeaturesEntry::new("thumb", MDCPUInformationARMElfHwCaps::Thumb as u32),
@@ -119,7 +119,7 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
#[cfg(target_arch = "aarch64")]
{
// No hwcaps on aarch64.
- cpu_features_entries = [];
+ cpu_features_entries = &[];
}
// processor_architecture should always be set, do this first
@@ -251,7 +251,7 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
if let Some(val) = value {
// Parse each space-separated tag.
for tag in val.split_whitespace() {
- for entry in &cpu_features_entries {
+ for entry in cpu_features_entries {
if entry.tag == tag {
sys_info.cpu.elf_hwcaps |= entry.hwcaps;
break;
diff --git a/src/minidump_cpu/minidump_cpu_aarch64.rs b/src/minidump_cpu/minidump_cpu_aarch64.rs
index c35e2e3c..2b7a56bd 100644
--- a/src/minidump_cpu/minidump_cpu_aarch64.rs
+++ b/src/minidump_cpu/minidump_cpu_aarch64.rs
@@ -1,12 +1,114 @@
+/// A u128 that matches the layout of uint128_t for C FFI purposes
+/// **BUT NOT THE ABI**. This is safe for pass-by-ref but not pass-by-value.
+///
+/// Rust underaligns u128 compared to C's ABI due to a long-standing llvm bug.
+/// Unfortuantely library code *can't* perfectly work around this, because
+/// primitives have magic ABIs.
+///
+/// Although repr(transparent) *exists* to preserve the magic ABI of primitives,
+/// you can't combine this with other reprs (which makes a kind of sense),
+/// and we need to apply repr(align(16)).
+///
+/// The upshot of this is that this type (or a struct containing it) can be
+/// passed *by-reference* to C, but if you try to pass it *by-value* then
+/// the ABI might not match and this value may not be passed to the function
+/// right.
+///
+/// This is good enough for our purposes, because we largely just want this
+/// for APIs like linux's getcontext which does in fact work by-reference.
+///
+/// See "i128 / u128 are not compatible with C's definition."
+/// https://github.com/rust-lang/rust/issues/54341
+#[repr(C, align(16))]
+#[derive(Debug, Copy, Clone, Default)]
+#[allow(non_camel_case_types)]
+pub struct layout_only_ffi_u128(u128);
+
+impl layout_only_ffi_u128 {
+ pub fn to_ne_bytes(self) -> [u8; 16] {
+ self.0.to_ne_bytes()
+ }
+}
+
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.
*/
+#[allow(non_camel_case_types)]
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,
}
+
+/* Windows only?
+#[repr(C)]
+#[derive(Default)]
+pub struct MDRawContextARM64 {
+ pub context_flags: u32,
+ pub cpsr: u32,
+ pub iregs: [u64; 32],
+ pub pc: u64,
+ pub float_save: libc_user_fpsimd_struct,
+ pub bcr: [u32; 8],
+ pub bvr: [u64; 8],
+ pub wcr: [u32; 2],
+ pub wvr: [u64; 2],
+}
+*/
+
+#[repr(C)]
+#[derive(Default)]
+pub struct MDRawContextARM64Old {
+ pub context_flags: u64,
+ pub iregs: [u64; 32],
+ pub pc: u64,
+ pub cpsr: u32,
+ pub float_save: FloatingSaveAreaARM64Old,
+}
+
+/// aarch64 floating point state
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Default)]
+#[allow(non_camel_case_types)]
+pub struct libc_user_fpsimd_struct {
+ pub regs: [layout_only_ffi_u128; 32usize],
+ pub fpsr: u32,
+ pub fpcr: u32,
+}
+
+/// aarch64 floating point state (old)
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct FloatingSaveAreaARM64Old {
+ pub fpsr: u32,
+ pub fpcr: u32,
+ pub regs: [layout_only_ffi_u128; 32usize],
+}
+
+pub const MD_CONTEXT_ARM64: u32 = 0x400000;
+pub const MD_CONTEXT_ARM64_OLD: u64 = 0x80000000;
+
+pub const MD_CONTEXT_ARM64_ALL_OLD: u64 = MD_CONTEXT_ARM64_OLD | 0x2 | 0x4;
+
+/*
+/* For (MDRawContextARM64_Old).context_flags. These values indicate the type of
+ * context stored in the structure. MD_CONTEXT_ARM64_OLD is Breakpad-defined.
+ * This value was chosen to avoid likely conflicts with MD_CONTEXT_*
+ * for other CPUs. */
+#define MD_CONTEXT_ARM64_OLD 0x80000000
+#define MD_CONTEXT_ARM64_INTEGER_OLD (MD_CONTEXT_ARM64_OLD | 0x00000002)
+#define MD_CONTEXT_ARM64_FLOATING_POINT_OLD (MD_CONTEXT_ARM64_OLD | 0x00000004)
+
+#define MD_CONTEXT_ARM64_FULL_OLD (MD_CONTEXT_ARM64_INTEGER_OLD | \
+ MD_CONTEXT_ARM64_FLOATING_POINT_OLD)
+
+#define MD_CONTEXT_ARM64_ALL_OLD (MD_CONTEXT_ARM64_INTEGER_OLD | \
+ MD_CONTEXT_ARM64_FLOATING_POINT_OLD)
+
+*/
\ No newline at end of file
diff --git a/src/minidump_cpu/mod.rs b/src/minidump_cpu/mod.rs
index ebce0ad5..ce90a2f7 100644
--- a/src/minidump_cpu/mod.rs
+++ b/src/minidump_cpu/mod.rs
@@ -21,6 +21,6 @@ pub type RawContextCPU = imp::MDRawContextX86;
#[cfg(target_arch = "arm")]
pub type RawContextCPU = imp::MDRawContextARM;
#[cfg(target_arch = "aarch64")]
-pub type RawContextCPU = imp::MDRawContextX86;
+pub type RawContextCPU = imp::MDRawContextARM64Old;
#[cfg(target_arch = "mips")]
pub type RawContextCPU = i32;
diff --git a/src/sections/thread_list_stream.rs b/src/sections/thread_list_stream.rs
index d3672d99..24eb596e 100644
--- a/src/sections/thread_list_stream.rs
+++ b/src/sections/thread_list_stream.rs
@@ -152,7 +152,7 @@ pub fn write(
dumper,
&mut thread,
instruction_ptr,
- info.stack_pointer,
+ info.stack_pointer as usize,
max_stack_len,
)?;
@@ -166,7 +166,7 @@ pub fn write(
// while the instruction pointer is already here.
config.crashing_thread_context = CrashingThreadContext::CrashContextPlusAddress((
cpu_section.location(),
- info.get_instruction_pointer(),
+ info.get_instruction_pointer() as usize,
));
}
}
diff --git a/src/thread_info/thread_info_aarch64.rs b/src/thread_info/thread_info_aarch64.rs
index cca218f4..a8233b76 100644
--- a/src/thread_info/thread_info_aarch64.rs
+++ b/src/thread_info/thread_info_aarch64.rs
@@ -1,7 +1,9 @@
-use super::Pid;
+use super::{Pid, CommonThreadInfo};
use crate::errors::ThreadInfoError;
-use crate::minidump_cpu::imp::{MDARM64RegisterNumbers, MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT};
+use crate::minidump_cpu::imp::{MDARM64RegisterNumbers, MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT, libc_user_fpsimd_struct, MD_CONTEXT_ARM64_OLD, MD_CONTEXT_ARM64_ALL_OLD};
+use crate::minidump_cpu::RawContextCPU;
use libc;
+use nix::sys::ptrace;
type Result = std::result::Result;
@@ -12,37 +14,60 @@ pub struct ThreadInfoAarch64 {
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: libc_user_fpsimd_struct,
}
+impl CommonThreadInfo for ThreadInfoAarch64 {}
+
impl ThreadInfoAarch64 {
+ // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
+ fn getfpregs(pid: Pid) -> Result {
+ Self::ptrace_get_data::(
+ ptrace::Request::PTRACE_GETFPREGS,
+ None,
+ nix::unistd::Pid::from_raw(pid),
+ )
+ }
+
+ // nix currently doesn't support PTRACE_GETFPREGS, so we have to do it ourselves
+ fn getregs(pid: Pid) -> Result {
+ Self::ptrace_get_data::(
+ ptrace::Request::PTRACE_GETFPREGS,
+ None,
+ nix::unistd::Pid::from_raw(pid),
+ )
+ }
+
pub fn get_instruction_pointer(&self) -> libc::c_ulonglong {
self.regs.pc
}
pub fn fill_cpu_context(&self, out: &mut RawContextCPU) {
- // out->context_flags = MD_CONTEXT_ARM64_FULL_OLD;
+ out.context_flags = MD_CONTEXT_ARM64_ALL_OLD;
out.cpsr = self.regs.pstate as u32;
- for idx in 0..MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP {
+ for idx in 0..MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP as usize {
out.iregs[idx] = self.regs.regs[idx];
}
- out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP] = self.regs.sp;
- out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_PC] = self.regs.pc;
- out.float_save.fpsr = self.fpregs.fpsr;
+ out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_SP as usize] = self.regs.sp;
+ out.iregs[MDARM64RegisterNumbers::MD_CONTEXT_ARM64_REG_PC as usize] = self.regs.pc;
+ out.pc = self.regs.pc;
out.float_save.fpcr = self.fpregs.fpcr;
+ out.float_save.fpsr = self.fpregs.fpsr;
+ out.float_save.regs = self.fpregs.regs;
+ }
+ 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)?;
- // 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
+ let stack_pointer = regs.uregs[13] as usize;
+
+ Ok(ThreadInfoAarch64 {
+ stack_pointer,
+ tgid,
+ ppid,
+ regs,
+ fpregs,
+ })
}
-}
+}
\ No newline at end of file
diff --git a/tests/minidump_writer.rs b/tests/minidump_writer.rs
index 95c26fb3..1744419a 100644
--- a/tests/minidump_writer.rs
+++ b/tests/minidump_writer.rs
@@ -27,7 +27,7 @@ enum Context {
Without,
}
-#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
+#[cfg(not(any(target_arch = "mips", target_arch = "arm", target_arch = "aarch64")))]
fn get_ucontext() -> Result {
let mut context = std::mem::MaybeUninit::::uninit();
let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
@@ -35,6 +35,19 @@ fn get_ucontext() -> Result {
unsafe { Ok(context.assume_init()) }
}
+#[cfg(target_arch = "aarch64")]
+fn get_ucontext() -> Result {
+ // Libc doesn't define this for arm64 because of the u128 thing
+ extern "C" {
+ pub fn getcontext(ucp: *mut libc::ucontext_t) -> libc::c_int;
+ }
+ let mut context = std::mem::MaybeUninit::::uninit();
+ let res = unsafe { getcontext(context.as_mut_ptr()) };
+ Errno::result(res)?;
+ unsafe { Ok(context.assume_init()) }
+}
+
+
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
fn get_crash_context(tid: Pid) -> CrashContext {
let siginfo: libc::siginfo_t = unsafe { std::mem::zeroed() };
diff --git a/tests/ptrace_dumper.rs b/tests/ptrace_dumper.rs
index c2e8fccf..42fa42ac 100644
--- a/tests/ptrace_dumper.rs
+++ b/tests/ptrace_dumper.rs
@@ -38,7 +38,7 @@ fn test_thread_list_from_parent() {
.get_thread_info_by_index(idx)
.expect("Could not get thread info by index");
let (_stack_ptr, stack_len) = dumper
- .get_stack_info(info.stack_pointer)
+ .get_stack_info(info.stack_pointer as usize)
.expect("Could not get stack_pointer");
assert!(stack_len > 0);
@@ -221,7 +221,7 @@ fn test_sanitize_stack_copy() {
dumper
.sanitize_stack_copy(
&mut simulated_stack,
- thread_info.stack_pointer,
+ thread_info.stack_pointer as usize,
size_of::(),
)
.expect("Could not sanitize stack");
@@ -238,7 +238,7 @@ fn test_sanitize_stack_copy() {
simulated_stack = vec![0u8; 2 * size_of::()];
simulated_stack[0..size_of::()].copy_from_slice(&(ii as usize).to_ne_bytes());
dumper
- .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer, 0)
+ .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer as usize, 0)
.expect("Failed to sanitize with small integers");
assert!(simulated_stack[size_of::()..] != defaced);
}
@@ -254,7 +254,7 @@ fn test_sanitize_stack_copy() {
simulated_stack = vec![0u8; 2 * size_of::()];
simulated_stack[size_of::()..].copy_from_slice(&instr_ptr.to_ne_bytes());
dumper
- .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer, 0)
+ .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer as usize, 0)
.expect("Failed to sanitize with instr_ptr");
assert!(simulated_stack[0..size_of::()] != defaced);
assert!(simulated_stack[size_of::()..] != defaced);
@@ -263,7 +263,7 @@ fn test_sanitize_stack_copy() {
let junk = "abcdefghijklmnop".as_bytes();
simulated_stack.copy_from_slice(&junk[0..2 * size_of::()]);
dumper
- .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer, 0)
+ .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer as usize, 0)
.expect("Failed to sanitize with junk");
assert_eq!(simulated_stack[0..size_of::()], defaced);
assert_eq!(simulated_stack[size_of::()..], defaced);
@@ -289,7 +289,7 @@ fn test_sanitize_stack_copy() {
simulated_stack[0..size_of::()].copy_from_slice(&heap_addr.to_ne_bytes());
dumper
- .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer, 0)
+ .sanitize_stack_copy(&mut simulated_stack, thread_info.stack_pointer as usize, 0)
.expect("Failed to sanitize with heap addr");
assert_eq!(simulated_stack[0..size_of::()], defaced);