From 92326dfe2be241c5e9e9c7adf065746698222e12 Mon Sep 17 00:00:00 2001 From: Alex Franchuk Date: Wed, 4 Sep 2024 12:09:12 -0400 Subject: [PATCH] Try to read the build id from the mapping file if reading from the process memory fails. We could potentially generalize this behavior to occur for SONAME as well, however it's likely unnecessary. Closes #134. --- src/linux/errors.rs | 6 ++++++ src/linux/module_reader.rs | 11 +++++++++++ src/linux/sections/mappings.rs | 29 +++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/linux/errors.rs b/src/linux/errors.rs index 86f27f36..982445a0 100644 --- a/src/linux/errors.rs +++ b/src/linux/errors.rs @@ -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, diff --git a/src/linux/module_reader.rs b/src/linux/module_reader.rs index fffa7514..08ee8189 100644 --- a/src/linux/module_reader.rs +++ b/src/linux/module_reader.rs @@ -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; + + fn read_from_file(path: &std::path::Path) -> Result { + 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. diff --git a/src/linux/sections/mappings.rs b/src/linux/sections/mappings.rs index c0172084..6688387c 100644 --- a/src/linux/sections/mappings.rs +++ b/src/linux/sections/mappings.rs @@ -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. @@ -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()