sentry_debug_images/
images.rs

1use std::env;
2
3use sentry_core::protocol::{DebugImage, SymbolicDebugImage};
4use sentry_core::types::{CodeId, DebugId, Uuid};
5
6use findshlibs::{SharedLibrary, SharedLibraryId, TargetSharedLibrary, TARGET_SUPPORTED};
7
8const UUID_SIZE: usize = 16;
9
10/// Converts an ELF object identifier into a `DebugId`.
11///
12/// The identifier data is first truncated or extended to match 16 byte size of
13/// Uuids. If the data is declared in little endian, the first three Uuid fields
14/// are flipped to match the big endian expected by the breakpad processor.
15///
16/// The `DebugId::appendix` field is always `0` for ELF.
17fn debug_id_from_build_id(build_id: &[u8]) -> Option<DebugId> {
18    let mut data = [0u8; UUID_SIZE];
19    let len = build_id.len().min(UUID_SIZE);
20    data[0..len].copy_from_slice(&build_id[0..len]);
21
22    #[cfg(target_endian = "little")]
23    {
24        // The ELF file targets a little endian architecture. Convert to
25        // network byte order (big endian) to match the Breakpad processor's
26        // expectations. For big endian object files, this is not needed.
27        data[0..4].reverse(); // uuid field 1
28        data[4..6].reverse(); // uuid field 2
29        data[6..8].reverse(); // uuid field 3
30    }
31
32    Uuid::from_slice(&data).map(DebugId::from_uuid).ok()
33}
34
35/// Returns the list of loaded libraries/images.
36pub fn debug_images() -> Vec<DebugImage> {
37    let mut images = vec![];
38    if !TARGET_SUPPORTED {
39        return images;
40    }
41
42    TargetSharedLibrary::each(|shlib| {
43        let maybe_debug_id = shlib.debug_id().and_then(|id| match id {
44            SharedLibraryId::Uuid(bytes) => Some(DebugId::from_uuid(Uuid::from_bytes(bytes))),
45            SharedLibraryId::GnuBuildId(ref id) => debug_id_from_build_id(id),
46            SharedLibraryId::PdbSignature(guid, age) => DebugId::from_guid_age(&guid, age).ok(),
47            _ => None,
48        });
49
50        let debug_id = match maybe_debug_id {
51            Some(debug_id) => debug_id,
52            None => return,
53        };
54
55        let mut name = shlib.name().to_string_lossy().to_string();
56        if name.is_empty() {
57            name = env::current_exe()
58                .map(|x| x.display().to_string())
59                .unwrap_or_else(|_| "<main>".to_string());
60        }
61
62        let code_id = shlib.id().map(|id| CodeId::new(id.to_string()));
63        let debug_name = shlib.debug_name().map(|n| n.to_string_lossy().to_string());
64
65        // For windows, the `virtual_memory_bias` actually returns the real
66        // `module_base`, which is the address that sentry uses for symbolication.
67        // Going via the segments means that the `image_addr` would be offset in
68        // a way that symbolication yields wrong results.
69        let (image_addr, image_vmaddr) = if cfg!(windows) {
70            (shlib.virtual_memory_bias().0.into(), 0.into())
71        } else {
72            (
73                shlib.actual_load_addr().0.into(),
74                shlib.stated_load_addr().0.into(),
75            )
76        };
77
78        images.push(
79            SymbolicDebugImage {
80                id: debug_id,
81                name,
82                arch: None,
83                image_addr,
84                image_size: shlib.len() as u64,
85                image_vmaddr,
86                code_id,
87                debug_file: debug_name,
88            }
89            .into(),
90        );
91    });
92
93    images
94}