Skip to content

Commit 071b0c1

Browse files
committed
Remove ObjectExt and make functions free-standing
1 parent 3bdf109 commit 071b0c1

File tree

10 files changed

+134
-153
lines changed

10 files changed

+134
-153
lines changed

samply-object/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
authors = ["Markus Stange <[email protected]>"]
55
license = "MIT OR Apache-2.0"
66
edition = "2021"
7-
description = "Samply object methods."
7+
description = "Samply object functions."
88
repository = "https://github.com/mstange/samply/"
99
readme = "README.md"
1010

samply-object/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# samply-object
22

3-
This crate defines the `ObjectExt` trait which allows getting `DebugId`s,
4-
`CodeId`s and relative base addresses for use with `samply`. Useful for writing
5-
your own profiles to be symbolicated and displayed with `samply load`.
3+
This crate defines the functions for getting `DebugId`s, `CodeId`s and relative
4+
base addresses for use with `samply`. Useful for writing your own profiles to
5+
be symbolicated and displayed with `samply load`.

samply-object/src/lib.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,108 @@
1-
mod object_ext;
1+
use debugid::DebugId;
2+
use object::{FileFlags, Object, ObjectSection};
3+
use uuid::Uuid;
24

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+
}

samply-object/src/object_ext.rs

Lines changed: 0 additions & 122 deletions
This file was deleted.

samply-symbols/src/binary_image.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use linux_perf_data::linux_perf_event_reader::RawData;
44
use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, PeFile, PeFile32, PeFile64};
55
use object::{FileKind, Object, ReadRef};
66
use samply_debugid::{CodeId, ElfBuildId, PeCodeId};
7-
use samply_object::ObjectExt;
7+
use samply_object::{code_id_for_object, debug_id_for_object, relative_address_base};
88

