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