|
1 | | -mod object_ext; |
| 1 | +use debugid::DebugId; |
| 2 | +use object::{FileFlags, Object, ObjectSection}; |
| 3 | +use uuid::Uuid; |
2 | 4 |
|
3 | | -pub use object_ext::ObjectExt; |
| 5 | +use samply_debugid::{CodeId, DebugIdExt, ElfBuildId}; |
| 6 | + |
| 7 | +/// Tries to obtain a CodeId for an object. |
| 8 | +/// |
| 9 | +/// This currently only handles mach-O and ELF. |
| 10 | +pub fn code_id_for_object<'data>(obj: &impl Object<'data>) -> Option<CodeId> { |
| 11 | + // ELF |
| 12 | + if let Ok(Some(build_id)) = obj.build_id() { |
| 13 | + return Some(CodeId::ElfBuildId(ElfBuildId::from_bytes(build_id))); |
| 14 | + } |
| 15 | + |
| 16 | + // mach-O |
| 17 | + if let Ok(Some(uuid)) = obj.mach_uuid() { |
| 18 | + return Some(CodeId::MachoUuid(Uuid::from_bytes(uuid))); |
| 19 | + } |
| 20 | + |
| 21 | + None |
| 22 | +} |
| 23 | + |
| 24 | +/// Tries to obtain a DebugId for an object. This uses the build ID, if available, |
| 25 | +/// and falls back to hashing the first page of the text section otherwise. |
| 26 | +/// Returns None on failure. |
| 27 | +pub fn debug_id_for_object<'data>(obj: &impl Object<'data>) -> Option<DebugId> { |
| 28 | + // Windows |
| 29 | + if let Ok(Some(pdb_info)) = obj.pdb_info() { |
| 30 | + return Some(DebugId::from_guid_age(&pdb_info.guid(), pdb_info.age()).unwrap()); |
| 31 | + } |
| 32 | + |
| 33 | + // ELF |
| 34 | + if let Ok(Some(build_id)) = obj.build_id() { |
| 35 | + return Some(DebugId::from_identifier(build_id, obj.is_little_endian())); |
| 36 | + } |
| 37 | + |
| 38 | + // mach-O |
| 39 | + if let Ok(Some(uuid)) = obj.mach_uuid() { |
| 40 | + return Some(DebugId::from_uuid(Uuid::from_bytes(uuid))); |
| 41 | + } |
| 42 | + |
| 43 | + // We were not able to locate a build ID, so fall back to creating a synthetic |
| 44 | + // identifier from a hash of the first page of the ".text" (program code) section. |
| 45 | + if let Some(section) = obj.section_by_name(".text") { |
| 46 | + let data_len = section.size().min(4096); |
| 47 | + if let Ok(Some(first_page_data)) = section.data_range(section.address(), data_len) { |
| 48 | + return Some(DebugId::from_text_first_page( |
| 49 | + first_page_data, |
| 50 | + obj.is_little_endian(), |
| 51 | + )); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + None |
| 56 | +} |
| 57 | + |
| 58 | +/// The "relative address base" is the base address which [`LookupAddress::Relative`] |
| 59 | +/// addresses are relative to. You start with an SVMA (a stated virtual memory address), |
| 60 | +/// you subtract the relative address base, and out comes a relative address. |
| 61 | +/// |
| 62 | +/// This function computes that base address. It is defined as follows: |
| 63 | +/// |
| 64 | +/// - For Windows binaries, the base address is the "image base address". |
| 65 | +/// - For mach-O binaries, the base address is the vmaddr of the __TEXT segment. |
| 66 | +/// - For ELF binaries, the base address is the vmaddr of the *first* segment, |
| 67 | +/// i.e. the vmaddr of the first "LOAD" ELF command. |
| 68 | +/// |
| 69 | +/// In many cases, this base address is simply zero: |
| 70 | +/// |
| 71 | +/// - ELF images of dynamic libraries (i.e. not executables) usually have a |
| 72 | +/// base address of zero. |
| 73 | +/// - Stand-alone mach-O dylibs usually have a base address of zero because their |
| 74 | +/// __TEXT segment is at address zero. |
| 75 | +/// - In PDBs, "RVAs" are relative addresses which are already relative to the |
| 76 | +/// image base. |
| 77 | +/// |
| 78 | +/// However, in the following cases, the base address is usually non-zero: |
| 79 | +/// |
| 80 | +/// - The "image base address" of Windows binaries is usually non-zero. |
| 81 | +/// - mach-O executable files (not dylibs) usually have their __TEXT segment at |
| 82 | +/// address 0x100000000. |
| 83 | +/// - mach-O libraries in the dyld shared cache have a __TEXT segment at some |
| 84 | +/// non-zero address in the cache. |
| 85 | +/// - ELF executables can have non-zero base addresses, e.g. 0x200000 or 0x400000. |
| 86 | +/// - Kernel ELF binaries ("vmlinux") have a large base address such as |
| 87 | +/// 0xffffffff81000000. Moreover, the base address seems to coincide with the |
| 88 | +/// vmaddr of the .text section, which is readily-available in perf.data files |
| 89 | +/// (in a synthetic mapping called "[kernel.kallsyms]_text"). |
| 90 | +pub fn relative_address_base<'data>(obj: &impl Object<'data>) -> u64 { |
| 91 | + use object::read::ObjectSegment; |
| 92 | + if let Some(text_segment) = obj.segments().find(|s| s.name() == Ok(Some("__TEXT"))) { |
| 93 | + // This is a mach-O image. "Relative addresses" are relative to the |
| 94 | + // vmaddr of the __TEXT segment. |
| 95 | + return text_segment.address(); |
| 96 | + } |
| 97 | + |
| 98 | + if let FileFlags::Elf { .. } = obj.flags() { |
| 99 | + // This is an ELF image. "Relative addresses" are relative to the |
| 100 | + // vmaddr of the first segment (the first LOAD command). |
| 101 | + if let Some(first_segment) = obj.segments().next() { |
| 102 | + return first_segment.address(); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + // For PE binaries, relative_address_base() returns the image base address. |
| 107 | + obj.relative_address_base() |
| 108 | +} |
0 commit comments