99
use crate::error::Error;
1010
use crate::jitdump::{debug_id_and_code_id_for_jitdump, JitDumpIndex};
@@ -111,7 +111,7 @@ impl<F: FileContents> BinaryImageInner<F> {
111111
let data = file.full_range();
112112
let object = object::File::parse(data)
113113
.map_err(|e| Error::ObjectParseError(*file_kind, e))?;
114-
let debug_id = object.debug_id();
114+
let debug_id = debug_id_for_object(&object);
115115
match file_kind {
116116
FileKind::Pe32 | FileKind::Pe64 => {
117117
let (code_id, debug_path, debug_name) =
@@ -128,13 +128,13 @@ impl<F: FileContents> BinaryImageInner<F> {
128128
}
129129
FileKind::MachO32 | FileKind::MachO64 => {
130130
let macho_data = MachOData::new(file, 0, *file_kind == FileKind::MachO64);
131-
let code_id = object.code_id();
131+
let code_id = code_id_for_object(&object);
132132
let arch = macho_data.get_arch().map(ToOwned::to_owned);
133133
let (debug_path, debug_name) = (path.clone(), name.clone());
134134
(debug_id, code_id, debug_path, debug_name, arch)
135135
}
136136
_ => {
137-
let code_id = object.code_id();
137+
let code_id = code_id_for_object(&object);
138138
let (debug_path, debug_name) = (path.clone(), name.clone());
139139
let arch =
140140
object_arch_to_string(object.architecture()).map(ToOwned::to_owned);
@@ -146,16 +146,16 @@ impl<F: FileContents> BinaryImageInner<F> {
146146
let data = member.data();
147147
let object = object::File::parse(data)
148148
.map_err(|e| Error::ObjectParseError(*file_kind, e))?;
149-
let debug_id = object.debug_id();
150-
let code_id = object.code_id();
149+
let debug_id = debug_id_for_object(&object);
150+
let code_id = code_id_for_object(&object);
151151
let (debug_path, debug_name) = (path.clone(), name.clone());
152152
let arch = member.arch();
153153
(debug_id, code_id, debug_path, debug_name, arch)
154154
}
155155
BinaryImageInner::MemberOfDyldSharedCache(dyld_cache_file_data) => {
156156
let (obj, macho_data) = dyld_cache_file_data.make_object()?.into_parts();
157-
let debug_id = obj.debug_id();
158-
let code_id = obj.code_id();
157+
let debug_id = debug_id_for_object(&obj);
158+
let code_id = code_id_for_object(&obj);
159159
let (debug_path, debug_name) = (path.clone(), name.clone());
160160
let arch = macho_data.get_arch().map(ToOwned::to_owned);
161161
(debug_id, code_id, debug_path, debug_name, arch)
@@ -243,7 +243,7 @@ impl<F: FileContents> BinaryImageInner<F> {
243243

244244
// Translate start_address from a "relative address" into an
245245
// SVMA ("stated virtual memory address").
246-
let image_base = object.samply_relative_address_base();
246+
let image_base = relative_address_base(&object);
247247
let start_svma = image_base + u64::from(start_address);
248248

249249
// Find the section and segment which contains our start_svma.

samply-symbols/src/elf.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use elsa::sync::FrozenVec;
66
use gimli::{CieOrFde, Dwarf, EhFrame, EndianSlice, RunTimeEndian, UnwindSection};
77
use object::{File, FileKind, Object, ObjectSection, ReadRef};
88
use samply_debugid::ElfBuildId;
9-
use samply_object::ObjectExt;
9+
use samply_object::debug_id_for_object;
1010
use yoke::Yoke;
1111
use yoke_derive::Yokeable;
1212

@@ -85,7 +85,7 @@ where
8585
H: FileAndPathHelper,
8686
{
8787
let (name, crc) = elf_file.gnu_debuglink().ok().flatten()?;
88-
let debug_id = elf_file.debug_id()?;
88+
let debug_id = debug_id_for_object(elf_file)?;
8989
let name = std::str::from_utf8(name).ok()?;
9090
let candidate_paths = helper
9191
.get_candidate_paths_for_gnu_debug_link_dest(original_file_location, name)
@@ -327,7 +327,7 @@ impl<'data, T: FileContents + 'static> ElfObjects<'data, T> {
327327
}
328328

329329
fn debug_id_for_object(&self) -> Option<DebugId> {
330-
self.object.debug_id()
330+
debug_id_for_object(&self.object)
331331
}
332332

333333
fn function_addresses(&self) -> (Option<Vec<u32>>, Option<Vec<u32>>) {

samply-symbols/src/macho.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use object::read::macho::{
99
};
1010
use object::read::{File, Object, ObjectSection};
1111
use object::{Endianness, FileKind, ReadRef};
12-
use samply_object::ObjectExt;
12+
use samply_object::debug_id_for_object;
1313
use uuid::Uuid;
1414
use yoke::Yoke;
1515
use yoke_derive::Yokeable;
@@ -405,8 +405,7 @@ impl<T: FileContents + 'static> ObjectSymbolMapOuter<T> for FileDataAndObject<T>
405405
addr2line_context,
406406
} = self.0.get();
407407
let (function_starts, function_ends) = compute_function_addresses_macho(macho_data, object);
408-
let debug_id = object
409-
.debug_id()
408+
let debug_id = debug_id_for_object(object)
410409
.ok_or(Error::InvalidInputError("debug ID cannot be read"))?;
411410
let symbol_map = ObjectSymbolMapInnerWrapper::new(
412411
object,

samply-symbols/src/symbol_map_object.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use gimli::{EndianSlice, RunTimeEndian};
99
use object::{
1010
ObjectMap, ObjectSection, ObjectSegment, SectionFlags, SectionIndex, SectionKind, SymbolKind,
1111
};
12-
use samply_object::ObjectExt;
12+
use samply_object::relative_address_base;
1313
use yoke::Yoke;
1414
use yoke_derive::Yokeable;
1515

@@ -676,7 +676,7 @@ impl<'a, FC: FileContents + 'static> ObjectSymbolMapInnerWrapper<'a, FC> {
676676
Symbol: object::ObjectSymbol<'a> + Send + Sync + 'a,
677677
DDM: DwoDwarfMaker<FC> + Sync,
678678
{
679-
let base_address = object_file.samply_relative_address_base();
679+
let base_address = relative_address_base(object_file);
680680
let list = SymbolList::new(
681681
object_file,
682682
base_address,

0 commit comments

Comments
 (0)