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}