use std::env;
use sentry_core::protocol::{DebugImage, SymbolicDebugImage};
use sentry_core::types::{CodeId, DebugId, Uuid};
use findshlibs::{SharedLibrary, SharedLibraryId, TargetSharedLibrary, TARGET_SUPPORTED};
const UUID_SIZE: usize = 16;
/// Converts an ELF object identifier into a `DebugId`.
///
/// The identifier data is first truncated or extended to match 16 byte size of
/// Uuids. If the data is declared in little endian, the first three Uuid fields
/// are flipped to match the big endian expected by the breakpad processor.
///
/// The `DebugId::appendix` field is always `0` for ELF.
fn debug_id_from_build_id(build_id: &[u8]) -> Option<DebugId> {
let mut data = [0u8; UUID_SIZE];
let len = build_id.len().min(UUID_SIZE);
data[0..len].copy_from_slice(&build_id[0..len]);
#[cfg(target_endian = "little")]
{
// The ELF file targets a little endian architecture. Convert to
// network byte order (big endian) to match the Breakpad processor's
// expectations. For big endian object files, this is not needed.
data[0..4].reverse(); // uuid field 1
data[4..6].reverse(); // uuid field 2
data[6..8].reverse(); // uuid field 3
}
Uuid::from_slice(&data).map(DebugId::from_uuid).ok()
}
/// Returns the list of loaded libraries/images.
pub fn debug_images() -> Vec<DebugImage> {
let mut images = vec![];
if !TARGET_SUPPORTED {
return images;
}
TargetSharedLibrary::each(|shlib| {
let maybe_debug_id = shlib.debug_id().and_then(|id| match id {
SharedLibraryId::Uuid(bytes) => Some(DebugId::from_uuid(Uuid::from_bytes(bytes))),
SharedLibraryId::GnuBuildId(ref id) => debug_id_from_build_id(id),
SharedLibraryId::PdbSignature(guid, age) => DebugId::from_guid_age(&guid, age).ok(),
_ => None,
});
let debug_id = match maybe_debug_id {
Some(debug_id) => debug_id,
None => return,
};
let mut name = shlib.name().to_string_lossy().to_string();
if name.is_empty() {
name = env::current_exe()
.map(|x| x.display().to_string())
.unwrap_or_else(|_| "<main>".to_string());
}
let code_id = shlib.id().map(|id| CodeId::new(format!("{}", id)));
let debug_name = shlib.debug_name().map(|n| n.to_string_lossy().to_string());
// For windows, the `virtual_memory_bias` actually returns the real
// `module_base`, which is the address that sentry uses for symbolication.
// Going via the segments means that the `image_addr` would be offset in
// a way that symbolication yields wrong results.
let (image_addr, image_vmaddr) = if cfg!(windows) {
(shlib.virtual_memory_bias().0.into(), 0.into())
} else {
(
shlib.actual_load_addr().0.into(),
shlib.stated_load_addr().0.into(),
)
};
images.push(
SymbolicDebugImage {
id: debug_id,
name,
arch: None,
image_addr,
image_size: shlib.len() as u64,
image_vmaddr,
code_id,
debug_file: debug_name,
}
.into(),
);
});
images
}