calltrace/
build_validator.rs1use libc::{dladdr, Dl_info};
7use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
8use std::sync::Once;
9use std::time::Duration;
10
11use crate::error::Result;
12
13static VALIDATION_INIT: Once = Once::new();
15static FUNCTION_HOOKS_DETECTED: AtomicBool = AtomicBool::new(false);
16static SYMBOL_RESOLUTION_AVAILABLE: AtomicBool = AtomicBool::new(false);
17static HOOK_CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
18
19const VALIDATION_TIMEOUT_MS: u64 = 200;
21
22#[derive(Debug)]
24pub struct BuildValidation {
25 pub finstrument_functions_ok: bool,
26 pub rdynamic_ok: bool,
27 pub validation_complete: bool,
28 pub error_messages: Vec<String>,
29}
30
31pub fn init_build_validation() -> Result<()> {
33 VALIDATION_INIT.call_once(|| {
34 test_symbol_resolution();
36
37 std::thread::spawn(|| {
39 validate_build_requirements();
40 });
41
42 std::thread::spawn(|| {
44 std::thread::sleep(Duration::from_millis(100));
45 let _preliminary_result = get_build_validation_result();
46 });
48 });
49
50 Ok(())
51}
52
53fn test_symbol_resolution() {
55 let mut symbol_found = false;
58
59 unsafe {
60 let main_sym = libc::dlsym(
62 libc::RTLD_DEFAULT,
63 b"main\0".as_ptr() as *const libc::c_char,
64 );
65 if !main_sym.is_null() {
66 let mut dl_info: Dl_info = std::mem::zeroed();
67 let result = dladdr(main_sym, &mut dl_info);
68
69 if result != 0 && !dl_info.dli_sname.is_null() {
70 symbol_found = true;
71 }
72 }
73
74 if !symbol_found {
76 if let Ok(exe_path) = std::env::current_exe() {
78 if let Some(_exe_path_str) = exe_path.to_str() {
79 let func_names = [c"main", c"_start", c"__libc_start_main"];
81 for func_name in func_names {
82 let sym = libc::dlsym(libc::RTLD_DEFAULT, func_name.as_ptr());
83 if !sym.is_null() {
84 let mut dl_info: Dl_info = std::mem::zeroed();
85 let result = dladdr(sym, &mut dl_info);
86
87 if result != 0 && !dl_info.dli_fname.is_null() {
89 let fname =
90 std::ffi::CStr::from_ptr(dl_info.dli_fname).to_string_lossy();
91 if fname.contains(
92 &exe_path.file_name().unwrap().to_string_lossy().to_string(),
93 ) {
94 symbol_found = true;
95 break;
96 }
97 }
98 }
99 }
100 }
101 }
102 }
103 }
104
105 SYMBOL_RESOLUTION_AVAILABLE.store(symbol_found, Ordering::Relaxed);
106}
107
108pub fn record_function_hook_call() {
110 HOOK_CALL_COUNT.fetch_add(1, Ordering::Relaxed);
111 FUNCTION_HOOKS_DETECTED.store(true, Ordering::Relaxed);
112}
113
114fn validate_build_requirements() {
116 std::thread::sleep(Duration::from_millis(VALIDATION_TIMEOUT_MS));
118
119 let _validation_result = get_build_validation_result();
121
122 }
124
125pub fn get_build_validation_result() -> BuildValidation {
127 let finstrument_ok = FUNCTION_HOOKS_DETECTED.load(Ordering::Relaxed);
128 let rdynamic_ok = SYMBOL_RESOLUTION_AVAILABLE.load(Ordering::Relaxed);
129 let hook_count = HOOK_CALL_COUNT.load(Ordering::Relaxed);
130
131 let mut error_messages = Vec::new();
132
133 if !finstrument_ok {
134 error_messages.push(format!(
135 "Function instrumentation not detected (received {} hook calls). \
136 Application was not built with -finstrument-functions flag.",
137 hook_count
138 ));
139 }
140
141 if !rdynamic_ok {
142 error_messages.push(
143 "Symbol resolution not available. \
144 Application was not built with -rdynamic flag."
145 .to_string(),
146 );
147 }
148
149 BuildValidation {
150 finstrument_functions_ok: finstrument_ok,
151 rdynamic_ok,
152 validation_complete: true,
153 error_messages,
154 }
155}
156
157pub fn is_validation_complete() -> bool {
159 VALIDATION_INIT.is_completed()
160}
161
162#[cfg(test)]
164pub fn force_validation_check() -> BuildValidation {
165 test_symbol_resolution();
166 get_build_validation_result()
167}