Skip to main content

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}