Skip to content

Commit

Permalink
Merge pull request #135 from afranchuk/buildid-fallback
Browse files Browse the repository at this point in the history
Try to read the build id from the mapping file if reading from the process memory fails.
  • Loading branch information
gabrielesvelto authored Sep 6, 2024
2 parents 2fbb435 + 92326df commit 771cabf
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 771cabf

Please sign in to comment.