use super::{super::maps_reader::MappingInfo, *};
#[derive(Debug, Error, serde::Serialize)]
pub enum SectionMappingsError {
#[error("Failed to write to memory")]
MemoryWriterError(#[from] MemoryWriterError),
#[error("Failed to get effective path of mapping ({0:?})")]
GetEffectivePathError(MappingInfo, #[source] MapsReaderError),
}
impl MinidumpWriter {
pub fn write_mappings(
&mut self,
buffer: &mut DumpBuf,
) -> Result<MDRawDirectory, SectionMappingsError> {
let mut modules = Vec::new();
for map_idx in 0..self.mappings.len() {
if !self.mappings[map_idx].is_interesting()
|| self.mappings[map_idx].is_contained_in(&self.user_mapping_list)
{
continue;
}
log::debug!("retrieving build id for {:?}", self.mappings[map_idx]);
let identifier = self
.build_id_from_process_memory_for_index(map_idx)
.or_else(|e| {
let Some(path) = &self.mappings[map_idx].name else {
return Err(e);
};
log::debug!("failed to get build id from process memory ({e}), attempting to retrieve from {}", path.display());
module_reader::read_build_id_from_file(&self.process_inspector, path.as_ref()).map_err(errors::WriterError::ModuleReaderError)
})
.unwrap_or_else(|e| {
log::warn!("failed to get build id for mapping: {e}");
Vec::new()
});
if identifier.is_empty() || identifier.iter().all(|&x| x == 0) {
continue;
}
let soname = self.soname_from_process_memory_for_index(map_idx).ok();
let module = fill_raw_module(
&self.process_inspector,
buffer,
&self.mappings[map_idx],
&identifier,
soname,
)?;
modules.push(module);
}
for user in &self.user_mapping_list {
let module = fill_raw_module(
&self.process_inspector,
buffer,
&user.mapping,
&user.identifier,
None,
)?;
modules.push(module);
}
let list_header = MemoryWriter::<u32>::alloc_with_val(buffer, modules.len() as u32)?;
let mut dirent = MDRawDirectory {
stream_type: MDStreamType::ModuleListStream as u32,
location: list_header.location(),
};
if !modules.is_empty() {
let mapping_list = MemoryArrayWriter::<MDRawModule>::alloc_from_iter(buffer, modules)?;
dirent.location.data_size += mapping_list.location().data_size;
}
Ok(dirent)
}
}
fn fill_raw_module(
process_inspector: &ProcessInspector,
buffer: &mut DumpBuf,
mapping: &MappingInfo,
identifier: &[u8],
soname: Option<String>,
) -> Result<MDRawModule, SectionMappingsError> {
let cv_record = if identifier.is_empty() {
Default::default()
} else {
let cv_signature = crate::minidump_format::format::CvSignature::Elf as u32;
let array_size = std::mem::size_of_val(&cv_signature) + identifier.len();
let mut sig_section = MemoryArrayWriter::<u8>::alloc_array(buffer, array_size)?;
for (index, val) in cv_signature
.to_ne_bytes()
.iter()
.chain(identifier.iter())
.enumerate()
{
sig_section.set_value_at(buffer, *val, index)?;
}
sig_section.location()
};
let (file_path, _, so_version) = mapping
.get_mapping_effective_path_name_and_version(process_inspector, soname)
.map_err(|e| SectionMappingsError::GetEffectivePathError(mapping.clone(), e))?;
let name_header = write_string_to_location(buffer, file_path.to_string_lossy().as_ref())?;
let version_info = so_version.map_or(Default::default(), |sov| format::VS_FIXEDFILEINFO {
signature: format::VS_FFI_SIGNATURE,
struct_version: format::VS_FFI_STRUCVERSION,
file_version_hi: sov.major,
file_version_lo: sov.minor,
product_version_hi: sov.patch,
product_version_lo: sov.prerelease,
..Default::default()
});
let raw_module = MDRawModule {
base_of_image: mapping.start_address as u64,
size_of_image: mapping.size as u32,
cv_record,
module_name_rva: name_header.rva,
version_info,
..Default::default()
};
Ok(raw_module)
}