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(non_camel_case_types, clippy::manual_non_exhaustive)]
33#![deny(unsafe_op_in_unsafe_fn)]
34
35
36macro_rules! input_zeroed {
37    ($container_ptr:ident, $container_ty:ty) => {{
38        // Each input type's first member is a `usize`.
39        let user_size = unsafe { $container_ptr.cast::<usize>().read() };
40        if user_size < std::mem::size_of_val(&user_size) {
41            false
42        } else {
43            let effective_size = memoffset::offset_of!($container_ty, reserved);
44            unsafe {
45                crate::util::is_mem_zero(
46                    $container_ptr.cast::<u8>().add(effective_size),
47                    user_size.saturating_sub(effective_size),
48                )
49            }
50        }
51    }};
52}
53
54
55macro_rules! input_sanitize {
56    ($container_ptr:ident, $container_ty:ty) => {
57        unsafe {
58            let user_type_size = (*$container_ptr).type_size;
59            if (user_type_size < std::mem::size_of::<$container_ty>()) {
60                let mut obj = std::mem::MaybeUninit::<$container_ty>::uninit();
61                let buf = obj.as_mut_ptr().cast::<u8>();
62                // Copy user data over local copy.
63                let () =
64                    std::ptr::copy_nonoverlapping($container_ptr.cast::<u8>(), buf, user_type_size);
65
66                let () = std::ptr::write_bytes(
67                    buf.add(user_type_size),
68                    0,
69                    std::mem::size_of::<$container_ty>() - user_type_size,
70                );
71                obj.assume_init()
72            } else {
73                $container_ptr.read()
74            }
75        }
76    };
77}
78
79
80mod error;
81mod helper;
82mod inspect;
83mod normalize;
84mod symbolize;
85mod trace;
86mod util;
87
88pub use error::*;
89pub use helper::*;
90pub use inspect::*;
91pub use normalize::*;
92pub use symbolize::*;
93pub use trace::*;
94
95
96#[cfg(test)]
97mod tests {
98    use std::io;
99    use std::path::Path;
100
101
102    /// Check that our C types don't have any padding bytes.
103    #[test]
104    fn no_padding_bytes() {
105        let header = Path::new(&env!("CARGO_MANIFEST_DIR"))
106            .join("include")
107            .join("blazesym.h");
108        let mut data = Vec::new();
109        let bindings = Box::new(&mut data);
110        let () = bindgen::builder()
111            .allowlist_type("blaze.+")
112            .explicit_padding(true)
113            .header(header.to_string_lossy())
114            .generate()
115            .unwrap()
116            .write(bindings as Box<dyn io::Write>)
117            .unwrap();
118
119        let contents = String::from_utf8(data).unwrap();
120        assert!(!contents.contains("padding"), "{contents}");
121    }
122}