1use std::{env, ffi::CString, os::raw::c_void, sync::OnceLock};
2use tracing::debug;
3
4pub use libc;
6pub use once_cell;
7pub use paste;
8pub use tracing;
9
10struct DlHandle(*mut c_void);
13unsafe impl Send for DlHandle {}
14unsafe impl Sync for DlHandle {}
15
16static CUDA_LIB: OnceLock<DlHandle> = OnceLock::new();
17static CUDART_LIB: OnceLock<DlHandle> = OnceLock::new();
18
19fn get_libcuda() -> *mut c_void {
20 let handle_wrapper = CUDA_LIB.get_or_init(|| unsafe {
21 let mut paths = vec![
22 "/usr/local/cuda/compat/libcuda.so".to_string(),
23 "/usr/lib/x86_64-linux-gnu/libcuda.so".to_string(),
24 "/usr/lib64/libcuda.so".to_string(),
25 "/usr/local/cuda/targets/x86_64-linux/lib/stubs/libcuda.so".to_string(),
26 ];
27
28 if let Some(cuda_home) = env::var_os("CUDA_HOME") {
29 let path = format!("{}/compat/libcuda.so", cuda_home.to_string_lossy());
30 paths.insert(0, path);
31 }
32
33 for path in paths.iter() {
34 let s = CString::new(path.clone()).unwrap();
35 let flags = libc::RTLD_NOW | libc::RTLD_LOCAL | libc::RTLD_NODELETE;
36 let handle = libc::dlopen(s.as_ptr(), flags);
37 if !handle.is_null() {
38 debug!("Loaded real CUDA driver from: {}", path);
39 return DlHandle(handle);
40 }
41 }
42
43 panic!("Failed to find/load libcuda.so. Ensure it is in LD_LIBRARY_PATH.");
44 });
45 handle_wrapper.0
46}
47
48fn get_libcudart() -> *mut c_void {
49 let handle_wrapper = CUDART_LIB.get_or_init(|| unsafe {
50 let mut paths = vec![
51 "/usr/local/cuda/targets/x86_64-linux/lib/libcudart.so".to_string(),
52 "/usr/lib/x86_64-linux-gnu/libcudart.so".to_string(),
53 "/usr/lib64/libcudart.so".to_string(),
54 ];
55
56 if let Some(cuda_home) = env::var_os("CUDA_HOME") {
58 let path = format!("{}/targets/x86_64-linux/lib/libcudart.so", cuda_home.to_string_lossy());
59 paths.insert(0, path);
60 }
61
62 for path in paths.iter() {
63 let s = CString::new(path.clone()).unwrap();
64 let flags = libc::RTLD_NOW | libc::RTLD_LOCAL | libc::RTLD_NODELETE;
65 let handle = libc::dlopen(s.as_ptr(), flags);
66 if !handle.is_null() {
67 debug!("Loaded real CUDA runtime from: {}", path);
68 return DlHandle(handle);
69 }
70 }
71
72 panic!("Failed to find/load libcudart.so. Ensure CUDA Toolkit is installed and in LD_LIBRARY_PATH.");
73 });
74 handle_wrapper.0
75}
76
77pub fn dlsym_next(symbol: &[u8]) -> *mut c_void {
78 let sym_str = std::str::from_utf8(symbol).unwrap_or("");
79
80 let handle = if sym_str.starts_with("cuda") || sym_str.starts_with("__cuda") {
82 get_libcudart()
83 } else {
84 get_libcuda()
85 };
86
87 unsafe { libc::dlsym(handle, symbol.as_ptr() as *const _) }
88}
89
90#[macro_export]
95macro_rules! install_hooks {
96 () => {
97 use std::{
98 ffi::CStr,
99 os::raw::{c_char, c_int, c_void},
100 };
101 use tracing::debug;
102
103 fn get_local_hook(name: &str) -> Option<*mut $crate::libc::c_void> {
104 let hook_fn = include!(concat!(env!("OUT_DIR"), "/hook_map.rs"));
106 hook_fn(name)
107 }
108
109 type CUresult = u32; type CUdriverProcAddressQueryResult = u32; $crate::cuda_hook! {
113 pub unsafe extern "C" fn cuGetProcAddress_v2(
114 symbol: *const $crate::libc::c_char,
115 pfn: *mut *mut $crate::libc::c_void,
116 cuda_version: $crate::libc::c_int,
117 flags: u64,
118 symbol_status: *mut CUdriverProcAddressQueryResult
119 ) -> CUresult {
120 let sym_name_c = unsafe { ::std::ffi::CStr::from_ptr(symbol) };
121 let sym_name = sym_name_c.to_string_lossy();
122
123 let real_fn = *__real_cuGetProcAddress_v2;
125 let ret = unsafe { real_fn(symbol, pfn, cuda_version, flags, symbol_status) };
126
127 if let Some(our_ptr) = get_local_hook(&sym_name) {
129 $crate::tracing::debug!("Hooking symbol via cuGetProcAddress: {}", sym_name);
130 unsafe { *pfn = our_ptr };
131 return 0; }
133
134 ret
135 }
136 }
137
138 $crate::cuda_hook! {
139 pub unsafe extern "C" fn cuGetProcAddress(
140 symbol: *const $crate::libc::c_char,
141 pfn: *mut *mut $crate::libc::c_void,
142 cuda_version: $crate::libc::c_int,
143 flags: u64,
144 symbol_status: *mut CUdriverProcAddressQueryResult
145 ) -> CUresult {
146 let sym_name_c = unsafe { ::std::ffi::CStr::from_ptr(symbol) };
147 let sym_name = sym_name_c.to_string_lossy();
148
149 let real_fn = *__real_cuGetProcAddress;
150 let ret = unsafe { real_fn(symbol, pfn, cuda_version, flags, symbol_status) };
151
152 if let Some(our_ptr) = get_local_hook(&sym_name) {
153 $crate::tracing::debug!("Hooking symbol via cuGetProcAddress: {}", sym_name);
154 unsafe { *pfn = our_ptr };
155 return 0; }
157
158 ret
159 }
160 }
161 };
162}
163
164#[macro_export]
165macro_rules! cuda_hook {
166 (
167 pub unsafe extern "C" fn $fname:ident( $($arg:ident : $arg_ty:ty),* $(,)? )
168 -> $ret:ty
169 $body:block
170 ) => {
171 $crate::paste::paste! {
172 #[allow(non_upper_case_globals)]
173 pub static [<__real_ $fname>]: $crate::once_cell::sync::Lazy<
174 unsafe extern "C" fn($($arg_ty),*) -> $ret
175 > = $crate::once_cell::sync::Lazy::new(|| {
176 let name = concat!(stringify!($fname), "\0");
177 let sym = $crate::dlsym_next(name.as_bytes());
178 if sym.is_null() {
179 panic!("Missing symbol: {}", stringify!($fname));
180 }
181 unsafe { std::mem::transmute(sym) }
182 });
183
184 #[unsafe(no_mangle)]
185 pub unsafe extern "C" fn $fname( $($arg : $arg_ty),* ) -> $ret {
186 $body
187 }
188 }
189 };
190}
191
192#[macro_export]
193macro_rules! generate_proxy {
194 (
196 @generate_alias
197 alias: $alias:ident,
198 target_fn: $fname:ident,
199 args: ( [ $( ($arg:ident : $arg_ty:ty) ),* ] ),
200 ret: $ret:ty
201 ) => {
202 $crate::paste::paste! {
203 #[unsafe(no_mangle)]
204 pub unsafe extern "C" fn $alias( $( $arg : $arg_ty ),* ) -> $ret {
205 let f = *[<__REAL_ $fname:upper>];
206 f( $( $arg ),* )
207 }
208 }
209 };
210
211 (
213 @recurse_aliases
214 target_fn: $fname:ident,
215 ret: $ret:ty,
216 args_tt: $args_tt:tt,
217 aliases: [ $($alias:ident),* ]
218 ) => {
219 $(
220 $crate::generate_proxy!(
221 @generate_alias
222 alias: $alias,
223 target_fn: $fname,
224 args: $args_tt,
225 ret: $ret
226 );
227 )*
228 };
229
230 (
232 @generate_main
233 fn $fname:ident ( [ $( ($arg:ident : $arg_ty:ty) ),* ] ) -> $ret:ty;
234 target_symbol: $real_sym:ident
235 ) => {
236 $crate::paste::paste! {
237 static [<__REAL_ $fname:upper>]: $crate::once_cell::sync::Lazy<
238 extern "C" fn( $($arg_ty),* ) -> $ret
239 > = $crate::once_cell::sync::Lazy::new(|| {
240 let name = concat!(stringify!($real_sym), "\0");
241 let ptr = $crate::dlsym_next(name.as_bytes());
242 if ptr.is_null() {
243 eprintln!("fatal: symbol '{}' not found in underlying library", name);
244 std::process::abort();
245 }
246 unsafe { std::mem::transmute(ptr) }
247 });
248
249 #[unsafe(no_mangle)]
250 pub extern "C" fn $fname( $( $arg : $arg_ty ),* ) -> $ret {
251 let f = *[<__REAL_ $fname:upper>];
252 f( $( $arg ),* )
253 }
254 }
255 };
256
257 (
259 fn $fname:ident $args_tt:tt -> $ret:ty;
260 name: $real_sym:ident
261 $(, aliases: $($alias:ident),* )?
262 ) => {
263 $crate::generate_proxy!(
264 @generate_main
265 fn $fname $args_tt -> $ret;
266 target_symbol: $real_sym
267 );
268 $(
269 $crate::generate_proxy!(
270 @recurse_aliases
271 target_fn: $fname,
272 ret: $ret,
273 args_tt: $args_tt,
274 aliases: [ $($alias),* ]
275 );
276 )?
277 };
278}