Skip to content

Commit

Permalink
Try to read the build id from the mapping file if reading from the
Browse files Browse the repository at this point in the history
process memory fails.

We could potentially generalize this behavior to occur for SONAME as
well, however it's likely unnecessary.

Closes #134.
  • Loading branch information
afranchuk committed Sep 6, 2024
1 parent 2fbb435 commit 92326df
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/linux/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ pub enum WriterError {

#[derive(Debug, Error)]
pub enum ModuleReaderError {
#[error("failed to read module file ({path}): {error}")]
MapFile {
path: std::path::PathBuf,
#[source]
error: std::io::Error,
},
#[error("failed to read module memory: {length} bytes at {offset}{}: {error}", .start_address.map(|addr| format!(" (start address: {addr})")).unwrap_or_default())]
ReadModuleMemory {
offset: u64,
Expand Down
11 changes: 11 additions & 0 deletions src/linux/module_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ fn section_header_with_name<'sc>(
/// Types which can be read from ProcessMemory.
pub trait ReadFromModule: Sized {
fn read_from_module(module_memory: ProcessMemory<'_>) -> Result<Self, Error>;

fn read_from_file(path: &std::path::Path) -> Result<Self, Error> {
let map = std::fs::File::open(path)
// Safety: the file is an executable binary (very likely read-only), and won't be changed.
.and_then(|file| unsafe { memmap2::Mmap::map(&file) })
.map_err(|error| Error::MapFile {
path: path.to_owned(),
error,
})?;
Self::read_from_module(ProcessMemory::Slice(&map))
}
}

/// The module build id.
Expand Down
29 changes: 27 additions & 2 deletions src/linux/sections/mappings.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::linux::maps_reader::MappingInfo;
use crate::linux::module_reader::{BuildId, SoName};
use crate::linux::module_reader::{BuildId, ReadFromModule, SoName};

/// Write information about the mappings in effect. Because we are using the
/// minidump format, the information about the mappings is pretty limited.
Expand All @@ -24,15 +24,40 @@ pub fn write(
{
continue;
}
log::debug!("retrieving build id for {:?}", &dumper.mappings[map_idx]);
let BuildId(identifier) = dumper
.from_process_memory_for_index(map_idx)
.unwrap_or_else(|_| BuildId(Vec::new()));
.or_else(|e| {
// If the mapping has an associated name that is a file, try to read the build id
// from the file. If there is no note segment with the build id in
// the program headers, we can't get to the note section if the section header
// table isn't loaded.
if let Some(path) = &dumper.mappings[map_idx].name {
let path = std::path::Path::new(&path);
if path.exists() {
log::debug!("failed to get build id from process memory ({e}), attempting to retrieve from {}", path.display());
return BuildId::read_from_file(path)
.map_err(errors::DumperError::ModuleReaderError);
}
log::debug!(
"not attempting to get build id from {}: path does not exist",
path.display()
);
}
Err(e)
})
.unwrap_or_else(|e| {
log::warn!("failed to get build id for mapping: {e}");
BuildId(Vec::new())
});

// If the identifier is all 0, its an uninteresting mapping (bmc#1676109)
if identifier.is_empty() || identifier.iter().all(|&x| x == 0) {
continue;
}

// SONAME should always be accessible through program headers alone, so we don't really
// need to fall back to trying to read from the mapping file.
let soname = dumper
.from_process_memory_for_index(map_idx)
.ok()
Expand Down

0 comments on commit 92326df

Please sign in to comment.