blazesym_c/
symbolize.rs

1use std::alloc::alloc;
2use std::alloc::dealloc;
3use std::alloc::Layout;
4use std::ffi::CStr;
5use std::ffi::OsStr;
6use std::fmt::Debug;
7use std::mem;
8use std::ops::Deref as _;
9use std::os::raw::c_char;
10use std::os::unix::ffi::OsStrExt as _;
11use std::path::Path;
12use std::path::PathBuf;
13use std::ptr;
14
15use blazesym::symbolize::cache;
16use blazesym::symbolize::source::Elf;
17use blazesym::symbolize::source::GsymData;
18use blazesym::symbolize::source::GsymFile;
19use blazesym::symbolize::source::Kernel;
20use blazesym::symbolize::source::Process;
21use blazesym::symbolize::source::Source;
22use blazesym::symbolize::CodeInfo;
23use blazesym::symbolize::Input;
24use blazesym::symbolize::Reason;
25use blazesym::symbolize::Sym;
26use blazesym::symbolize::Symbolized;
27use blazesym::symbolize::Symbolizer;
28use blazesym::Addr;
29use blazesym::MaybeDefault;
30
31use crate::blaze_err;
32#[cfg(doc)]
33use crate::blaze_err_last;
34use crate::set_last_err;
35use crate::util::slice_from_aligned_user_array;
36use crate::util::slice_from_user_array;
37use crate::util::DynSize as _;
38
39
40/// Configuration for caching of ELF symbolization data.
41#[repr(C)]
42#[derive(Debug)]
43pub struct blaze_cache_src_elf {
44    /// The size of this object's type.
45    ///
46    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
47    /// ensure compatibility in the presence of member additions.
48    pub type_size: usize,
49    /// The path to the ELF file.
50    pub path: *const c_char,
51    /// Unused member available for future expansion. Must be initialized
52    /// to zero.
53    pub reserved: [u8; 16],
54}
55
56impl Default for blaze_cache_src_elf {
57    fn default() -> Self {
58        Self {
59            type_size: mem::size_of::<Self>(),
60            path: ptr::null(),
61            reserved: [0; 16],
62        }
63    }
64}
65
66impl From<blaze_cache_src_elf> for cache::Elf {
67    fn from(elf: blaze_cache_src_elf) -> Self {
68        let blaze_cache_src_elf {
69            type_size: _,
70            path,
71            reserved: _,
72        } = elf;
73        Self {
74            path: unsafe { from_cstr(path) },
75            _non_exhaustive: (),
76        }
77    }
78}
79
80
81/// Configuration for caching of process-level data.
82#[repr(C)]
83#[derive(Debug)]
84pub struct blaze_cache_src_process {
85    /// The size of this object's type.
86    ///
87    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
88    /// ensure compatibility in the presence of member additions.
89    pub type_size: usize,
90    /// The referenced process' ID.
91    pub pid: u32,
92    /// Whether to cache the process' VMAs for later use.
93    ///
94    /// Caching VMAs can be useful, because it conceptually enables the
95    /// library to serve a symbolization request targeting a process
96    /// even if said process has since exited the system.
97    ///
98    /// Note that once VMAs have been cached this way, the library will
99    /// refrain from re-reading updated VMAs unless instructed to.
100    /// Hence, if you have reason to believe that a process may have
101    /// changed its memory regions (by loading a new shared object, for
102    /// example), you would have to make another request to cache them
103    /// yourself.
104    ///
105    /// Note furthermore that if you cache VMAs to later symbolize
106    /// addresses after the original process has already exited, you
107    /// will have to opt-out of usage of `/proc/<pid>/map_files/` as
108    /// part of the symbolization request. Refer to
109    /// [`blaze_symbolize_src_process::no_map_files`].
110    pub cache_vmas: bool,
111    /// Unused member available for future expansion. Must be initialized
112    /// to zero.
113    pub reserved: [u8; 19],
114}
115
116impl Default for blaze_cache_src_process {
117    fn default() -> Self {
118        Self {
119            type_size: mem::size_of::<Self>(),
120            pid: 0,
121            cache_vmas: false,
122            reserved: [0; 19],
123        }
124    }
125}
126
127impl From<blaze_cache_src_process> for cache::Process {
128    fn from(process: blaze_cache_src_process) -> Self {
129        let blaze_cache_src_process {
130            type_size: _,
131            pid,
132            cache_vmas,
133            reserved: _,
134        } = process;
135        Self {
136            pid: pid.into(),
137            cache_vmas,
138            _non_exhaustive: (),
139        }
140    }
141}
142
143
144/// The parameters to load symbols and debug information from an ELF.
145///
146/// Describes the path and address of an ELF file loaded in a
147/// process.
148#[repr(C)]
149#[derive(Debug)]
150pub struct blaze_symbolize_src_elf {
151    /// The size of this object's type.
152    ///
153    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
154    /// ensure compatibility in the presence of member additions.
155    pub type_size: usize,
156    /// The path to the ELF file.
157    ///
158    /// The referenced file may be an executable or shared object. For example,
159    /// passing "/bin/sh" will load symbols and debug information from `sh` and
160    /// passing "/lib/libc.so.xxx" will load symbols and debug information from
161    /// libc.
162    pub path: *const c_char,
163    /// Whether or not to consult debug symbols to satisfy the request
164    /// (if present).
165    pub debug_syms: bool,
166    /// Unused member available for future expansion. Must be initialized
167    /// to zero.
168    pub reserved: [u8; 23],
169}
170
171impl Default for blaze_symbolize_src_elf {
172    fn default() -> Self {
173        Self {
174            type_size: mem::size_of::<Self>(),
175            path: ptr::null(),
176            debug_syms: false,
177            reserved: [0; 23],
178        }
179    }
180}
181
182impl From<blaze_symbolize_src_elf> for Elf {
183    fn from(elf: blaze_symbolize_src_elf) -> Self {
184        let blaze_symbolize_src_elf {
185            type_size: _,
186            path,
187            debug_syms,
188            reserved: _,
189        } = elf;
190        Self {
191            path: unsafe { from_cstr(path) },
192            debug_syms,
193            _non_exhaustive: (),
194        }
195    }
196}
197
198
199/// The parameters to load symbols and debug information from a kernel.
200#[repr(C)]
201#[derive(Debug)]
202pub struct blaze_symbolize_src_kernel {
203    /// The size of this object's type.
204    ///
205    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
206    /// ensure compatibility in the presence of member additions.
207    pub type_size: usize,
208    /// The path of a `kallsyms` file to use.
209    ///
210    /// When `NULL`, this will refer to `kallsyms` of the running kernel.
211    /// If set to `'\0'` (`""`) usage of `kallsyms` will be disabled.
212    /// Otherwise the copy at the given path will be used.
213    ///
214    /// If both a `vmlinux` as well as a `kallsyms` file are found,
215    /// `vmlinux` will generally be given preference and `kallsyms` acts
216    /// as a fallback.
217    pub kallsyms: *const c_char,
218    /// The path of the `vmlinux` file to use.
219    ///
220    /// `vmlinux` is generally an uncompressed and unstripped object
221    /// file that is typically used in debugging, profiling, and
222    /// similar use cases.
223    ///
224    /// When `NULL`, the library will search for `vmlinux` candidates in
225    /// various locations, taking into account the currently running
226    /// kernel version. If set to `'\0'` (`""`) discovery and usage of a
227    /// `vmlinux` will be disabled. Otherwise the copy at the given path
228    /// will be used.
229    ///
230    /// If both a `vmlinux` as well as a `kallsyms` file are found,
231    /// `vmlinux` will generally be given preference and `kallsyms` acts
232    /// as a fallback.
233    pub vmlinux: *const c_char,
234    /// Whether or not to consult debug symbols from `vmlinux` to
235    /// satisfy the request (if present).
236    pub debug_syms: bool,
237    /// Unused member available for future expansion. Must be initialized
238    /// to zero.
239    pub reserved: [u8; 23],
240}
241
242impl Default for blaze_symbolize_src_kernel {
243    fn default() -> Self {
244        Self {
245            type_size: mem::size_of::<Self>(),
246            kallsyms: ptr::null(),
247            vmlinux: ptr::null(),
248            debug_syms: false,
249            reserved: [0; 23],
250        }
251    }
252}
253
254impl From<blaze_symbolize_src_kernel> for Kernel {
255    fn from(kernel: blaze_symbolize_src_kernel) -> Self {
256        fn to_maybe_path(path: *const c_char) -> MaybeDefault<PathBuf> {
257            if !path.is_null() {
258                let path = unsafe { from_cstr(path) };
259                if path.as_os_str().is_empty() {
260                    MaybeDefault::None
261                } else {
262                    MaybeDefault::Some(path)
263                }
264            } else {
265                MaybeDefault::Default
266            }
267        }
268
269        let blaze_symbolize_src_kernel {
270            type_size: _,
271            kallsyms,
272            vmlinux,
273            debug_syms,
274            reserved: _,
275        } = kernel;
276        Self {
277            kallsyms: to_maybe_path(kallsyms),
278            vmlinux: to_maybe_path(vmlinux),
279            kaslr_offset: None,
280            debug_syms,
281            _non_exhaustive: (),
282        }
283    }
284}
285
286
287/// The parameters to load symbols and debug information from a process.
288///
289/// Load all ELF files in a process as the sources of symbols and debug
290/// information.
291#[repr(C)]
292#[derive(Debug)]
293pub struct blaze_symbolize_src_process {
294    /// The size of this object's type.
295    ///
296    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
297    /// ensure compatibility in the presence of member additions.
298    pub type_size: usize,
299    /// The referenced process' ID.
300    pub pid: u32,
301    /// Whether or not to consult debug symbols to satisfy the request
302    /// (if present).
303    pub debug_syms: bool,
304    /// Whether to incorporate a process' perf map file into the symbolization
305    /// procedure.
306    pub perf_map: bool,
307    /// Whether to work with `/proc/<pid>/map_files/` entries or with
308    /// symbolic paths mentioned in `/proc/<pid>/maps` instead.
309    ///
310    /// `no_map_files` usage is generally discouraged, as symbolic paths
311    /// are unlikely to work reliably in mount namespace contexts or
312    /// when files have been deleted from the file system. However, by
313    /// using symbolic paths (i.e., with `no_map_files` being `true`)
314    /// the need for requiring the `SYS_ADMIN` capability is eliminated.
315    pub no_map_files: bool,
316    /// Whether or not to symbolize addresses in a vDSO (virtual dynamic
317    /// shared object).
318    pub no_vdso: bool,
319    /// Unused member available for future expansion. Must be initialized
320    /// to zero.
321    pub reserved: [u8; 16],
322}
323
324impl Default for blaze_symbolize_src_process {
325    fn default() -> Self {
326        Self {
327            type_size: mem::size_of::<Self>(),
328            pid: 0,
329            debug_syms: false,
330            perf_map: false,
331            no_map_files: false,
332            no_vdso: false,
333            reserved: [0; 16],
334        }
335    }
336}
337
338impl From<blaze_symbolize_src_process> for Process {
339    fn from(process: blaze_symbolize_src_process) -> Self {
340        let blaze_symbolize_src_process {
341            type_size: _,
342            pid,
343            debug_syms,
344            perf_map,
345            no_map_files,
346            no_vdso,
347            reserved: _,
348        } = process;
349        Self {
350            pid: pid.into(),
351            debug_syms,
352            perf_map,
353            map_files: !no_map_files,
354            vdso: !no_vdso,
355            _non_exhaustive: (),
356        }
357    }
358}
359
360
361/// The parameters to load symbols and debug information from "raw" Gsym data.
362#[repr(C)]
363#[derive(Debug)]
364pub struct blaze_symbolize_src_gsym_data {
365    /// The size of this object's type.
366    ///
367    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
368    /// ensure compatibility in the presence of member additions.
369    pub type_size: usize,
370    /// The Gsym data.
371    pub data: *const u8,
372    /// The size of the Gsym data.
373    pub data_len: usize,
374    /// Unused member available for future expansion. Must be initialized
375    /// to zero.
376    pub reserved: [u8; 16],
377}
378
379impl Default for blaze_symbolize_src_gsym_data {
380    fn default() -> Self {
381        Self {
382            type_size: mem::size_of::<Self>(),
383            data: ptr::null(),
384            data_len: 0,
385            reserved: [0; 16],
386        }
387    }
388}
389
390impl From<blaze_symbolize_src_gsym_data> for GsymData<'_> {
391    fn from(gsym: blaze_symbolize_src_gsym_data) -> Self {
392        let blaze_symbolize_src_gsym_data {
393            type_size: _,
394            data,
395            data_len,
396            reserved: _,
397        } = gsym;
398        Self {
399            data: unsafe { slice_from_aligned_user_array(data, data_len) },
400            _non_exhaustive: (),
401        }
402    }
403}
404
405
406/// The parameters to load symbols and debug information from a Gsym file.
407#[repr(C)]
408#[derive(Debug)]
409pub struct blaze_symbolize_src_gsym_file {
410    /// The size of this object's type.
411    ///
412    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
413    /// ensure compatibility in the presence of member additions.
414    pub type_size: usize,
415    /// The path to a gsym file.
416    pub path: *const c_char,
417    /// Unused member available for future expansion. Must be initialized
418    /// to zero.
419    pub reserved: [u8; 16],
420}
421
422impl Default for blaze_symbolize_src_gsym_file {
423    fn default() -> Self {
424        Self {
425            type_size: mem::size_of::<Self>(),
426            path: ptr::null(),
427            reserved: [0; 16],
428        }
429    }
430}
431
432impl From<blaze_symbolize_src_gsym_file> for GsymFile {
433    fn from(gsym: blaze_symbolize_src_gsym_file) -> Self {
434        let blaze_symbolize_src_gsym_file {
435            type_size: _,
436            path,
437            reserved: _,
438        } = gsym;
439        Self {
440            path: unsafe { from_cstr(path) },
441            _non_exhaustive: (),
442        }
443    }
444}
445
446
447/// C ABI compatible version of [`blazesym::symbolize::Symbolizer`].
448///
449/// It is returned by [`blaze_symbolizer_new`] and should be free by
450/// [`blaze_symbolizer_free`].
451pub type blaze_symbolizer = Symbolizer;
452
453
454/// The reason why symbolization failed.
455///
456/// The reason is generally only meant as a hint. Reasons reported may
457/// change over time and, hence, should not be relied upon for the
458/// correctness of the application.
459#[repr(transparent)]
460#[derive(Copy, Clone, Debug, Eq, PartialEq)]
461pub struct blaze_symbolize_reason(u8);
462
463// Please adjust `blaze_normalize_reason` as well when adding a new
464// variant.
465impl blaze_symbolize_reason {
466    /// Symbolization was successful.
467    pub const SUCCESS: blaze_symbolize_reason = blaze_symbolize_reason(0);
468    /// The absolute address was not found in the corresponding process'
469    /// virtual memory map.
470    pub const UNMAPPED: blaze_symbolize_reason = blaze_symbolize_reason(1);
471    /// The file offset does not map to a valid piece of code/data.
472    pub const INVALID_FILE_OFFSET: blaze_symbolize_reason = blaze_symbolize_reason(2);
473    /// The `/proc/<pid>/maps` entry corresponding to the address does
474    /// not have a component (file system path, object, ...) associated
475    /// with it.
476    pub const MISSING_COMPONENT: blaze_symbolize_reason = blaze_symbolize_reason(3);
477    /// The symbolization source has no or no relevant symbols.
478    pub const MISSING_SYMS: blaze_symbolize_reason = blaze_symbolize_reason(4);
479    /// The address could not be found in the symbolization source.
480    pub const UNKNOWN_ADDR: blaze_symbolize_reason = blaze_symbolize_reason(5);
481    /// The address belonged to an entity that is currently unsupported.
482    pub const UNSUPPORTED: blaze_symbolize_reason = blaze_symbolize_reason(6);
483    /// An error prevented the symbolization of the address from succeeding.
484    pub const IGNORED_ERROR: blaze_symbolize_reason = blaze_symbolize_reason(7);
485}
486
487
488impl From<Reason> for blaze_symbolize_reason {
489    fn from(reason: Reason) -> Self {
490        match reason {
491            Reason::Unmapped => blaze_symbolize_reason::UNMAPPED,
492            Reason::InvalidFileOffset => blaze_symbolize_reason::INVALID_FILE_OFFSET,
493            Reason::MissingComponent => blaze_symbolize_reason::MISSING_COMPONENT,
494            Reason::MissingSyms => blaze_symbolize_reason::MISSING_SYMS,
495            Reason::Unsupported => blaze_symbolize_reason::UNSUPPORTED,
496            Reason::UnknownAddr => blaze_symbolize_reason::UNKNOWN_ADDR,
497            Reason::IgnoredError => blaze_symbolize_reason::IGNORED_ERROR,
498            _ => unreachable!(),
499        }
500    }
501}
502
503
504/// Retrieve a textual representation of the reason of a symbolization
505/// failure.
506#[no_mangle]
507pub extern "C" fn blaze_symbolize_reason_str(reason: blaze_symbolize_reason) -> *const c_char {
508    match reason {
509        blaze_symbolize_reason::SUCCESS => b"success\0".as_ptr().cast(),
510        blaze_symbolize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
511        blaze_symbolize_reason::INVALID_FILE_OFFSET => {
512            Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
513        }
514        blaze_symbolize_reason::MISSING_COMPONENT => {
515            Reason::MissingComponent.as_bytes().as_ptr().cast()
516        }
517        blaze_symbolize_reason::MISSING_SYMS => Reason::MissingSyms.as_bytes().as_ptr().cast(),
518        blaze_symbolize_reason::UNKNOWN_ADDR => Reason::UnknownAddr.as_bytes().as_ptr().cast(),
519        blaze_symbolize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
520        blaze_symbolize_reason::IGNORED_ERROR => Reason::IgnoredError.as_bytes().as_ptr().cast(),
521        _ => b"unknown reason\0".as_ptr().cast(),
522    }
523}
524
525
526/// Source code location information for a symbol or inlined function.
527#[repr(C)]
528#[derive(Debug)]
529pub struct blaze_symbolize_code_info {
530    /// The directory in which the source file resides.
531    ///
532    /// This attribute is optional and may be NULL.
533    pub dir: *const c_char,
534    /// The file that defines the symbol.
535    ///
536    /// This attribute is optional and may be NULL.
537    pub file: *const c_char,
538    /// The line number on which the symbol is located in the source
539    /// code.
540    pub line: u32,
541    /// The column number of the symbolized instruction in the source
542    /// code.
543    pub column: u16,
544    /// Unused member available for future expansion.
545    pub reserved: [u8; 10],
546}
547
548
549/// Data about an inlined function call.
550#[repr(C)]
551#[derive(Debug)]
552pub struct blaze_symbolize_inlined_fn {
553    /// The symbol name of the inlined function.
554    pub name: *const c_char,
555    /// Source code location information for the inlined function.
556    pub code_info: blaze_symbolize_code_info,
557    /// Unused member available for future expansion.
558    pub reserved: [u8; 8],
559}
560
561
562/// The result of symbolization of an address.
563///
564/// A `blaze_sym` is the information of a symbol found for an
565/// address.
566#[repr(C)]
567#[derive(Debug)]
568pub struct blaze_sym {
569    /// The symbol name is where the given address should belong to.
570    ///
571    /// If an address could not be symbolized, this member will be NULL.
572    /// Check the `reason` member for additional information pertaining
573    /// the failure.
574    pub name: *const c_char,
575    /// The path to or name of the module containing the symbol.
576    ///
577    /// Typically this would be the path to a executable or shared
578    /// object. Depending on the symbol source this member may not be
579    /// present or it could also just be a file name without path. In
580    /// case of an ELF file contained inside an APK, this will be an
581    /// Android style path of the form `<apk>!<elf-in-apk>`. E.g.,
582    /// `/root/test.apk!/lib/libc.so`.
583    pub module: *const c_char,
584    /// The address at which the symbol is located (i.e., its "start").
585    ///
586    /// This is the "normalized" address of the symbol, as present in
587    /// the file (and reported by tools such as `readelf(1)`,
588    /// `llvm-gsymutil`, or similar).
589    pub addr: Addr,
590    /// The byte offset of the address that got symbolized from the
591    /// start of the symbol (i.e., from `addr`).
592    ///
593    /// E.g., when symbolizing address 0x1337 of a function that starts at
594    /// 0x1330, the offset will be set to 0x07 (and `addr` will be 0x1330). This
595    /// member is especially useful in contexts when input addresses are not
596    /// already normalized, such as when symbolizing an address in a process
597    /// context (which may have been relocated and/or have layout randomizations
598    /// applied).
599    pub offset: usize,
600    /// The size of the symbol.
601    ///
602    /// If the symbol's size is not available, this member will be `-1`.
603    /// Note that some symbol sources may not distinguish between
604    /// "unknown" size and `0`. In that case the size will be reported
605    /// as `0` here as well.
606    pub size: isize,
607    /// Source code location information for the symbol.
608    pub code_info: blaze_symbolize_code_info,
609    /// The number of symbolized inlined function calls present.
610    pub inlined_cnt: usize,
611    /// An array of `inlined_cnt` symbolized inlined function calls.
612    pub inlined: *const blaze_symbolize_inlined_fn,
613    /// On error (i.e., if `name` is NULL), a reason trying to explain
614    /// why symbolization failed.
615    pub reason: blaze_symbolize_reason,
616    /// Unused member available for future expansion.
617    pub reserved: [u8; 15],
618}
619
620/// `blaze_syms` is the result of symbolization of a list of addresses.
621///
622/// Instances of [`blaze_syms`] are returned by any of the `blaze_symbolize_*`
623/// variants. They should be freed by calling [`blaze_syms_free`].
624#[repr(C)]
625#[derive(Debug)]
626pub struct blaze_syms {
627    /// The number of symbols being reported.
628    pub cnt: usize,
629    /// The symbols corresponding to input addresses.
630    ///
631    /// Symbolization happens based on the ordering of (input) addresses.
632    /// Therefore, every input address has an associated symbol.
633    pub syms: [blaze_sym; 0],
634}
635
636/// Create a `PathBuf` from a pointer of C string
637///
638/// # Safety
639/// The provided `cstr` should be terminated with a NUL byte.
640pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
641    Path::new(OsStr::from_bytes(
642        unsafe { CStr::from_ptr(cstr) }.to_bytes(),
643    ))
644    .to_path_buf()
645}
646
647
648/// Options for configuring [`blaze_symbolizer`] objects.
649#[repr(C)]
650#[derive(Debug)]
651pub struct blaze_symbolizer_opts {
652    /// The size of this object's type.
653    ///
654    /// Make sure to initialize it to `sizeof(<type>)`. This member is used to
655    /// ensure compatibility in the presence of member additions.
656    pub type_size: usize,
657    /// Array of debug directories to search for split debug information.
658    ///
659    /// These directories will be consulted (in given order) when resolving
660    /// debug links in binaries. By default and when this member is NULL,
661    /// `/usr/lib/debug` and `/lib/debug/` will be searched. Setting an array
662    /// here will overwrite these defaults, so make sure to include these
663    /// directories as desired.
664    ///
665    /// Note that the directory containing a symbolization source is always an
666    /// implicit candidate target directory of the highest precedence.
667    pub debug_dirs: *const *const c_char,
668    /// The number of array elements in [`debug_dirs`][Self::debug_dirs].
669    pub debug_dirs_len: usize,
670    /// Whether or not to automatically reload file system based
671    /// symbolization sources that were updated since the last
672    /// symbolization operation.
673    pub auto_reload: bool,
674    /// Whether to attempt to gather source code location information.
675    ///
676    /// This option only has an effect if `debug_syms` of the particular
677    /// symbol source is set to `true`. Furthermore, it is a necessary
678    /// prerequisite for retrieving inlined function information (see
679    /// [`inlined_fns`][Self::inlined_fns]).
680    pub code_info: bool,
681    /// Whether to report inlined functions as part of symbolization.
682    ///
683    /// This option only has an effect if [`code_info`][Self::code_info]
684    /// is `true`.
685    pub inlined_fns: bool,
686    /// Whether or not to transparently demangle symbols.
687    ///
688    /// Demangling happens on a best-effort basis. Currently supported
689    /// languages are Rust and C++ and the flag will have no effect if
690    /// the underlying language does not mangle symbols (such as C).
691    pub demangle: bool,
692    /// Unused member available for future expansion. Must be initialized
693    /// to zero.
694    pub reserved: [u8; 20],
695}
696
697impl Default for blaze_symbolizer_opts {
698    fn default() -> Self {
699        Self {
700            type_size: mem::size_of::<Self>(),
701            debug_dirs: ptr::null(),
702            debug_dirs_len: 0,
703            auto_reload: false,
704            code_info: false,
705            inlined_fns: false,
706            demangle: false,
707            reserved: [0; 20],
708        }
709    }
710}
711
712
713/// Create an instance of a symbolizer.
714///
715/// C ABI compatible version of [`blazesym::symbolize::Symbolizer::new()`].
716/// Please refer to its documentation for the default configuration in use.
717///
718/// On success, the function creates a new [`blaze_symbolizer`] object
719/// and returns it. The resulting object should be released using
720/// [`blaze_symbolizer_free`] once it is no longer needed.
721///
722/// On error, the function returns `NULL` and sets the thread's last error to
723/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
724/// error.
725#[no_mangle]
726pub extern "C" fn blaze_symbolizer_new() -> *mut blaze_symbolizer {
727    let symbolizer = Symbolizer::new();
728    let symbolizer_box = Box::new(symbolizer);
729    let () = set_last_err(blaze_err::OK);
730    Box::into_raw(symbolizer_box)
731}
732
733/// Create an instance of a symbolizer with configurable options.
734///
735/// On success, the function creates a new [`blaze_symbolizer`] object
736/// and returns it. The resulting object should be released using
737/// [`blaze_symbolizer_free`] once it is no longer needed.
738///
739/// On error, the function returns `NULL` and sets the thread's last error to
740/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
741/// error.
742///
743/// # Safety
744/// - `opts` needs to point to a valid [`blaze_symbolizer_opts`] object
745#[no_mangle]
746pub unsafe extern "C" fn blaze_symbolizer_new_opts(
747    opts: *const blaze_symbolizer_opts,
748) -> *mut blaze_symbolizer {
749    if !input_zeroed!(opts, blaze_symbolizer_opts) {
750        let () = set_last_err(blaze_err::INVALID_INPUT);
751        return ptr::null_mut()
752    }
753    let opts = input_sanitize!(opts, blaze_symbolizer_opts);
754
755    let blaze_symbolizer_opts {
756        type_size: _,
757        debug_dirs,
758        debug_dirs_len: _debug_dirs_len,
759        auto_reload,
760        code_info,
761        inlined_fns,
762        demangle,
763        reserved: _,
764    } = opts;
765
766    let builder = Symbolizer::builder()
767        .enable_auto_reload(auto_reload)
768        .enable_code_info(code_info)
769        .enable_inlined_fns(inlined_fns)
770        .enable_demangling(demangle);
771
772    let builder = if debug_dirs.is_null() {
773        builder
774    } else {
775        #[cfg(feature = "dwarf")]
776        {
777            // SAFETY: The caller ensures that the pointer is valid and the count
778            //         matches.
779            let slice = unsafe { slice_from_user_array(debug_dirs, _debug_dirs_len) };
780            let iter = slice.iter().map(|cstr| {
781                Path::new(OsStr::from_bytes(
782                    // SAFETY: The caller ensures that valid C strings are
783                    //         provided.
784                    unsafe { CStr::from_ptr(cstr.cast()) }.to_bytes(),
785                ))
786            });
787
788            builder.set_debug_dirs(Some(iter))
789        }
790
791        #[cfg(not(feature = "dwarf"))]
792        {
793            builder
794        }
795    };
796
797    let symbolizer = builder.build();
798    let symbolizer_box = Box::new(symbolizer);
799    let () = set_last_err(blaze_err::OK);
800    Box::into_raw(symbolizer_box)
801}
802
803/// Free an instance of blazesym a symbolizer for C API.
804///
805/// # Safety
806/// The pointer must have been returned by [`blaze_symbolizer_new`] or
807/// [`blaze_symbolizer_new_opts`].
808#[no_mangle]
809pub unsafe extern "C" fn blaze_symbolizer_free(symbolizer: *mut blaze_symbolizer) {
810    if !symbolizer.is_null() {
811        drop(unsafe { Box::from_raw(symbolizer) });
812    }
813}
814
815
816/// Cache an ELF symbolization source.
817///
818/// Cache symbolization data of an ELF file.
819///
820/// The function sets the thread's last error to either
821/// [`blaze_err::OK`] to indicate success or a different error code
822/// associated with the problem encountered. Use [`blaze_err_last`] to
823/// retrieve this error.
824///
825/// # Safety
826/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
827/// - `cache` needs to point to a valid [`blaze_cache_src_process`] object
828#[no_mangle]
829pub unsafe extern "C" fn blaze_symbolize_cache_elf(
830    symbolizer: *mut blaze_symbolizer,
831    cache: *const blaze_cache_src_elf,
832) {
833    if !input_zeroed!(cache, blaze_cache_src_elf) {
834        let () = set_last_err(blaze_err::INVALID_INPUT);
835        return
836    }
837    let cache = input_sanitize!(cache, blaze_cache_src_elf);
838    let cache = cache::Cache::from(cache::Elf::from(cache));
839
840    // SAFETY: The caller ensures that the pointer is valid.
841    let symbolizer = unsafe { &*symbolizer };
842    let result = symbolizer.cache(&cache);
843    let err = result
844        .map(|()| blaze_err::OK)
845        .unwrap_or_else(|err| err.kind().into());
846    let () = set_last_err(err);
847}
848
849
850/// Cache VMA meta data associated with a process.
851///
852/// Cache VMA meta data associated with a process. This will speed up
853/// subsequent symbolization requests while also enabling symbolization
854/// of addresses belonging to processes that exited after being cache
855/// this way.
856///
857/// If this method fails, any previously cached data is left untouched
858/// and will be used subsequently as if no failure occurred. Put
859/// differently, this method is only effectful on the happy path.
860///
861/// The function sets the thread's last error to either
862/// [`blaze_err::OK`] to indicate success or a different error code
863/// associated with the problem encountered. Use [`blaze_err_last`] to
864/// retrieve this error.
865///
866/// # Safety
867/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
868/// - `cache` needs to point to a valid [`blaze_cache_src_process`] object
869#[no_mangle]
870pub unsafe extern "C" fn blaze_symbolize_cache_process(
871    symbolizer: *mut blaze_symbolizer,
872    cache: *const blaze_cache_src_process,
873) {
874    if !input_zeroed!(cache, blaze_cache_src_process) {
875        let () = set_last_err(blaze_err::INVALID_INPUT);
876        return
877    }
878    let cache = input_sanitize!(cache, blaze_cache_src_process);
879    let cache = cache::Cache::from(cache::Process::from(cache));
880
881    // SAFETY: The caller ensures that the pointer is valid.
882    let symbolizer = unsafe { &*symbolizer };
883    let result = symbolizer.cache(&cache);
884    let err = result
885        .map(|()| blaze_err::OK)
886        .unwrap_or_else(|err| err.kind().into());
887    let () = set_last_err(err);
888}
889
890fn make_cstr(src: &OsStr, cstr_last: &mut *mut c_char) -> *mut c_char {
891    let cstr = *cstr_last;
892    unsafe { ptr::copy_nonoverlapping(src.as_bytes().as_ptr(), cstr as *mut u8, src.len()) };
893    unsafe { *cstr.add(src.len()) = 0 };
894    *cstr_last = unsafe { cstr_last.add(src.len() + 1) };
895
896    cstr
897}
898
899fn convert_code_info(
900    code_info_in: Option<&CodeInfo>,
901    code_info_out: &mut blaze_symbolize_code_info,
902    cstr_last: &mut *mut c_char,
903) {
904    code_info_out.dir = code_info_in
905        .as_ref()
906        .and_then(|info| {
907            info.dir
908                .as_ref()
909                .map(|d| make_cstr(d.as_os_str(), cstr_last))
910        })
911        .unwrap_or_else(ptr::null_mut);
912    code_info_out.file = code_info_in
913        .as_ref()
914        .map(|info| make_cstr(&info.file, cstr_last))
915        .unwrap_or_else(ptr::null_mut);
916    code_info_out.line = code_info_in
917        .as_ref()
918        .and_then(|info| info.line)
919        .unwrap_or(0);
920    code_info_out.column = code_info_in
921        .as_ref()
922        .and_then(|info| info.column)
923        .unwrap_or(0);
924}
925
926
927/// Convert a [`Sym`] into the `blaze_sym` C correspondent.
928pub(crate) fn convert_sym(
929    sym: &Sym,
930    sym_ref: &mut blaze_sym,
931    inlined_last: &mut *mut blaze_symbolize_inlined_fn,
932    cstr_last: &mut *mut c_char,
933) {
934    let name_ptr = make_cstr(OsStr::new(sym.name.as_ref()), cstr_last);
935    let module_ptr = sym
936        .module
937        .as_deref()
938        .map(|module| make_cstr(module, cstr_last))
939        .unwrap_or(ptr::null_mut());
940
941    sym_ref.name = name_ptr;
942    sym_ref.module = module_ptr;
943    sym_ref.addr = sym.addr;
944    sym_ref.offset = sym.offset;
945    sym_ref.size = sym
946        .size
947        .map(|size| isize::try_from(size).unwrap_or(isize::MAX))
948        .unwrap_or(-1);
949    convert_code_info(sym.code_info.as_deref(), &mut sym_ref.code_info, cstr_last);
950    sym_ref.inlined_cnt = sym.inlined.len();
951    sym_ref.inlined = *inlined_last;
952    sym_ref.reason = blaze_symbolize_reason::SUCCESS;
953
954    for inlined in sym.inlined.iter() {
955        let inlined_ref = unsafe { &mut **inlined_last };
956
957        let name_ptr = make_cstr(OsStr::new(inlined.name.as_ref()), cstr_last);
958        inlined_ref.name = name_ptr;
959        convert_code_info(
960            inlined.code_info.as_ref(),
961            &mut inlined_ref.code_info,
962            cstr_last,
963        );
964
965        *inlined_last = unsafe { inlined_last.add(1) };
966    }
967}
968
969
970/// Convert [`Sym`] objects to [`blaze_syms`] ones.
971///
972/// The returned pointer should be released using [`blaze_syms_free`] once
973/// usage concluded.
974fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_syms {
975    // Allocate a buffer to contain a blaze_syms, all
976    // blaze_sym, and C strings of symbol and path.
977    let (strtab_size, inlined_fn_cnt) = results.iter().fold((0, 0), |acc, sym| match sym {
978        Symbolized::Sym(sym) => (acc.0 + sym.c_str_size(), acc.1 + sym.inlined.len()),
979        Symbolized::Unknown(..) => acc,
980    });
981
982    let buf_size = mem::size_of::<u64>()
983        + strtab_size
984        + mem::size_of::<blaze_syms>()
985        + mem::size_of::<blaze_sym>() * results.len()
986        + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt;
987    let buf = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
988    if buf.is_null() {
989        return ptr::null()
990    }
991
992    // Prepend a `u64` to store the size of the buffer.
993    unsafe { *buf.cast::<u64>() = buf_size as u64 };
994
995    let syms_buf = unsafe { buf.add(mem::size_of::<u64>()) };
996
997    let syms_ptr = syms_buf.cast::<blaze_syms>();
998    let mut syms_last = unsafe { (*syms_ptr).syms.as_mut_ptr() };
999    let mut inlined_last = unsafe {
1000        syms_buf.add(mem::size_of::<blaze_syms>() + mem::size_of::<blaze_sym>() * results.len())
1001    } as *mut blaze_symbolize_inlined_fn;
1002    let mut cstr_last = unsafe {
1003        syms_buf.add(
1004            mem::size_of::<blaze_syms>()
1005                + mem::size_of::<blaze_sym>() * results.len()
1006                + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt,
1007        )
1008    } as *mut c_char;
1009
1010    unsafe { (*syms_ptr).cnt = results.len() };
1011
1012    // Convert all `Sym`s to `blazesym_sym`s.
1013    for sym in results {
1014        match sym {
1015            Symbolized::Sym(sym) => {
1016                let sym_ref = unsafe { &mut *syms_last };
1017                let () = convert_sym(&sym, sym_ref, &mut inlined_last, &mut cstr_last);
1018            }
1019            Symbolized::Unknown(reason) => {
1020                // Unknown symbols/addresses are just represented with all
1021                // fields set to zero (except for reason).
1022                // SAFETY: `syms_last` is pointing to a writable and properly
1023                //         aligned `blaze_sym` object.
1024                let () = unsafe { syms_last.write_bytes(0, 1) };
1025                let sym_ref = unsafe { &mut *syms_last };
1026                sym_ref.reason = reason.into();
1027            }
1028        }
1029
1030        syms_last = unsafe { syms_last.add(1) };
1031    }
1032
1033    syms_ptr
1034}
1035
1036unsafe fn blaze_symbolize_impl(
1037    symbolizer: *mut blaze_symbolizer,
1038    src: Source<'_>,
1039    inputs: Input<*const u64>,
1040    input_cnt: usize,
1041) -> *const blaze_syms {
1042    // SAFETY: The caller ensures that the pointer is valid.
1043    let symbolizer = unsafe { &*symbolizer };
1044    // SAFETY: The caller ensures that the pointer is valid and the count
1045    //         matches.
1046    let addrs = unsafe { slice_from_user_array(*inputs.as_inner_ref(), input_cnt) };
1047
1048    let input = match inputs {
1049        Input::AbsAddr(..) => Input::AbsAddr(addrs.deref()),
1050        Input::VirtOffset(..) => Input::VirtOffset(addrs.deref()),
1051        Input::FileOffset(..) => Input::FileOffset(addrs.deref()),
1052    };
1053
1054    let result = symbolizer.symbolize(&src, input);
1055    match result {
1056        Ok(results) if results.is_empty() => {
1057            let () = set_last_err(blaze_err::OK);
1058            ptr::null()
1059        }
1060        Ok(results) => {
1061            let result = convert_symbolizedresults_to_c(results);
1062            if result.is_null() {
1063                let () = set_last_err(blaze_err::OUT_OF_MEMORY);
1064            } else {
1065                let () = set_last_err(blaze_err::OK);
1066            }
1067            result
1068        }
1069        Err(err) => {
1070            let () = set_last_err(err.kind().into());
1071            ptr::null_mut()
1072        }
1073    }
1074}
1075
1076
1077/// Symbolize a list of process absolute addresses.
1078///
1079/// On success, the function returns a [`blaze_syms`] containing an
1080/// array of `abs_addr_cnt` [`blaze_sym`] objects. The returned object
1081/// should be released using [`blaze_syms_free`] once it is no longer
1082/// needed.
1083///
1084/// On error, the function returns `NULL` and sets the thread's last error to
1085/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1086/// error.
1087///
1088/// # Safety
1089/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1090/// - `src` needs to point to a valid [`blaze_symbolize_src_process`] object
1091/// - `abs_addrs` needs to point to an array of `abs_addr_cnt` addresses
1092#[no_mangle]
1093pub unsafe extern "C" fn blaze_symbolize_process_abs_addrs(
1094    symbolizer: *mut blaze_symbolizer,
1095    src: *const blaze_symbolize_src_process,
1096    abs_addrs: *const Addr,
1097    abs_addr_cnt: usize,
1098) -> *const blaze_syms {
1099    if !input_zeroed!(src, blaze_symbolize_src_process) {
1100        let () = set_last_err(blaze_err::INVALID_INPUT);
1101        return ptr::null()
1102    }
1103    let src = input_sanitize!(src, blaze_symbolize_src_process);
1104    let src = Source::from(Process::from(src));
1105
1106    unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1107}
1108
1109
1110/// Symbolize a list of kernel absolute addresses.
1111///
1112/// On success, the function returns a [`blaze_syms`] containing an
1113/// array of `abs_addr_cnt` [`blaze_sym`] objects. The returned object
1114/// should be released using [`blaze_syms_free`] once it is no longer
1115/// needed.
1116///
1117/// On error, the function returns `NULL` and sets the thread's last error to
1118/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1119/// error.
1120///
1121/// # Safety
1122/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1123/// - `src` needs to point to a valid [`blaze_symbolize_src_kernel`] object
1124/// - `abs_addrs` needs to point to an array of `abs_addr_cnt` addresses
1125#[no_mangle]
1126pub unsafe extern "C" fn blaze_symbolize_kernel_abs_addrs(
1127    symbolizer: *mut blaze_symbolizer,
1128    src: *const blaze_symbolize_src_kernel,
1129    abs_addrs: *const Addr,
1130    abs_addr_cnt: usize,
1131) -> *const blaze_syms {
1132    if !input_zeroed!(src, blaze_symbolize_src_kernel) {
1133        let () = set_last_err(blaze_err::INVALID_INPUT);
1134        return ptr::null()
1135    }
1136    let src = input_sanitize!(src, blaze_symbolize_src_kernel);
1137    let src = Source::from(Kernel::from(src));
1138
1139    unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1140}
1141
1142
1143/// Symbolize virtual offsets in an ELF file.
1144///
1145/// On success, the function returns a [`blaze_syms`] containing an
1146/// array of `virt_offset_cnt` [`blaze_sym`] objects. The returned
1147/// object should be released using [`blaze_syms_free`] once it is no
1148/// longer needed.
1149///
1150/// On error, the function returns `NULL` and sets the thread's last error to
1151/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1152/// error.
1153///
1154/// # Safety
1155/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1156/// - `src` needs to point to a valid [`blaze_symbolize_src_elf`] object
1157/// - `virt_offsets` needs to point to an array of `virt_offset_cnt` addresses
1158#[no_mangle]
1159pub unsafe extern "C" fn blaze_symbolize_elf_virt_offsets(
1160    symbolizer: *mut blaze_symbolizer,
1161    src: *const blaze_symbolize_src_elf,
1162    virt_offsets: *const Addr,
1163    virt_offset_cnt: usize,
1164) -> *const blaze_syms {
1165    if !input_zeroed!(src, blaze_symbolize_src_elf) {
1166        let () = set_last_err(blaze_err::INVALID_INPUT);
1167        return ptr::null()
1168    }
1169    let src = input_sanitize!(src, blaze_symbolize_src_elf);
1170    let src = Source::from(Elf::from(src));
1171
1172    unsafe {
1173        blaze_symbolize_impl(
1174            symbolizer,
1175            src,
1176            Input::VirtOffset(virt_offsets),
1177            virt_offset_cnt,
1178        )
1179    }
1180}
1181
1182/// Symbolize file offsets in an ELF file.
1183///
1184/// On success, the function returns a [`blaze_syms`] containing an
1185/// array of `file_offset_cnt` [`blaze_sym`] objects. The returned
1186/// object should be released using [`blaze_syms_free`] once it is no
1187/// longer needed.
1188///
1189/// On error, the function returns `NULL` and sets the thread's last error to
1190/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1191/// error.
1192///
1193/// # Safety
1194/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1195/// - `src` needs to point to a valid [`blaze_symbolize_src_elf`] object
1196/// - `file_offsets` needs to point to an array of `file_offset_cnt` addresses
1197#[no_mangle]
1198pub unsafe extern "C" fn blaze_symbolize_elf_file_offsets(
1199    symbolizer: *mut blaze_symbolizer,
1200    src: *const blaze_symbolize_src_elf,
1201    file_offsets: *const Addr,
1202    file_offset_cnt: usize,
1203) -> *const blaze_syms {
1204    if !input_zeroed!(src, blaze_symbolize_src_elf) {
1205        let () = set_last_err(blaze_err::INVALID_INPUT);
1206        return ptr::null()
1207    }
1208    let src = input_sanitize!(src, blaze_symbolize_src_elf);
1209    let src = Source::from(Elf::from(src));
1210
1211    unsafe {
1212        blaze_symbolize_impl(
1213            symbolizer,
1214            src,
1215            Input::FileOffset(file_offsets),
1216            file_offset_cnt,
1217        )
1218    }
1219}
1220
1221
1222/// Symbolize virtual offsets using "raw" Gsym data.
1223///
1224/// On success, the function returns a [`blaze_syms`] containing an
1225/// array of `virt_offset_cnt` [`blaze_sym`] objects. The returned
1226/// object should be released using [`blaze_syms_free`] once it is no
1227/// longer needed.
1228///
1229/// On error, the function returns `NULL` and sets the thread's last error to
1230/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1231/// error.
1232///
1233/// # Safety
1234/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1235/// - `src` needs to point to a valid [`blaze_symbolize_src_gsym_data`] object
1236/// - `virt_offsets` needs to point to an array of `virt_offset_cnt` addresses
1237#[no_mangle]
1238pub unsafe extern "C" fn blaze_symbolize_gsym_data_virt_offsets(
1239    symbolizer: *mut blaze_symbolizer,
1240    src: *const blaze_symbolize_src_gsym_data,
1241    virt_offsets: *const Addr,
1242    virt_offset_cnt: usize,
1243) -> *const blaze_syms {
1244    if !input_zeroed!(src, blaze_symbolize_src_gsym_data) {
1245        let () = set_last_err(blaze_err::INVALID_INPUT);
1246        return ptr::null()
1247    }
1248    let src = input_sanitize!(src, blaze_symbolize_src_gsym_data);
1249    let src = Source::from(GsymData::from(src));
1250    unsafe {
1251        blaze_symbolize_impl(
1252            symbolizer,
1253            src,
1254            Input::VirtOffset(virt_offsets),
1255            virt_offset_cnt,
1256        )
1257    }
1258}
1259
1260
1261/// Symbolize virtual offsets in a Gsym file.
1262///
1263/// On success, the function returns a [`blaze_syms`] containing an
1264/// array of `virt_offset_cnt` [`blaze_sym`] objects. The returned
1265/// object should be released using [`blaze_syms_free`] once it is no
1266/// longer needed.
1267///
1268/// On error, the function returns `NULL` and sets the thread's last error to
1269/// indicate the problem encountered. Use [`blaze_err_last`] to retrieve this
1270/// error.
1271///
1272/// # Safety
1273/// - `symbolizer` needs to point to a valid [`blaze_symbolizer`] object
1274/// - `src` needs to point to a valid [`blaze_symbolize_src_gsym_file`] object
1275/// - `virt_offsets` needs to point to an array of `virt_offset_cnt` addresses
1276#[no_mangle]
1277pub unsafe extern "C" fn blaze_symbolize_gsym_file_virt_offsets(
1278    symbolizer: *mut blaze_symbolizer,
1279    src: *const blaze_symbolize_src_gsym_file,
1280    virt_offsets: *const Addr,
1281    virt_offset_cnt: usize,
1282) -> *const blaze_syms {
1283    if !input_zeroed!(src, blaze_symbolize_src_gsym_file) {
1284        let () = set_last_err(blaze_err::INVALID_INPUT);
1285        return ptr::null()
1286    }
1287    let src = input_sanitize!(src, blaze_symbolize_src_gsym_file);
1288    let src = Source::from(GsymFile::from(src));
1289
1290    unsafe {
1291        blaze_symbolize_impl(
1292            symbolizer,
1293            src,
1294            Input::VirtOffset(virt_offsets),
1295            virt_offset_cnt,
1296        )
1297    }
1298}
1299
1300
1301/// Free an array returned by any of the `blaze_symbolize_*` variants.
1302///
1303/// # Safety
1304/// The pointer must have been returned by any of the `blaze_symbolize_*`
1305/// variants.
1306#[no_mangle]
1307pub unsafe extern "C" fn blaze_syms_free(syms: *const blaze_syms) {
1308    if syms.is_null() {
1309        return
1310    }
1311
1312    // Retrieve back the buffer with the `u64` size header.
1313    let buf = unsafe { syms.byte_sub(mem::size_of::<u64>()).cast::<u8>().cast_mut() };
1314    let size = unsafe { *buf.cast::<u64>() } as usize;
1315    unsafe { dealloc(buf, Layout::from_size_align(size, 8).unwrap()) };
1316}
1317
1318
1319#[cfg(test)]
1320mod tests {
1321    use super::*;
1322
1323    use std::borrow::Cow;
1324    use std::ffi::CString;
1325    use std::fs::copy;
1326    use std::fs::read as read_file;
1327    use std::fs::remove_file;
1328    use std::hint::black_box;
1329    use std::io::Error;
1330    use std::os::unix::ffi::OsStringExt as _;
1331    use std::slice;
1332
1333    use blazesym::inspect;
1334    use blazesym::normalize;
1335    use blazesym::symbolize::InlinedFn;
1336    use blazesym::symbolize::Reason;
1337    use blazesym::Pid;
1338
1339    use tempfile::tempdir;
1340
1341    use test_tag::tag;
1342
1343    use crate::blaze_err_last;
1344
1345
1346    /// Check that various types have expected sizes.
1347    #[tag(miri)]
1348    #[test]
1349    #[cfg(target_pointer_width = "64")]
1350    fn type_sizes() {
1351        assert_eq!(mem::size_of::<blaze_cache_src_elf>(), 32);
1352        assert_eq!(mem::size_of::<blaze_cache_src_process>(), 32);
1353        assert_eq!(mem::size_of::<blaze_symbolize_src_elf>(), 40);
1354        assert_eq!(mem::size_of::<blaze_symbolize_src_kernel>(), 48);
1355        assert_eq!(mem::size_of::<blaze_symbolize_src_process>(), 32);
1356        assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_data>(), 40);
1357        assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_file>(), 32);
1358        assert_eq!(mem::size_of::<blaze_symbolizer_opts>(), 48);
1359        assert_eq!(mem::size_of::<blaze_symbolize_code_info>(), 32);
1360        assert_eq!(mem::size_of::<blaze_symbolize_inlined_fn>(), 48);
1361        assert_eq!(mem::size_of::<blaze_sym>(), 104);
1362    }
1363
1364    /// Exercise the `Debug` representation of various types.
1365    #[tag(miri)]
1366    #[test]
1367    fn debug_repr() {
1368        let elf = blaze_symbolize_src_elf {
1369            type_size: 24,
1370            ..Default::default()
1371        };
1372        assert_eq!(
1373            format!("{elf:?}"),
1374            "blaze_symbolize_src_elf { type_size: 24, path: 0x0, debug_syms: false, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1375        );
1376
1377        let kernel = blaze_symbolize_src_kernel {
1378            type_size: 32,
1379            debug_syms: true,
1380            ..Default::default()
1381        };
1382        assert_eq!(
1383            format!("{kernel:?}"),
1384            "blaze_symbolize_src_kernel { type_size: 32, kallsyms: 0x0, vmlinux: 0x0, debug_syms: true, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1385        );
1386
1387        let process = blaze_symbolize_src_process {
1388            type_size: 16,
1389            pid: 1337,
1390            debug_syms: true,
1391            ..Default::default()
1392        };
1393        assert_eq!(
1394            format!("{process:?}"),
1395            "blaze_symbolize_src_process { type_size: 16, pid: 1337, debug_syms: true, perf_map: false, no_map_files: false, no_vdso: false, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1396        );
1397
1398        let gsym_data = blaze_symbolize_src_gsym_data {
1399            type_size: 24,
1400            data: ptr::null(),
1401            data_len: 0,
1402            reserved: [0; 16],
1403        };
1404        assert_eq!(
1405            format!("{gsym_data:?}"),
1406            "blaze_symbolize_src_gsym_data { type_size: 24, data: 0x0, data_len: 0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1407        );
1408
1409        let gsym_file = blaze_symbolize_src_gsym_file {
1410            type_size: 16,
1411            path: ptr::null(),
1412            reserved: [0; 16],
1413        };
1414        assert_eq!(
1415            format!("{gsym_file:?}"),
1416            "blaze_symbolize_src_gsym_file { type_size: 16, path: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1417        );
1418
1419        let sym = blaze_sym {
1420            name: ptr::null(),
1421            module: ptr::null(),
1422            addr: 0x1337,
1423            offset: 24,
1424            size: 16,
1425            code_info: blaze_symbolize_code_info {
1426                dir: ptr::null(),
1427                file: ptr::null(),
1428                line: 42,
1429                column: 1,
1430                reserved: [0; 10],
1431            },
1432            inlined_cnt: 0,
1433            inlined: ptr::null(),
1434            reason: blaze_symbolize_reason::UNSUPPORTED,
1435            reserved: [0; 15],
1436        };
1437        assert_eq!(
1438            format!("{sym:?}"),
1439            "blaze_sym { name: 0x0, module: 0x0, addr: 4919, offset: 24, size: 16, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, inlined_cnt: 0, inlined: 0x0, reason: blaze_symbolize_reason(6), reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1440        );
1441
1442        let inlined = blaze_symbolize_inlined_fn {
1443            name: ptr::null(),
1444            code_info: blaze_symbolize_code_info {
1445                dir: ptr::null(),
1446                file: ptr::null(),
1447                line: 42,
1448                column: 1,
1449                reserved: [0; 10],
1450            },
1451            reserved: [0; 8],
1452        };
1453        assert_eq!(
1454            format!("{inlined:?}"),
1455            "blaze_symbolize_inlined_fn { name: 0x0, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reserved: [0, 0, 0, 0, 0, 0, 0, 0] }"
1456        );
1457
1458        let syms = blaze_syms { cnt: 0, syms: [] };
1459        assert_eq!(format!("{syms:?}"), "blaze_syms { cnt: 0, syms: [] }");
1460
1461        let opts = blaze_symbolizer_opts {
1462            type_size: 16,
1463            demangle: true,
1464            ..Default::default()
1465        };
1466        assert_eq!(
1467            format!("{opts:?}"),
1468            "blaze_symbolizer_opts { type_size: 16, debug_dirs: 0x0, debug_dirs_len: 0, auto_reload: false, code_info: false, inlined_fns: false, demangle: true, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1469        );
1470    }
1471
1472    /// Make sure that we can stringify symbolization reasons as expected.
1473    #[tag(miri)]
1474    #[test]
1475    fn reason_stringification() {
1476        let data = [
1477            (Reason::Unmapped, blaze_symbolize_reason::UNMAPPED),
1478            (
1479                Reason::InvalidFileOffset,
1480                blaze_symbolize_reason::INVALID_FILE_OFFSET,
1481            ),
1482            (
1483                Reason::MissingComponent,
1484                blaze_symbolize_reason::MISSING_COMPONENT,
1485            ),
1486            (Reason::MissingSyms, blaze_symbolize_reason::MISSING_SYMS),
1487            (Reason::Unsupported, blaze_symbolize_reason::UNSUPPORTED),
1488            (Reason::UnknownAddr, blaze_symbolize_reason::UNKNOWN_ADDR),
1489            (Reason::IgnoredError, blaze_symbolize_reason::IGNORED_ERROR),
1490        ];
1491
1492        for (reason, expected) in data {
1493            assert_eq!(blaze_symbolize_reason::from(reason), expected);
1494            let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1495            let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1496            assert_eq!(cstr, expected);
1497        }
1498    }
1499
1500
1501    /// Check that we can convert a [`blaze_symbolize_src_kernel`]
1502    /// reference into a [`Kernel`].
1503    #[tag(miri)]
1504    #[test]
1505    fn kernel_conversion() {
1506        let kernel = blaze_symbolize_src_kernel::default();
1507        let kernel = Kernel::from(kernel);
1508        assert_eq!(kernel.kallsyms, MaybeDefault::Default);
1509        assert_eq!(kernel.vmlinux, MaybeDefault::Default);
1510
1511        let kernel = blaze_symbolize_src_kernel {
1512            kallsyms: b"\0" as *const _ as *const c_char,
1513            vmlinux: b"\0" as *const _ as *const c_char,
1514            ..Default::default()
1515        };
1516        let kernel = Kernel::from(kernel);
1517        assert_eq!(kernel.kallsyms, MaybeDefault::None);
1518        assert_eq!(kernel.vmlinux, MaybeDefault::None);
1519
1520        let kernel = blaze_symbolize_src_kernel {
1521            kallsyms: b"/proc/kallsyms\0" as *const _ as *const c_char,
1522            vmlinux: b"/boot/vmlinux\0" as *const _ as *const c_char,
1523            debug_syms: false,
1524            ..Default::default()
1525        };
1526
1527        let kernel = Kernel::from(kernel);
1528        assert_eq!(
1529            kernel.kallsyms,
1530            MaybeDefault::Some(PathBuf::from("/proc/kallsyms"))
1531        );
1532        assert_eq!(
1533            kernel.vmlinux,
1534            MaybeDefault::Some(PathBuf::from("/boot/vmlinux"))
1535        );
1536    }
1537
1538    /// Check that we can convert a [`blaze_cache_src_process`] into a
1539    /// [`cache::Process`].
1540    #[tag(miri)]
1541    #[test]
1542    fn cache_process_conversion() {
1543        let process = blaze_cache_src_process {
1544            pid: 42,
1545            cache_vmas: false,
1546            ..Default::default()
1547        };
1548        let process = cache::Process::from(process);
1549        assert_eq!(process.pid, Pid::from(42))
1550    }
1551
1552    /// Test the Rust to C symbol conversion.
1553    #[tag(miri)]
1554    #[test]
1555    fn symbol_conversion() {
1556        fn touch<X: Clone>(x: &X) {
1557            let x = x.clone();
1558            let _x = black_box(x);
1559        }
1560
1561        fn touch_cstr(s: *const c_char) {
1562            if !s.is_null() {
1563                let s = unsafe { CStr::from_ptr(s) }.to_bytes();
1564                let _x = black_box(s);
1565            }
1566        }
1567
1568        fn touch_code_info(code_info: &blaze_symbolize_code_info) {
1569            let blaze_symbolize_code_info {
1570                dir,
1571                file,
1572                line,
1573                column,
1574                reserved: _,
1575            } = code_info;
1576
1577            let _x = touch_cstr(*dir);
1578            let _x = touch_cstr(*file);
1579            let _x = touch(line);
1580            let _x = touch(column);
1581        }
1582
1583        /// Touch all "members" of a [`blaze_syms`].
1584        fn touch_syms(syms: *const blaze_syms) {
1585            let syms = unsafe { &*syms };
1586            for i in 0..syms.cnt {
1587                let sym = unsafe { &*syms.syms.as_slice().as_ptr().add(i) };
1588                let blaze_sym {
1589                    name,
1590                    module,
1591                    addr,
1592                    offset,
1593                    size,
1594                    code_info,
1595                    inlined_cnt,
1596                    inlined,
1597                    reason,
1598                    reserved: _,
1599                } = sym;
1600
1601                let () = touch_cstr(*name);
1602                let () = touch_cstr(*module);
1603                let _x = touch(addr);
1604                let _x = touch(offset);
1605                let _x = touch(size);
1606                let () = touch_code_info(code_info);
1607
1608                for j in 0..*inlined_cnt {
1609                    let inlined_fn = unsafe { &*inlined.add(j) };
1610                    let blaze_symbolize_inlined_fn {
1611                        name,
1612                        code_info,
1613                        reserved: _,
1614                    } = inlined_fn;
1615                    let () = touch_cstr(*name);
1616                    let () = touch_code_info(code_info);
1617                }
1618                let () = touch(reason);
1619            }
1620        }
1621
1622        // Empty list of symbols.
1623        let results = vec![];
1624        let syms = convert_symbolizedresults_to_c(results);
1625        assert!(!syms.is_null());
1626
1627        let () = touch_syms(syms);
1628        let () = unsafe { blaze_syms_free(syms) };
1629
1630        // A single symbol with inlined function information.
1631        let results = vec![Symbolized::Sym(Sym {
1632            name: "test".into(),
1633            module: Some(Cow::from(OsStr::new("module"))),
1634            addr: 0x1337,
1635            offset: 0x1338,
1636            size: Some(42),
1637            code_info: Some(Box::new(CodeInfo {
1638                dir: None,
1639                file: OsStr::new("a-file").into(),
1640                line: Some(42),
1641                column: Some(43),
1642                _non_exhaustive: (),
1643            })),
1644            inlined: vec![InlinedFn {
1645                name: "inlined_fn".into(),
1646                code_info: Some(CodeInfo {
1647                    dir: Some(Path::new("/some/dir").into()),
1648                    file: OsStr::new("another-file").into(),
1649                    line: Some(42),
1650                    column: Some(43),
1651                    _non_exhaustive: (),
1652                }),
1653                _non_exhaustive: (),
1654            }]
1655            .into_boxed_slice(),
1656            _non_exhaustive: (),
1657        })];
1658        let syms = convert_symbolizedresults_to_c(results);
1659        assert!(!syms.is_null());
1660        let () = touch_syms(syms);
1661        let () = unsafe { blaze_syms_free(syms) };
1662
1663        // One symbol and some unsymbolized values.
1664        let results = vec![
1665            Symbolized::Unknown(Reason::UnknownAddr),
1666            Symbolized::Sym(Sym {
1667                name: "test".into(),
1668                module: Some(Cow::from(OsStr::new("module"))),
1669                addr: 0x1337,
1670                offset: 0x1338,
1671                size: None,
1672                code_info: None,
1673                inlined: vec![InlinedFn {
1674                    name: "inlined_fn".into(),
1675                    code_info: None,
1676                    _non_exhaustive: (),
1677                }]
1678                .into_boxed_slice(),
1679                _non_exhaustive: (),
1680            }),
1681            Symbolized::Unknown(Reason::InvalidFileOffset),
1682        ];
1683        let syms = convert_symbolizedresults_to_c(results);
1684        assert!(!syms.is_null());
1685        let () = touch_syms(syms);
1686        let () = unsafe { blaze_syms_free(syms) };
1687    }
1688
1689    /// Make sure that we can create and free a symbolizer instance.
1690    #[tag(miri)]
1691    #[test]
1692    fn symbolizer_creation() {
1693        let symbolizer = blaze_symbolizer_new();
1694        let () = unsafe { blaze_symbolizer_free(symbolizer) };
1695    }
1696
1697    /// Make sure that we can create and free a symbolizer instance with the
1698    /// provided options.
1699    #[tag(miri)]
1700    #[test]
1701    fn symbolizer_creation_with_opts() {
1702        let opts = blaze_symbolizer_opts {
1703            demangle: true,
1704            ..Default::default()
1705        };
1706
1707        let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1708        let () = unsafe { blaze_symbolizer_free(symbolizer) };
1709    }
1710
1711    /// Make sure that we can symbolize an address using ELF, DWARF, and
1712    /// GSYM.
1713    #[test]
1714    fn symbolize_elf_dwarf_gsym() {
1715        fn test<F>(symbolize: F, has_code_info: bool)
1716        where
1717            F: FnOnce(*mut blaze_symbolizer, *const Addr, usize) -> *const blaze_syms,
1718        {
1719            let symbolizer = blaze_symbolizer_new();
1720            let addrs = [0x2000200];
1721            let result = symbolize(symbolizer, addrs.as_ptr(), addrs.len());
1722
1723            assert!(!result.is_null());
1724
1725            let result = unsafe { &*result };
1726            assert_eq!(result.cnt, 1);
1727            let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1728            let sym = &syms[0];
1729            assert_eq!(
1730                unsafe { CStr::from_ptr(sym.name) },
1731                CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1732            );
1733            assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1734            assert_eq!(sym.addr, 0x2000200);
1735            assert_eq!(sym.offset, 0);
1736            assert!(sym.size > 0);
1737
1738            if has_code_info {
1739                assert!(!sym.code_info.dir.is_null());
1740                assert!(!sym.code_info.file.is_null());
1741                assert_eq!(
1742                    unsafe { CStr::from_ptr(sym.code_info.file) },
1743                    CStr::from_bytes_with_nul(b"test-stable-addrs.c\0").unwrap()
1744                );
1745                assert_eq!(sym.code_info.line, 10);
1746            } else {
1747                assert!(sym.code_info.dir.is_null());
1748                assert!(sym.code_info.file.is_null());
1749                assert_eq!(sym.code_info.line, 0);
1750            }
1751
1752            let () = unsafe { blaze_syms_free(result) };
1753            let () = unsafe { blaze_symbolizer_free(symbolizer) };
1754        }
1755
1756        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1757            .join("..")
1758            .join("data")
1759            .join("test-stable-addrs-no-dwarf.bin");
1760        let path_c = CString::new(path.to_str().unwrap()).unwrap();
1761        let elf_src = blaze_symbolize_src_elf {
1762            path: path_c.as_ptr(),
1763            debug_syms: true,
1764            ..Default::default()
1765        };
1766
1767        let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1768            blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1769        };
1770        test(symbolize, false);
1771
1772        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1773            .join("..")
1774            .join("data")
1775            .join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1776        let path_c = CString::new(path.to_str().unwrap()).unwrap();
1777        let elf_src = blaze_symbolize_src_elf {
1778            path: path_c.as_ptr(),
1779            debug_syms: true,
1780            ..Default::default()
1781        };
1782
1783        let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1784            blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1785        };
1786        test(symbolize, true);
1787
1788        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1789            .join("..")
1790            .join("data")
1791            .join("test-stable-addrs.gsym");
1792        let path_c = CString::new(path.to_str().unwrap()).unwrap();
1793        let gsym_src = blaze_symbolize_src_gsym_file {
1794            path: path_c.as_ptr(),
1795            ..Default::default()
1796        };
1797
1798        let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1799            blaze_symbolize_gsym_file_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1800        };
1801        test(symbolize, true);
1802
1803        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1804            .join("..")
1805            .join("data")
1806            .join("test-stable-addrs.gsym");
1807        let data = read_file(path).unwrap();
1808        let gsym_src = blaze_symbolize_src_gsym_data {
1809            data: data.as_ptr(),
1810            data_len: data.len(),
1811            ..Default::default()
1812        };
1813
1814        let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1815            blaze_symbolize_gsym_data_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1816        };
1817        test(symbolize, true);
1818    }
1819
1820
1821    /// Check that we can symbolize a file offset in an ELF file.
1822    #[test]
1823    #[cfg_attr(
1824        not(target_pointer_width = "64"),
1825        ignore = "loads 64 bit shared object"
1826    )]
1827    fn symbolize_elf_file_offset() {
1828        let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1829            .join("..")
1830            .join("data")
1831            .join("libtest-so.so")
1832            .canonicalize()
1833            .unwrap();
1834        let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1835        let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1836        assert!(!handle.is_null());
1837
1838        let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1839        assert!(!the_answer_addr.is_null());
1840
1841        let normalizer = normalize::Normalizer::new();
1842        let normalized = normalizer
1843            .normalize_user_addrs(Pid::Slf, [the_answer_addr as Addr].as_slice())
1844            .unwrap();
1845        assert_eq!(normalized.outputs.len(), 1);
1846        assert_eq!(normalized.meta.len(), 1);
1847
1848        let rc = unsafe { libc::dlclose(handle) };
1849        assert_eq!(rc, 0, "{}", Error::last_os_error());
1850
1851        let output = normalized.outputs[0];
1852        let meta = &normalized.meta[output.1];
1853        assert_eq!(meta.as_elf().unwrap().path, test_so);
1854
1855        let symbolizer = blaze_symbolizer_new();
1856        let elf_src = blaze_symbolize_src_elf {
1857            path: so_cstr.as_ptr(),
1858            ..Default::default()
1859        };
1860        let offsets = [output.0];
1861        let result = unsafe {
1862            blaze_symbolize_elf_file_offsets(
1863                symbolizer,
1864                &elf_src,
1865                offsets.as_slice().as_ptr(),
1866                offsets.len(),
1867            )
1868        };
1869        assert!(!result.is_null());
1870
1871        let result = unsafe { &*result };
1872        assert_eq!(result.cnt, 1);
1873
1874        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1875        let sym = &syms[0];
1876        assert!(!sym.name.is_null());
1877        assert!(!sym.module.is_null());
1878        assert!(sym.size > 0);
1879        assert_eq!(
1880            unsafe { CStr::from_ptr(sym.name) },
1881            CStr::from_bytes_with_nul(b"the_answer\0").unwrap()
1882        );
1883
1884        let () = unsafe { blaze_syms_free(result) };
1885        let () = unsafe { blaze_symbolizer_free(symbolizer) };
1886    }
1887
1888    /// Check that we can symbolize data in a non-existent ELF binary after
1889    /// caching it.
1890    #[tag(other_os)]
1891    #[test]
1892    fn symbolize_elf_cached() {
1893        let dir = tempdir().unwrap();
1894        let path__ = Path::new(&env!("CARGO_MANIFEST_DIR"))
1895            .join("..")
1896            .join("data")
1897            .join("test-stable-addrs.bin");
1898        let path = dir.path().join("test-stable-addrs-temporary.bin");
1899        let _count = copy(&path__, &path).unwrap();
1900
1901        let symbolizer = blaze_symbolizer_new();
1902
1903        let path_c = CString::new(path.to_str().unwrap()).unwrap();
1904        let cache = blaze_cache_src_elf {
1905            path: path_c.as_ptr(),
1906            ..Default::default()
1907        };
1908        let () = unsafe { blaze_symbolize_cache_elf(symbolizer, &cache) };
1909        assert_eq!(blaze_err_last(), blaze_err::OK);
1910
1911        let () = remove_file(&path).unwrap();
1912
1913        let src = blaze_symbolize_src_elf {
1914            path: path_c.as_ptr(),
1915            debug_syms: false,
1916            ..Default::default()
1917        };
1918        let addrs = [0x2000200];
1919        let result = unsafe {
1920            blaze_symbolize_elf_virt_offsets(symbolizer, &src, addrs.as_ptr(), addrs.len())
1921        };
1922        assert!(!result.is_null());
1923
1924        let result = unsafe { &*result };
1925        assert_eq!(result.cnt, 1);
1926        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1927        let sym = &syms[0];
1928
1929        assert_eq!(
1930            unsafe { CStr::from_ptr(sym.name) },
1931            CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1932        );
1933        assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1934        assert_eq!(sym.addr, 0x2000200);
1935
1936        let () = unsafe { blaze_syms_free(result) };
1937        let () = unsafe { blaze_symbolizer_free(symbolizer) };
1938    }
1939
1940    /// Symbolize an address inside a DWARF file, with and without
1941    /// auto-demangling enabled.
1942    #[test]
1943    fn symbolize_dwarf_demangle() {
1944        fn test(path: &Path, addr: Addr) -> Result<(), ()> {
1945            let opts = blaze_symbolizer_opts {
1946                code_info: true,
1947                inlined_fns: true,
1948                ..Default::default()
1949            };
1950
1951            let path_c = CString::new(path.to_str().unwrap()).unwrap();
1952            let elf_src = blaze_symbolize_src_elf {
1953                path: path_c.as_ptr(),
1954                debug_syms: true,
1955                ..Default::default()
1956            };
1957
1958            let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1959            let addrs = [addr];
1960            let result = unsafe {
1961                blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1962            };
1963            assert!(!result.is_null());
1964
1965            let result = unsafe { &*result };
1966            assert_eq!(result.cnt, 1);
1967            let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1968            let sym = &syms[0];
1969            let name = unsafe { CStr::from_ptr(sym.name) };
1970            assert!(
1971                name.to_str().unwrap().contains("test13test_function"),
1972                "{name:?}"
1973            );
1974
1975            if sym.inlined_cnt == 0 {
1976                let () = unsafe { blaze_syms_free(result) };
1977                let () = unsafe { blaze_symbolizer_free(symbolizer) };
1978                return Err(())
1979            }
1980
1981            assert_eq!(sym.inlined_cnt, 1);
1982            let name = unsafe { CStr::from_ptr((*sym.inlined).name) };
1983            assert!(
1984                name.to_str().unwrap().contains("test12inlined_call"),
1985                "{name:?}"
1986            );
1987
1988            let () = unsafe { blaze_syms_free(result) };
1989            let () = unsafe { blaze_symbolizer_free(symbolizer) };
1990
1991            // Do it again, this time with demangling enabled.
1992            let opts = blaze_symbolizer_opts {
1993                code_info: true,
1994                inlined_fns: true,
1995                demangle: true,
1996                ..Default::default()
1997            };
1998
1999            let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2000            let addrs = [addr];
2001            let result = unsafe {
2002                blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2003            };
2004            assert!(!result.is_null());
2005
2006            let result = unsafe { &*result };
2007            assert_eq!(result.cnt, 1);
2008            let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2009            let sym = &syms[0];
2010            assert_eq!(
2011                unsafe { CStr::from_ptr(sym.name) },
2012                CStr::from_bytes_with_nul(b"test::test_function\0").unwrap()
2013            );
2014            assert_eq!(unsafe { CStr::from_ptr(sym.module) }, &*path_c);
2015
2016            assert_eq!(sym.inlined_cnt, 1);
2017            assert_eq!(
2018                unsafe { CStr::from_ptr((*sym.inlined).name) },
2019                CStr::from_bytes_with_nul(b"test::inlined_call\0").unwrap()
2020            );
2021
2022            let () = unsafe { blaze_syms_free(result) };
2023            let () = unsafe { blaze_symbolizer_free(symbolizer) };
2024            Ok(())
2025        }
2026
2027        let test_dwarf = Path::new(&env!("CARGO_MANIFEST_DIR"))
2028            .join("..")
2029            .join("data")
2030            .join("test-rs.bin");
2031        let elf = inspect::source::Elf::new(&test_dwarf);
2032        let src = inspect::source::Source::Elf(elf);
2033
2034        let inspector = inspect::Inspector::new();
2035        let results = inspector
2036            .lookup(&src, &["_RNvCs69hjMPjVIJK_4test13test_function"])
2037            .unwrap()
2038            .into_iter()
2039            .flatten()
2040            .collect::<Vec<_>>();
2041        assert!(!results.is_empty());
2042
2043        let addr = results[0].addr;
2044        let src = Source::Elf(Elf::new(&test_dwarf));
2045        let symbolizer = Symbolizer::builder().enable_demangling(false).build();
2046        let result = symbolizer
2047            .symbolize_single(&src, Input::VirtOffset(addr))
2048            .unwrap()
2049            .into_sym()
2050            .unwrap();
2051
2052        let addr = result.addr;
2053        let size = result.size.unwrap() as u64;
2054        for inst_addr in addr..addr + size {
2055            if test(&test_dwarf, inst_addr).is_ok() {
2056                return
2057            }
2058        }
2059
2060        panic!("failed to find inlined function call");
2061    }
2062
2063    /// Make sure that we can symbolize an address in a process.
2064    #[test]
2065    fn symbolize_in_process() {
2066        let process_src = blaze_symbolize_src_process {
2067            pid: 0,
2068            debug_syms: true,
2069            perf_map: true,
2070            ..Default::default()
2071        };
2072
2073        let symbolizer = blaze_symbolizer_new();
2074        let addrs = [blaze_symbolizer_new as Addr];
2075        let result = unsafe {
2076            blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2077        };
2078
2079        assert!(!result.is_null());
2080
2081        let result = unsafe { &*result };
2082        assert_eq!(result.cnt, 1);
2083        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2084        let sym = &syms[0];
2085        assert_eq!(
2086            unsafe { CStr::from_ptr(sym.name) },
2087            CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2088        );
2089
2090        let () = unsafe { blaze_syms_free(result) };
2091        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2092    }
2093
2094    /// Make sure that we can symbolize addresses in a process after
2095    /// caching the corresponding metadata.
2096    #[test]
2097    fn symbolize_in_process_cached() {
2098        let symbolizer = blaze_symbolizer_new();
2099        let cache = blaze_cache_src_process {
2100            pid: 0,
2101            cache_vmas: true,
2102            ..Default::default()
2103        };
2104        let () = unsafe { blaze_symbolize_cache_process(symbolizer, &cache) };
2105        assert_eq!(blaze_err_last(), blaze_err::OK);
2106
2107        let src = blaze_symbolize_src_process {
2108            pid: 0,
2109            debug_syms: true,
2110            perf_map: true,
2111            ..Default::default()
2112        };
2113        let addrs = [blaze_symbolizer_new as Addr];
2114        let result = unsafe {
2115            blaze_symbolize_process_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2116        };
2117
2118        assert!(!result.is_null());
2119
2120        let result = unsafe { &*result };
2121        assert_eq!(result.cnt, 1);
2122        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2123        let sym = &syms[0];
2124        assert_eq!(
2125            unsafe { CStr::from_ptr(sym.name) },
2126            CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2127        );
2128
2129        let () = unsafe { blaze_syms_free(result) };
2130        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2131    }
2132
2133    /// Make sure that we can symbolize an address in the kernel via
2134    /// kallsyms.
2135    #[test]
2136    fn symbolize_in_kernel() {
2137        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2138            .join("..")
2139            .join("data")
2140            .join("kallsyms");
2141        let path_c = CString::new(path.to_str().unwrap()).unwrap();
2142        let src = blaze_symbolize_src_kernel {
2143            kallsyms: path_c.as_ptr(),
2144            vmlinux: b"\0" as *const _ as *const c_char,
2145            ..Default::default()
2146        };
2147
2148        let symbolizer = blaze_symbolizer_new();
2149        let addrs = [0xc080a470];
2150        let result = unsafe {
2151            blaze_symbolize_kernel_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2152        };
2153
2154        assert!(!result.is_null());
2155
2156        let result = unsafe { &*result };
2157        assert_eq!(result.cnt, 1);
2158        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2159        let sym = &syms[0];
2160        assert_eq!(
2161            unsafe { CStr::from_ptr(sym.name) },
2162            CStr::from_bytes_with_nul(b"init_task\0").unwrap()
2163        );
2164        assert_eq!(sym.module, ptr::null_mut());
2165
2166        let () = unsafe { blaze_syms_free(result) };
2167        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2168    }
2169
2170    /// Check that we report the expected error when attempting to
2171    /// symbolize data using a non-existent source.
2172    #[test]
2173    fn symbolize_elf_non_existent() {
2174        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2175            .join("..")
2176            .join("data")
2177            .join("does-not-actually-exist.bin");
2178        let path_c = CString::new(path.to_str().unwrap()).unwrap();
2179        let elf_src = blaze_symbolize_src_elf {
2180            path: path_c.as_ptr(),
2181            debug_syms: true,
2182            ..Default::default()
2183        };
2184
2185        let symbolizer = blaze_symbolizer_new();
2186        let addrs = [blaze_symbolizer_new as Addr];
2187        let result = unsafe {
2188            blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2189        };
2190        assert!(result.is_null());
2191        assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
2192
2193        let () = unsafe { blaze_syms_free(result) };
2194        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2195    }
2196
2197    /// Check that we can handle no configured debug directories.
2198    #[test]
2199    fn symbolize_no_debug_dirs() {
2200        let dir = tempdir().unwrap();
2201        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2202            .join("..")
2203            .join("data")
2204            .join("test-stable-addrs-stripped-with-link.bin");
2205        let dst = dir.path().join("test-stable-addrs-stripped-with-link.bin");
2206        let _count = copy(path, &dst).unwrap();
2207
2208        let debug_dirs = [];
2209        let opts = blaze_symbolizer_opts {
2210            debug_dirs: debug_dirs.as_ptr(),
2211            debug_dirs_len: debug_dirs.len(),
2212            code_info: true,
2213            inlined_fns: true,
2214            demangle: true,
2215            ..Default::default()
2216        };
2217        let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2218
2219        let path_c = CString::new(dst.to_str().unwrap()).unwrap();
2220        let elf_src = blaze_symbolize_src_elf {
2221            path: path_c.as_ptr(),
2222            debug_syms: true,
2223            ..Default::default()
2224        };
2225        let addrs = [0x2000200];
2226        let result = unsafe {
2227            blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2228        };
2229        assert!(!result.is_null());
2230
2231        let result = unsafe { &*result };
2232        assert_eq!(result.cnt, 1);
2233        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2234        let sym = &syms[0];
2235        // Shouldn't have symbolized because the debug link target cannot be
2236        // found.
2237        assert_eq!(sym.name, ptr::null());
2238        assert_eq!(sym.module, ptr::null());
2239        assert_eq!(sym.reason, blaze_symbolize_reason::MISSING_SYMS);
2240
2241        let () = unsafe { blaze_syms_free(result) };
2242        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2243    }
2244
2245    /// Make sure that debug directories are configurable.
2246    #[test]
2247    fn symbolize_configurable_debug_dirs() {
2248        let debug_dir1 = tempdir().unwrap();
2249        let debug_dir1_c = CString::new(debug_dir1.path().to_str().unwrap()).unwrap();
2250        let debug_dir2 = tempdir().unwrap();
2251        let debug_dir2_c = CString::new(debug_dir2.path().to_str().unwrap()).unwrap();
2252
2253        let src = Path::new(&env!("CARGO_MANIFEST_DIR"))
2254            .join("..")
2255            .join("data")
2256            .join("test-stable-addrs-dwarf-only.dbg");
2257        let dst = debug_dir2.path().join("test-stable-addrs-dwarf-only.dbg");
2258        let _count = copy(src, dst).unwrap();
2259
2260        let debug_dirs = [debug_dir1_c.as_ptr(), debug_dir2_c.as_ptr()];
2261        let opts = blaze_symbolizer_opts {
2262            debug_dirs: debug_dirs.as_ptr(),
2263            debug_dirs_len: debug_dirs.len(),
2264            code_info: true,
2265            inlined_fns: true,
2266            demangle: true,
2267            ..Default::default()
2268        };
2269        let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2270
2271        let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2272            .join("..")
2273            .join("data")
2274            .join("test-stable-addrs-stripped-with-link.bin");
2275        let path_c = CString::new(path.to_str().unwrap()).unwrap();
2276        let elf_src = blaze_symbolize_src_elf {
2277            path: path_c.as_ptr(),
2278            debug_syms: true,
2279            ..Default::default()
2280        };
2281        let addrs = [0x2000200];
2282        let result = unsafe {
2283            blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2284        };
2285        assert!(!result.is_null());
2286
2287        let result = unsafe { &*result };
2288        assert_eq!(result.cnt, 1);
2289        let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2290        let sym = &syms[0];
2291        let name = unsafe { CStr::from_ptr(sym.name) };
2292        assert_eq!(name.to_str().unwrap(), "factorial");
2293        let module = unsafe { CStr::from_ptr(sym.module) };
2294        assert!(
2295            module
2296                .to_str()
2297                .unwrap()
2298                .ends_with("test-stable-addrs-stripped-with-link.bin"),
2299            "{module:?}"
2300        );
2301
2302        let () = unsafe { blaze_syms_free(result) };
2303        let () = unsafe { blaze_symbolizer_free(symbolizer) };
2304    }
2305}