Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions src/crash_context/crash_context_aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::minidump_cpu::{RawContextCPU, imp::{MD_CONTEXT_ARM64_ALL_OLD, MDARM64RegisterNumbers}};

use super::CrashContext;

impl CrashContext {
Expand All @@ -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;
}
}
4 changes: 3 additions & 1 deletion src/crash_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you created your own struct, this will not produce an error anymore? So the second half of the comment can be removed, I think.

#[cfg(not(any(
target_arch = "aarch64",
target_arch = "mips",
Expand Down
8 changes: 4 additions & 4 deletions src/dumper_cpu_info/cpu_info_arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
102 changes: 102 additions & 0 deletions src/minidump_cpu/minidump_cpu_aarch64.rs
Original file line number Diff line number Diff line change
@@ -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] {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this function used?

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)

*/
2 changes: 1 addition & 1 deletion src/minidump_cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
4 changes: 2 additions & 2 deletions src/sections/thread_list_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub fn write(
dumper,
&mut thread,
instruction_ptr,
info.stack_pointer,
info.stack_pointer as usize,
max_stack_len,
)?;

Expand All @@ -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,
));
}
}
Expand Down
69 changes: 47 additions & 22 deletions src/thread_info/thread_info_aarch64.rs
Original file line number Diff line number Diff line change
@@ -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<T> = std::result::Result<T, ThreadInfoError>;

Expand All @@ -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<libc_user_fpsimd_struct> {
Self::ptrace_get_data::<libc_user_fpsimd_struct>(
ptrace::Request::PTRACE_GETFPREGS,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've almost got everything reasonable and compiling, but the last remaining blocker is this code here, where we get each thread's registers with ptrace-based register lookup.

For whatever reason it appears that PTRACE_GETREGS and PTRACE_GETFPREGS just don't exist on aarch64 (android?). The docs note that the APIs don't exist on all platforms but it is just Weird that this was the platform to do it?

Not fully clear on what's up with the breakpad version, ifdef hell...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible I want PTRACE_GETREGSET, but the nix ptrace stuff also doesn't have that for android. Not clear if that's a bug tho.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear, I have not much to add here. I'm most certainly not an Android expert :-/

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<libc::user_regs_struct> {
Self::ptrace_get_data::<libc::user_regs_struct>(
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<Self> {
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::<Vec<_>>()
.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,
})
}
}
}
15 changes: 14 additions & 1 deletion tests/minidump_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,27 @@ 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<libc::ucontext_t> {
let mut context = std::mem::MaybeUninit::<libc::ucontext_t>::uninit();
let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
Errno::result(res)?;
unsafe { Ok(context.assume_init()) }
}

#[cfg(target_arch = "aarch64")]
fn get_ucontext() -> Result<libc::ucontext_t> {
// 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::<libc::ucontext_t>::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() };
Expand Down
Loading