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}