Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mac module load address #44

Merged
merged 4 commits into from
Jul 15, 2022
Merged
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
3 changes: 3 additions & 0 deletions src/mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#[cfg(target_pointer_width = "32")]
compile_error!("Various MacOS FFI bindings assume we are on a 64-bit architechture");

/// Re-export of the mach2 library for users who want to call mach specific functions
pub use mach2;

pub mod errors;
pub mod mach;
pub mod minidump_writer;
Expand Down
2 changes: 2 additions & 0 deletions src/mac/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ pub enum WriterError {
MemoryWriterError(#[from] crate::mem_writer::MemoryWriterError),
#[error("Failed to write to file")]
FileWriterError(#[from] crate::dir_section::FileWriterError),
#[error("Attempted to write an exception stream with no crash context")]
NoCrashContext,
}
65 changes: 58 additions & 7 deletions src/mac/minidump_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,71 @@ use crate::{
};
use std::io::{Seek, Write};

pub use mach2::mach_types::{task_t, thread_t};

type Result<T> = std::result::Result<T, WriterError>;

pub struct MinidumpWriter {
/// The crash context as captured by an exception handler
pub(crate) crash_context: crash_context::CrashContext,
pub(crate) crash_context: Option<crash_context::CrashContext>,
/// List of raw blocks of memory we've written into the stream. These are
/// referenced by other streams (eg thread list)
pub(crate) memory_blocks: Vec<MDMemoryDescriptor>,
/// The task being dumped
pub(crate) task: task_t,
/// The handler thread, so it can be ignored/deprioritized
pub(crate) handler_thread: thread_t,
}

impl MinidumpWriter {
/// Creates a minidump writer
pub fn new(crash_context: crash_context::CrashContext) -> Self {
/// Creates a minidump writer for the specified mach task (process) and
/// handler thread. If not specified, defaults to the current task and thread.
///
/// ```
/// use minidump_writer::{minidump_writer::MinidumpWriter, mach2};
///
/// // Note that this is the same as specifying `None` for both the task and
/// // handler thread, this is just meant to illustrate how you can setup
/// // a MinidumpWriter manually instead of using a `CrashContext`
/// // SAFETY: syscalls
/// let mdw = unsafe {
/// MinidumpWriter::new(
/// Some(mach2::traps::mach_task_self()),
/// Some(mach2::mach_init::mach_thread_self()),
/// )
/// };
/// ```
pub fn new(task: Option<task_t>, handler_thread: Option<thread_t>) -> Self {
Self {
crash_context: None,
memory_blocks: Vec::new(),
task: task.unwrap_or_else(|| {
// SAFETY: syscall
unsafe { mach2::traps::mach_task_self() }
}),
handler_thread: handler_thread.unwrap_or_else(|| {
// SAFETY: syscall
unsafe { mach2::mach_init::mach_thread_self() }
}),
}
}

/// Creates a minidump writer with the specified crash context, presumably
/// for another task
pub fn with_crash_context(crash_context: crash_context::CrashContext) -> Self {
let task = crash_context.task;
let handler_thread = crash_context.handler_thread;

Self {
crash_context,
crash_context: Some(crash_context),
memory_blocks: Vec::new(),
task,
handler_thread,
}
}

/// Writes a minidump to the specified destination, returning the raw minidump
/// contents upon success
pub fn dump(&mut self, destination: &mut (impl Write + Seek)) -> Result<Vec<u8>> {
let writers = {
#[allow(clippy::type_complexity)]
Expand All @@ -43,7 +89,12 @@ impl MinidumpWriter {
// Exception stream needs to be the last entry in this array as it may
// be omitted in the case where the minidump is written without an
// exception.
if self.crash_context.exception.is_some() {
if self
.crash_context
.as_ref()
.and_then(|cc| cc.exception.as_ref())
.is_some()
{
writers.push(Box::new(|mw, buffer, dumper| {
mw.write_exception(buffer, dumper)
}));
Expand Down Expand Up @@ -77,7 +128,7 @@ impl MinidumpWriter {
// we should have a mostly-intact dump
dir_section.write_to_file(&mut buffer, None)?;

let dumper = super::task_dumper::TaskDumper::new(self.crash_context.task);
let dumper = super::task_dumper::TaskDumper::new(self.task);

for mut writer in writers {
let dirent = writer(self, &mut buffer, &dumper)?;
Expand All @@ -93,7 +144,7 @@ impl MinidumpWriter {
pub(crate) fn threads(&self, dumper: &TaskDumper) -> ActiveThreads {
ActiveThreads {
threads: dumper.read_threads().unwrap_or_default(),
handler_thread: self.crash_context.handler_thread,
handler_thread: self.handler_thread,
i: 0,
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/mac/streams/breakpad_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ impl MinidumpWriter {
| BreakpadInfoValid::RequestingThreadId.bits(),
// The thread where the exception port handled the exception, might
// be useful to ignore/deprioritize when processing the minidump
dump_thread_id: self.crash_context.handler_thread,
dump_thread_id: self.handler_thread,
// The actual thread where the exception was thrown
requesting_thread_id: self.crash_context.thread,
requesting_thread_id: self.crash_context.as_ref().map(|cc| cc.thread).unwrap_or(0),
},
)?;

Expand Down
14 changes: 10 additions & 4 deletions src/mac/streams/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ impl MinidumpWriter {
buffer: &mut DumpBuf,
dumper: &TaskDumper,
) -> Result<MDRawDirectory, WriterError> {
let thread_state = dumper.read_thread_state(self.crash_context.thread).ok();
// This shouldn't fail since we won't be writing this stream if the crash context is
// not present
let crash_context = self
.crash_context
.as_ref()
.ok_or(WriterError::NoCrashContext)?;

let thread_state = dumper.read_thread_state(crash_context.thread).ok();

let thread_context = if let Some(ts) = &thread_state {
let mut cpu = Default::default();
Expand All @@ -22,8 +29,7 @@ impl MinidumpWriter {
None
};

let exception_record = self
.crash_context
let exception_record = crash_context
.exception
.as_ref()
.map(|exc| {
Expand All @@ -46,7 +52,7 @@ impl MinidumpWriter {
.unwrap_or_default();

let stream = MDRawExceptionStream {
thread_id: self.crash_context.thread,
thread_id: crash_context.thread,
exception_record,
thread_context: thread_context.unwrap_or_default(),
__align: 0,
Expand Down
63 changes: 33 additions & 30 deletions src/mac/streams/memory_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,47 @@ impl MinidumpWriter {
) -> Result<MDRawDirectory, WriterError> {
// Include some memory around the instruction pointer if the crash was
// due to an exception
if self.crash_context.exception.is_some() {
const IP_MEM_SIZE: u64 = 256;
if let Some(cc) = &self.crash_context {
if cc.exception.is_some() {
const IP_MEM_SIZE: u64 = 256;

let get_ip_block = |tid| -> Option<std::ops::Range<u64>> {
let thread_state = dumper.read_thread_state(tid).ok()?;
let get_ip_block = |tid| -> Option<std::ops::Range<u64>> {
let thread_state = dumper.read_thread_state(tid).ok()?;

let ip = thread_state.pc();
let ip = thread_state.pc();

// Bound it to the upper and lower bounds of the region
// it's contained within. If it's not in a known memory region,
// don't bother trying to write it.
let region = dumper.get_vm_region(ip).ok()?;
// Bound it to the upper and lower bounds of the region
// it's contained within. If it's not in a known memory region,
// don't bother trying to write it.
let region = dumper.get_vm_region(ip).ok()?;

if ip < region.range.start || ip > region.range.end {
return None;
}

// Try to get IP_MEM_SIZE / 2 bytes before and after the IP, but
// settle for whatever's available.
let start = std::cmp::max(region.range.start, ip - IP_MEM_SIZE / 2);
let end = std::cmp::min(ip + IP_MEM_SIZE / 2, region.range.end);
if ip < region.range.start || ip > region.range.end {
return None;
}

Some(start..end)
};
// Try to get IP_MEM_SIZE / 2 bytes before and after the IP, but
// settle for whatever's available.
let start = std::cmp::max(region.range.start, ip - IP_MEM_SIZE / 2);
let end = std::cmp::min(ip + IP_MEM_SIZE / 2, region.range.end);

if let Some(ip_range) = get_ip_block(self.crash_context.thread) {
let size = ip_range.end - ip_range.start;
let stack_buffer = dumper.read_task_memory(ip_range.start as _, size as usize)?;
let ip_location = MDLocationDescriptor {
data_size: size as u32,
rva: buffer.position() as u32,
Some(start..end)
};
buffer.write_all(&stack_buffer);

self.memory_blocks.push(MDMemoryDescriptor {
start_of_memory_range: ip_range.start,
memory: ip_location,
});
if let Some(ip_range) = get_ip_block(cc.thread) {
let size = ip_range.end - ip_range.start;
let stack_buffer =
dumper.read_task_memory(ip_range.start as _, size as usize)?;
let ip_location = MDLocationDescriptor {
data_size: size as u32,
rva: buffer.position() as u32,
};
buffer.write_all(&stack_buffer);

self.memory_blocks.push(MDMemoryDescriptor {
start_of_memory_range: ip_range.start,
memory: ip_location,
});
}
}
}

Expand Down
Loading