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);
12unsafe impl Send for DlHandle {}
13unsafe impl Sync for DlHandle {}
14
15static CUDA_LIB: OnceLock<DlHandle> = OnceLock::new();
16
17fn get_libcuda() -> *mut c_void {
18 let handle_wrapper = CUDA_LIB.get_or_init(|| unsafe {
19 let mut paths = vec![
20 "/usr/local/cuda/compat/libcuda.so".to_string(),
21 "/usr/lib/x86_64-linux-gnu/libcuda.so".to_string(),
22 "/usr/lib64/libcuda.so".to_string(),
23 "/usr/local/cuda/targets/x86_64-linux/lib/stubs/libcuda.so".to_string(),
24 ];
25
26 if let Some(cuda_home) = env::var_os("CUDA_HOME") {
27 let path = format!("{}/compat/libcuda.so", cuda_home.to_string_lossy());
28 paths.insert(0, path);
29 }
30
31 for path in paths.iter() {
32 let s = CString::new(path.clone()).unwrap();
33 let flags = libc::RTLD_NOW | libc::RTLD_LOCAL | libc::RTLD_NODELETE;
34 let handle = libc::dlopen(s.as_ptr(), flags);
35 if !handle.is_null() {
36 debug!("Loaded real CUDA driver from: {}", path);
37 return DlHandle(handle);
38 }
39 }
40 panic!("Failed to find/load libcuda.so. Ensure it is in LD_LIBRARY_PATH.");
41 });
42 handle_wrapper.0
43}
44
45pub fn dlsym_next(symbol: &[u8]) -> *mut c_void {
46 let handle = get_libcuda();
47 unsafe { libc::dlsym(handle, symbol.as_ptr() as *const _) }
48}
49
50#[macro_export]
55macro_rules! install_hooks {
56 () => {
57 use std::{
58 ffi::CStr,
59 os::raw::{c_char, c_int, c_void},
60 };
61 use tracing::debug;
62
63 fn get_local_hook(name: &str) -> Option<*mut $crate::libc::c_void> {
64 let hook_fn = include!(concat!(env!("OUT_DIR"), "/hook_map.rs"));
66 hook_fn(name)
67 }
68
69 type CUresult = u32; type CUdriverProcAddressQueryResult = u32; $crate::cuda_hook! {
73 pub unsafe extern "C" fn cuGetProcAddress_v2(
74 symbol: *const $crate::libc::c_char,
75 pfn: *mut *mut $crate::libc::c_void,
76 cuda_version: $crate::libc::c_int,
77 flags: u64,
78 symbol_status: *mut CUdriverProcAddressQueryResult
79 ) -> CUresult {
80 let sym_name_c = unsafe { ::std::ffi::CStr::from_ptr(symbol) };
81 let sym_name = sym_name_c.to_string_lossy();
82
83 let real_fn = *__real_cuGetProcAddress_v2;
85 let ret = unsafe { real_fn(symbol, pfn, cuda_version, flags, symbol_status) };
86
87 if let Some(our_ptr) = get_local_hook(&sym_name) {
89 $crate::tracing::debug!("Hooking symbol: {}", sym_name);
90 unsafe { *pfn = our_ptr };
91 return 0; }
93 ret
94 }
95 }
96
97 $crate::cuda_hook! {
98 pub unsafe extern "C" fn cuGetProcAddress(
99 symbol: *const $crate::libc::c_char,
100 pfn: *mut *mut $crate::libc::c_void,
101 cuda_version: $crate::libc::c_int,
102 flags: u64,
103 symbol_status: *mut CUdriverProcAddressQueryResult
104 ) -> CUresult {
105 let sym_name_c = unsafe { ::std::ffi::CStr::from_ptr(symbol) };
106 let sym_name = sym_name_c.to_string_lossy();
107
108 let real_fn = *__real_cuGetProcAddress;
109 let ret = unsafe { real_fn(symbol, pfn, cuda_version, flags, symbol_status) };
110
111 if let Some(our_ptr) = get_local_hook(&sym_name) {
112 $crate::tracing::debug!("Hooking symbol: {}", sym_name);
113 unsafe { *pfn = our_ptr };
114 return 0; }
116 ret
117 }
118 }
119 };
120}
121
122#[macro_export]
123macro_rules! cuda_hook {
124 (
125 pub unsafe extern "C" fn $fname:ident( $($arg:ident : $arg_ty:ty),* $(,)? )
126 -> $ret:ty
127 $body:block
128 ) => {
129 $crate::paste::paste! {
130 #[allow(non_upper_case_globals)]
131 pub static [<__real_ $fname>]: $crate::once_cell::sync::Lazy<
132 unsafe extern "C" fn($($arg_ty),*) -> $ret
133 > = $crate::once_cell::sync::Lazy::new(|| {
134 let name = concat!(stringify!($fname), "\0");
135 let sym = $crate::dlsym_next(name.as_bytes());
136 if sym.is_null() {
137 panic!("Missing symbol: {}", stringify!($fname));
138 }
139 unsafe { std::mem::transmute(sym) }
140 });
141
142 #[unsafe(no_mangle)]
143 pub unsafe extern "C" fn $fname( $($arg : $arg_ty),* ) -> $ret {
144 $body
145 }
146 }
147 };
148}
149
150#[macro_export]
151macro_rules! generate_proxy {
152 (
154 @generate_alias
155 alias: $alias:ident,
156 target_fn: $fname:ident,
157 args: ( [ $( ($arg:ident : $arg_ty:ty) ),* ] ),
158 ret: $ret:ty
159 ) => {
160 $crate::paste::paste! {
161 #[unsafe(no_mangle)]
162 pub unsafe extern "C" fn $alias( $( $arg : $arg_ty ),* ) -> $ret {
163 let f = *[<__REAL_ $fname:upper>];
164 f( $( $arg ),* )
165 }
166 }
167 };
168
169 (
171 @recurse_aliases
172 target_fn: $fname:ident,
173 ret: $ret:ty,
174 args_tt: $args_tt:tt,
175 aliases: [ $($alias:ident),* ]
176 ) => {
177 $(
178 $crate::generate_proxy!(
179 @generate_alias
180 alias: $alias,
181 target_fn: $fname,
182 args: $args_tt,
183 ret: $ret
184 );
185 )*
186 };
187
188 (
190 @generate_main
191 fn $fname:ident ( [ $( ($arg:ident : $arg_ty:ty) ),* ] ) -> $ret:ty;
192 target_symbol: $real_sym:ident
193 ) => {
194 $crate::paste::paste! {
195 static [<__REAL_ $fname:upper>]: $crate::once_cell::sync::Lazy<
196 extern "C" fn( $($arg_ty),* ) -> $ret
197 > = $crate::once_cell::sync::Lazy::new(|| {
198 let name = concat!(stringify!($real_sym), "\0");
199 let ptr = $crate::dlsym_next(name.as_bytes());
200 if ptr.is_null() {
201 eprintln!("fatal: symbol '{}' not found in underlying libcuda", name);
202 std::process::abort();
203 }
204 unsafe { std::mem::transmute(ptr) }
205 });
206
207 #[unsafe(no_mangle)]
208 pub extern "C" fn $fname( $( $arg : $arg_ty ),* ) -> $ret {
209 let f = *[<__REAL_ $fname:upper>];
210 f( $( $arg ),* )
211 }
212 }
213 };
214
215 (
217 fn $fname:ident $args_tt:tt -> $ret:ty;
218 name: $real_sym:ident
219 $(, aliases: $($alias:ident),* )?
220 ) => {
221 $crate::generate_proxy!(
222 @generate_main
223 fn $fname $args_tt -> $ret;
224 target_symbol: $real_sym
225 );
226
227 $(
228 $crate::generate_proxy!(
229 @recurse_aliases
230 target_fn: $fname,
231 ret: $ret,
232 args_tt: $args_tt,
233 aliases: [ $($alias),* ]
234 );
235 )?
236 };
237}