blazesym_c/
lib.rs

1//! C API bindings for [`blazesym`]. Please refer to its documentation
2//! for a high level overview of the functionality provided.
3//!
4//! # Error handling
5//! Fallible functions generally return a `NULL` pointer. To provide
6//! users with a better idea of what went wrong, they additionally set a
7//! thread local last [error code][blaze_err]. This error indicates what
8//! kind of issue caused the operation to fail.
9//! A call to a fallible function always overwrites this error code. As
10//! such, please make sure to check the error before making an
11//! additional API call into the library.
12//!
13//! # Thread-Safety
14//! The library does not perform any synchronization of concurrent
15//! accesses to the same object. However, state is strictly kept at the
16//! object level (no shared global state), meaning that while concurrent
17//! accesses to the *same* object (e.g., multiple
18//! [`blaze_symbolize_process_abs_addrs`] calls on the same
19//! [`blaze_symbolizer`] instance) from multiple threads necessitates
20//! serialization at the call site, it is fine to issue requests to
21//! different objects from multiple threads.
22//!
23//! # Compatibility
24//! The library aims to provide forward compatibility with newer
25//! versions and backward compatibility with older ones. To make that
26//! happen, relevant types that are being passed to the library contain
27//! the `type_size` member that is to be set to the type's size, e.g.:
28//! ```c
29#![doc = include_str!("../examples/input-struct-init.c")]
30//! ```
31
32#![allow(
33    non_camel_case_types,
34    clippy::collapsible_if,
35    clippy::fn_to_numeric_cast,
36    clippy::let_and_return,
37    clippy::let_unit_value,
38    clippy::manual_non_exhaustive
39)]
40#![deny(unsafe_op_in_unsafe_fn)]
41
42
43macro_rules! input_zeroed {
44    ($container_ptr:ident, $container_ty:ty) => {{
45        // Each input type's first member is a `usize`.
46        let user_size = unsafe { $container_ptr.cast::<usize>().read() };
47        if user_size < std::mem::size_of_val(&user_size) {
48            false
49        } else {
50            let effective_size = memoffset::offset_of!($container_ty, reserved);
51            unsafe {
52                crate::util::is_mem_zero(
53                    $container_ptr.cast::<u8>().add(effective_size),
54                    user_size.saturating_sub(effective_size),
55                )
56            }
57        }
58    }};
59}
60
61
62macro_rules! input_sanitize {
63    ($container_ptr:ident, $container_ty:ty) => {
64        unsafe {
65            let user_type_size = (*$container_ptr).type_size;
66            if (user_type_size < std::mem::size_of::<$container_ty>()) {
67                let mut obj = std::mem::MaybeUninit::<$container_ty>::uninit();
68                let buf = obj.as_mut_ptr().cast::<u8>();
69                // Copy user data over local copy.
70                let () =
71                    std::ptr::copy_nonoverlapping($container_ptr.cast::<u8>(), buf, user_type_size);
72
73                let () = std::ptr::write_bytes(
74                    buf.add(user_type_size),
75                    0,
76                    std::mem::size_of::<$container_ty>() - user_type_size,
77                );
78                obj.assume_init()
79            } else {
80                $container_ptr.read()
81            }
82        }
83    };
84}
85
86
87mod error;
88mod helper;
89mod inspect;
90mod normalize;
91mod symbolize;
92mod trace;
93mod util;
94
95pub use error::*;
96pub use helper::*;
97pub use inspect::*;
98pub use normalize::*;
99pub use symbolize::*;
100pub use trace::*;
101
102
103#[cfg(test)]
104mod tests {
105    use std::io;
106    use std::path::Path;
107
108
109    /// Check that our C types don't have any padding bytes.
110    #[test]
111    fn no_padding_bytes() {
112        let header = Path::new(&env!("CARGO_MANIFEST_DIR"))
113            .join("include")
114            .join("blazesym.h");
115        let mut data = Vec::new();
116        let bindings = Box::new(&mut data);
117        let () = bindgen::builder()
118            .allowlist_type("blaze.+")
119            .explicit_padding(true)
120            .header(header.to_string_lossy())
121            .generate()
122            .unwrap()
123            .write(bindings as Box<dyn io::Write>)
124            .unwrap();
125
126        let contents = String::from_utf8(data).unwrap();
127        assert!(!contents.contains("padding"), "{contents}");
128    }
129}