blazesym/symbolize/source.rs
1//! Definitions of supported symbolization sources.
2
3use std::cmp::min;
4use std::fmt::Debug;
5use std::fmt::Formatter;
6use std::fmt::Result as FmtResult;
7use std::path::PathBuf;
8
9use crate::MaybeDefault;
10use crate::Pid;
11
12#[cfg(doc)]
13use super::Symbolizer;
14
15
16cfg_apk! {
17/// A single APK file.
18///
19/// This type is used in the [`Source::Apk`] variant.
20#[derive(Clone)]
21pub struct Apk {
22 /// The path to an APK file.
23 pub path: PathBuf,
24 /// Whether or not to consult debug symbols to satisfy the request
25 /// (if present).
26 ///
27 /// On top of this runtime configuration, the crate needs to be
28 /// built with the `dwarf` feature to actually consult debug
29 /// symbols. If neither is satisfied, ELF symbols will be used.
30 pub debug_syms: bool,
31 /// The struct is non-exhaustive and open to extension.
32 #[doc(hidden)]
33 pub _non_exhaustive: (),
34}
35
36impl Apk {
37 /// Create a new [`Apk`] object, referencing the provided path.
38 ///
39 /// `debug_syms` defaults to `true` when using this constructor.
40 #[inline]
41 pub fn new(path: impl Into<PathBuf>) -> Self {
42 Self {
43 path: path.into(),
44 debug_syms: true,
45 _non_exhaustive: (),
46 }
47 }
48}
49
50impl From<Apk> for Source<'static> {
51 #[inline]
52 fn from(apk: Apk) -> Self {
53 Self::Apk(apk)
54 }
55}
56
57impl Debug for Apk {
58 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
59 let Self {
60 path,
61 debug_syms: _,
62 _non_exhaustive: (),
63 } = self;
64
65 f.debug_tuple(stringify!(Apk)).field(path).finish()
66 }
67}
68}
69
70
71cfg_breakpad! {
72/// A single Breakpad file.
73///
74/// This type is used in the [`Source::Breakpad`] variant.
75#[derive(Clone)]
76pub struct Breakpad {
77 /// The path to a Breakpad file.
78 pub path: PathBuf,
79 /// The struct is non-exhaustive and open to extension.
80 #[doc(hidden)]
81 pub _non_exhaustive: (),
82}
83
84impl Breakpad {
85 /// Create a new [`Breakpad`] object, referencing the provided path.
86 #[inline]
87 pub fn new(path: impl Into<PathBuf>) -> Self {
88 Self {
89 path: path.into(),
90 _non_exhaustive: (),
91 }
92 }
93}
94
95impl From<Breakpad> for Source<'static> {
96 #[inline]
97 fn from(breakpad: Breakpad) -> Self {
98 Self::Breakpad(breakpad)
99 }
100}
101
102impl Debug for Breakpad {
103 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
104 let Self {
105 path,
106 _non_exhaustive: (),
107 } = self;
108
109 f.debug_tuple(stringify!(Breakpad)).field(path).finish()
110 }
111}
112}
113
114
115/// A single ELF file.
116///
117/// This type is used in the [`Source::Elf`] variant.
118#[derive(Clone)]
119pub struct Elf {
120 /// The path to an ELF file.
121 pub path: PathBuf,
122 /// Whether or not to consult debug symbols to satisfy the request
123 /// (if present).
124 ///
125 /// On top of this runtime configuration, the crate needs to be
126 /// built with the `dwarf` feature to actually consult debug
127 /// symbols. If neither is satisfied, ELF symbols will be used.
128 ///
129 /// Debug symbols are anything DWARF related. This can be DWARF data
130 /// directly embedded in the ELF file, separate DWARF packages
131 /// discovered by adding the `.dwp` extension to `path`, as well as
132 /// data referenced via debug links inside the binary. For the
133 /// latter, the directories that are searched for candidate files
134 /// can be configured via [`Builder::set_debug_dirs`][debug_dirs].
135 ///
136 /// [debug_dirs]: crate::symbolize::Builder::set_debug_dirs
137 pub debug_syms: bool,
138 /// The struct is non-exhaustive and open to extension.
139 #[doc(hidden)]
140 pub _non_exhaustive: (),
141}
142
143impl Elf {
144 /// Create a new [`Elf`] object, referencing the provided path.
145 ///
146 /// `debug_syms` defaults to `true` when using this constructor.
147 #[inline]
148 pub fn new(path: impl Into<PathBuf>) -> Self {
149 Self {
150 path: path.into(),
151 debug_syms: true,
152 _non_exhaustive: (),
153 }
154 }
155}
156
157impl From<Elf> for Source<'static> {
158 #[inline]
159 fn from(elf: Elf) -> Self {
160 Self::Elf(elf)
161 }
162}
163
164impl Debug for Elf {
165 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
166 let Self {
167 path,
168 debug_syms: _,
169 _non_exhaustive: (),
170 } = self;
171
172 f.debug_tuple(stringify!(Elf)).field(path).finish()
173 }
174}
175
176
177/// Configuration for kernel address symbolization.
178///
179/// This type is used in the [`Source::Kernel`] variant.
180#[derive(Clone, Debug, PartialEq)]
181pub struct Kernel {
182 /// The path of a `kallsyms` file to use.
183 ///
184 /// By default, this will refer to `kallsyms` of the running kernel.
185 /// If set to [`None`][MaybeDefault::None] usage of `kallsyms` will
186 /// be disabled. Otherwise the copy at the given path will be used.
187 ///
188 /// If both a `vmlinux` as well as a `kallsyms` file are found,
189 /// `vmlinux` will generally be given preference and `kallsyms` acts
190 /// as a fallback.
191 pub kallsyms: MaybeDefault<PathBuf>,
192 /// The path of the `vmlinux` file to use.
193 ///
194 /// `vmlinux` is generally an uncompressed and unstripped object
195 /// file that is typically used in debugging, profiling, and
196 /// similar use cases.
197 ///
198 /// By default, the library will search for candidates in various
199 /// locations, taking into account the currently running kernel
200 /// version. If set to [`None`][MaybeDefault::None] discovery and
201 /// usage of a vmlinux file will be disabled. Otherwise the copy at
202 /// the given path will be used.
203 ///
204 /// If both a `vmlinux` as well as a `kallsyms` file are found,
205 /// `vmlinux` will generally be given preference and `kallsyms` acts
206 /// as a fallback.
207 pub vmlinux: MaybeDefault<PathBuf>,
208 /// The KASLR offset to use.
209 ///
210 /// Given a value of `None`, the library will attempt to deduce the
211 /// offset itself. Note that this value only has relevance when a
212 /// kernel image is used for symbolization, because `kallsyms` based
213 /// data already include randomization adjusted addresses.
214 pub kaslr_offset: Option<u64>,
215 /// Whether or not to consult debug symbols from `vmlinux` to
216 /// satisfy the request (if present).
217 ///
218 /// On top of this runtime configuration, the crate needs to be
219 /// built with the `dwarf` feature to actually consult debug
220 /// symbols. If either is not satisfied, only ELF symbols will be
221 /// used.
222 pub debug_syms: bool,
223 /// The struct is non-exhaustive and open to extension.
224 #[doc(hidden)]
225 pub _non_exhaustive: (),
226}
227
228impl Default for Kernel {
229 fn default() -> Self {
230 Self {
231 kallsyms: MaybeDefault::Default,
232 vmlinux: MaybeDefault::Default,
233 kaslr_offset: None,
234 debug_syms: true,
235 _non_exhaustive: (),
236 }
237 }
238}
239
240impl From<Kernel> for Source<'static> {
241 #[inline]
242 fn from(kernel: Kernel) -> Self {
243 Self::Kernel(kernel)
244 }
245}
246
247
248/// Configuration for process based address symbolization.
249///
250/// This type is used in the [`Source::Process`] variant.
251///
252/// The corresponding addresses supplied to [`Symbolizer::symbolize`] are
253/// expected to be absolute addresses
254/// ([`Input::AbsAddr`][crate::symbolize::Input::AbsAddr]) as valid within the
255/// process identified by the [`pid`][Process::pid] member.
256///
257/// # Notes
258/// Please note that process symbolization is generally a privileged operation
259/// and may require the granting of additional capabilities compared to other
260/// symbolization sources.
261#[derive(Clone)]
262pub struct Process {
263 /// The referenced process' ID.
264 pub pid: Pid,
265 /// Whether or not to consult debug symbols to satisfy the request
266 /// (if present).
267 ///
268 /// On top of this runtime configuration, the crate needs to be
269 /// built with the `dwarf` feature to actually consult debug
270 /// symbols. If neither is satisfied, ELF symbols will be used.
271 pub debug_syms: bool,
272 /// Whether to incorporate a process' [perf map] file into the
273 /// symbolization procedure.
274 ///
275 /// Perf map files mostly have relevance in just-in-time compiled languages,
276 /// where they provide an interface for the runtime to expose addresses of
277 /// dynamic symbols to profiling tools.
278 ///
279 /// [perf map]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt
280 pub perf_map: bool,
281 /// Whether to work with `/proc/<pid>/map_files/` entries or with
282 /// symbolic paths mentioned in `/proc/<pid>/maps` instead.
283 ///
284 /// `map_files` usage is generally strongly encouraged, as symbolic
285 /// path usage is unlikely to work reliably in mount namespace
286 /// contexts or when files have been deleted from the file system.
287 /// However, by using symbolic paths the need for requiring the
288 /// `SYS_ADMIN` capability is eliminated.
289 pub map_files: bool,
290 /// Whether or not to symbolize addresses in a vDSO (virtual dynamic
291 /// shared object).
292 pub vdso: bool,
293 /// The struct is non-exhaustive and open to extension.
294 #[doc(hidden)]
295 pub _non_exhaustive: (),
296}
297
298impl Process {
299 /// Create a new [`Process`] object using the provided `pid`.
300 ///
301 /// `debug_syms`, `perf_map`, `map_files`, and `vdso` default to
302 /// `true` when using this constructor.
303 #[inline]
304 pub fn new(pid: Pid) -> Self {
305 Self {
306 pid,
307 debug_syms: true,
308 perf_map: true,
309 map_files: true,
310 vdso: true,
311 _non_exhaustive: (),
312 }
313 }
314}
315
316impl Debug for Process {
317 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
318 let Self {
319 pid,
320 debug_syms: _,
321 perf_map: _,
322 map_files: _,
323 vdso: _,
324 _non_exhaustive: (),
325 } = self;
326
327 f.debug_tuple(stringify!(Process))
328 // We use the `Display` representation here.
329 .field(&format_args!("{pid}"))
330 .finish()
331 }
332}
333
334impl From<Process> for Source<'static> {
335 #[inline]
336 fn from(process: Process) -> Self {
337 Self::Process(process)
338 }
339}
340
341
342cfg_gsym! {
343/// Enumeration of supported Gsym sources.
344///
345/// This type is used in the [`Source::Gsym`] variant.
346#[derive(Clone)]
347pub enum Gsym<'dat> {
348 /// "Raw" Gsym data.
349 Data(GsymData<'dat>),
350 /// A Gsym file.
351 File(GsymFile),
352}
353
354impl Debug for Gsym<'_> {
355 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
356 match self {
357 Self::Data(data) => Debug::fmt(data, f),
358 Self::File(file) => Debug::fmt(file, f),
359 }
360 }
361}
362
363impl<'dat> From<Gsym<'dat>> for Source<'dat> {
364 #[inline]
365 fn from(gsym: Gsym<'dat>) -> Self {
366 Self::Gsym(gsym)
367 }
368}
369
370
371/// Gsym data.
372#[derive(Clone)]
373pub struct GsymData<'dat> {
374 /// The "raw" Gsym data.
375 pub data: &'dat [u8],
376 /// The struct is non-exhaustive and open to extension.
377 #[doc(hidden)]
378 pub _non_exhaustive: (),
379}
380
381impl<'dat> GsymData<'dat> {
382 /// Create a new [`GsymData`] object, referencing the provided path.
383 #[inline]
384 pub fn new(data: &'dat [u8]) -> Self {
385 Self {
386 data,
387 _non_exhaustive: (),
388 }
389 }
390}
391
392impl Debug for GsymData<'_> {
393 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
394 let Self {
395 data,
396 _non_exhaustive: (),
397 } = self;
398
399 f.debug_tuple(stringify!(GsymData))
400 .field(&data.get(0..(min(data.len(), 32))).unwrap_or_default())
401 .finish()
402 }
403}
404
405impl<'dat> From<GsymData<'dat>> for Source<'dat> {
406 #[inline]
407 fn from(gsym: GsymData<'dat>) -> Self {
408 Self::Gsym(Gsym::Data(gsym))
409 }
410}
411
412
413/// A Gsym file.
414#[derive(Clone)]
415pub struct GsymFile {
416 /// The path to the Gsym file.
417 pub path: PathBuf,
418 /// The struct is non-exhaustive and open to extension.
419 #[doc(hidden)]
420 pub _non_exhaustive: (),
421}
422
423impl GsymFile {
424 /// Create a new [`GsymFile`] object, referencing the provided path.
425 #[inline]
426 pub fn new(path: impl Into<PathBuf>) -> Self {
427 Self {
428 path: path.into(),
429 _non_exhaustive: (),
430 }
431 }
432}
433
434impl Debug for GsymFile {
435 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
436 let Self {
437 path,
438 _non_exhaustive: (),
439 } = self;
440
441 f.debug_tuple(stringify!(GsymFile)).field(path).finish()
442 }
443}
444
445impl From<GsymFile> for Source<'static> {
446 #[inline]
447 fn from(gsym: GsymFile) -> Self {
448 Self::Gsym(Gsym::File(gsym))
449 }
450}
451}
452
453
454/// The description of a source of symbols and debug information that the
455/// library will consult to satisfy an address symbolization request.
456///
457/// Objects of this type are used first and foremost with the
458/// [`Symbolizer::symbolize`] method.
459#[derive(Clone)]
460#[non_exhaustive]
461pub enum Source<'dat> {
462 /// A single APK file.
463 #[cfg(feature = "apk")]
464 #[cfg_attr(docsrs, doc(cfg(feature = "apk")))]
465 Apk(Apk),
466 /// A single Breakpad file.
467 #[cfg(feature = "breakpad")]
468 #[cfg_attr(docsrs, doc(cfg(feature = "breakpad")))]
469 Breakpad(Breakpad),
470 /// A single ELF file.
471 Elf(Elf),
472 /// Information about the Linux kernel.
473 Kernel(Kernel),
474 /// Information about a process.
475 Process(Process),
476 /// A Gsym file.
477 #[cfg(feature = "gsym")]
478 #[cfg_attr(docsrs, doc(cfg(feature = "gsym")))]
479 Gsym(Gsym<'dat>),
480 #[doc(hidden)]
481 Phantom(&'dat ()),
482}
483
484impl Debug for Source<'_> {
485 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
486 match self {
487 #[cfg(feature = "apk")]
488 Self::Apk(apk) => Debug::fmt(apk, f),
489 #[cfg(feature = "breakpad")]
490 Self::Breakpad(breakpad) => Debug::fmt(breakpad, f),
491 Self::Elf(elf) => Debug::fmt(elf, f),
492 Self::Kernel(kernel) => Debug::fmt(kernel, f),
493 Self::Process(process) => Debug::fmt(process, f),
494 #[cfg(feature = "gsym")]
495 Self::Gsym(gsym) => Debug::fmt(gsym, f),
496 Self::Phantom(()) => unreachable!(),
497 }
498 }
499}
500
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505
506
507 /// Exercise the `Debug` representation of various types.
508 #[test]
509 fn debug_repr() {
510 let apk = Apk::new("/a-path/with/components.apk");
511 assert_eq!(format!("{apk:?}"), "Apk(\"/a-path/with/components.apk\")");
512 let src = Source::from(apk);
513 assert_eq!(format!("{src:?}"), "Apk(\"/a-path/with/components.apk\")");
514
515 let breakpad = Breakpad::new("/a-path/with/components.sym");
516 assert_eq!(
517 format!("{breakpad:?}"),
518 "Breakpad(\"/a-path/with/components.sym\")"
519 );
520 let src = Source::from(breakpad);
521 assert_eq!(
522 format!("{src:?}"),
523 "Breakpad(\"/a-path/with/components.sym\")"
524 );
525
526 let elf = Elf::new("/a-path/with/components.elf");
527 assert_eq!(format!("{elf:?}"), "Elf(\"/a-path/with/components.elf\")");
528 let src = Source::from(elf);
529 assert_eq!(format!("{src:?}"), "Elf(\"/a-path/with/components.elf\")");
530
531 let gsym_data = GsymData::new(b"12345");
532 assert_eq!(format!("{gsym_data:?}"), "GsymData([49, 50, 51, 52, 53])");
533 let gsym = Gsym::Data(gsym_data.clone());
534 assert_eq!(format!("{gsym:?}"), "GsymData([49, 50, 51, 52, 53])");
535
536 let gsym_file = GsymFile::new("/a-path/gsym");
537 assert_eq!(format!("{gsym_file:?}"), "GsymFile(\"/a-path/gsym\")");
538 let gsym = Gsym::File(gsym_file);
539 assert_eq!(format!("{gsym:?}"), "GsymFile(\"/a-path/gsym\")");
540 let src = Source::from(gsym);
541 assert_eq!(format!("{src:?}"), "GsymFile(\"/a-path/gsym\")");
542 let src = Source::from(Gsym::Data(gsym_data));
543 assert_eq!(format!("{src:?}"), "GsymData([49, 50, 51, 52, 53])");
544
545 let kernel = Kernel::default();
546 assert_ne!(format!("{kernel:?}"), "");
547 let src = Source::from(kernel);
548 assert_ne!(format!("{src:?}"), "");
549
550 let process = Process::new(Pid::Slf);
551 assert_eq!(format!("{process:?}"), "Process(self)");
552 let process = Process::new(Pid::from(1234));
553 assert_eq!(format!("{process:?}"), "Process(1234)");
554 let src = Source::from(process);
555 assert_eq!(format!("{src:?}"), "Process(1234)");
556 }
557}