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