1use std::ops::Deref;
3
4use libffi::high::call as ffi;
5use libffi::low::CodePtr;
6use rustc_abi::{BackendRepr, HasDataLayout, Size};
7use rustc_middle::mir::interpret::Pointer;
8use rustc_middle::ty::{self as ty, IntTy, UintTy};
9use rustc_span::Symbol;
10
11use crate::*;
12
13impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
14trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
15 fn call_native_with_args<'a>(
17 &mut self,
18 link_name: Symbol,
19 dest: &MPlaceTy<'tcx>,
20 ptr: CodePtr,
21 libffi_args: Vec<libffi::high::Arg<'a>>,
22 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
23 let this = self.eval_context_mut();
24
25 let scalar = match dest.layout.ty.kind() {
28 ty::Int(IntTy::I8) => {
30 let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) };
34 Scalar::from_i8(x)
35 }
36 ty::Int(IntTy::I16) => {
37 let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) };
38 Scalar::from_i16(x)
39 }
40 ty::Int(IntTy::I32) => {
41 let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) };
42 Scalar::from_i32(x)
43 }
44 ty::Int(IntTy::I64) => {
45 let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) };
46 Scalar::from_i64(x)
47 }
48 ty::Int(IntTy::Isize) => {
49 let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) };
50 Scalar::from_target_isize(x.try_into().unwrap(), this)
51 }
52 ty::Uint(UintTy::U8) => {
54 let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) };
55 Scalar::from_u8(x)
56 }
57 ty::Uint(UintTy::U16) => {
58 let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) };
59 Scalar::from_u16(x)
60 }
61 ty::Uint(UintTy::U32) => {
62 let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) };
63 Scalar::from_u32(x)
64 }
65 ty::Uint(UintTy::U64) => {
66 let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) };
67 Scalar::from_u64(x)
68 }
69 ty::Uint(UintTy::Usize) => {
70 let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) };
71 Scalar::from_target_usize(x.try_into().unwrap(), this)
72 }
73 ty::Tuple(t_list) if t_list.is_empty() => {
76 unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
77 return interp_ok(ImmTy::uninit(dest.layout));
78 }
79 ty::RawPtr(..) => {
80 let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) };
81 let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
82 Scalar::from_pointer(ptr, this)
83 }
84 _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name),
85 };
86 interp_ok(ImmTy::from_scalar(scalar, dest.layout))
87 }
88
89 fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> {
93 let this = self.eval_context_mut();
94 let (lib, lib_path) = this.machine.native_lib.as_ref().unwrap();
96 let func: libloading::Symbol<'_, unsafe extern "C" fn()> =
97 unsafe { lib.get(link_name.as_str().as_bytes()).ok()? };
98 #[expect(clippy::as_conversions)] let fn_ptr = *func.deref() as *mut std::ffi::c_void;
100
101 let mut info = std::mem::MaybeUninit::<libc::Dl_info>::zeroed();
113 unsafe {
114 if libc::dladdr(fn_ptr, info.as_mut_ptr()) != 0 {
115 let info = info.assume_init();
116 #[cfg(target_os = "cygwin")]
117 let fname_ptr = info.dli_fname.as_ptr();
118 #[cfg(not(target_os = "cygwin"))]
119 let fname_ptr = info.dli_fname;
120 assert!(!fname_ptr.is_null());
121 if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap()
122 != lib_path.to_str().unwrap()
123 {
124 return None;
125 }
126 }
127 }
128
129 Some(CodePtr(fn_ptr))
131 }
132}
133
134impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
135pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
136 fn call_native_fn(
142 &mut self,
143 link_name: Symbol,
144 dest: &MPlaceTy<'tcx>,
145 args: &[OpTy<'tcx>],
146 ) -> InterpResult<'tcx, bool> {
147 let this = self.eval_context_mut();
148 let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) {
150 Some(ptr) => ptr,
151 None => {
152 return interp_ok(false);
154 }
155 };
156
157 let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
159 for arg in args.iter() {
160 if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
161 throw_unsup_format!("only scalar argument types are support for native calls")
162 }
163 let imm = this.read_immediate(arg)?;
164 libffi_args.push(imm_to_carg(&imm, this)?);
165 if matches!(arg.layout.ty.kind(), ty::RawPtr(..)) {
168 let ptr = imm.to_scalar().to_pointer(this)?;
169 let Some(prov) = ptr.provenance else {
170 continue;
172 };
173 if !this.machine.native_call_mem_warned.replace(true) {
175 this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
177 }
178
179 this.expose_provenance(prov)?;
180 }
181 }
182
183 this.prepare_exposed_for_native_call()?;
185
186 let libffi_args = libffi_args
188 .iter()
189 .map(|arg| arg.arg_downcast())
190 .collect::<Vec<libffi::high::Arg<'_>>>();
191
192 let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
194 this.write_immediate(*ret, dest)?;
195 interp_ok(true)
196 }
197}
198
199#[derive(Debug, Clone)]
200enum CArg {
206 Int8(i8),
208 Int16(i16),
210 Int32(i32),
212 Int64(i64),
214 ISize(isize),
216 UInt8(u8),
218 UInt16(u16),
220 UInt32(u32),
222 UInt64(u64),
224 USize(usize),
226 RawPtr(*mut std::ffi::c_void),
228}
229
230impl<'a> CArg {
231 fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
233 match self {
234 CArg::Int8(i) => ffi::arg(i),
235 CArg::Int16(i) => ffi::arg(i),
236 CArg::Int32(i) => ffi::arg(i),
237 CArg::Int64(i) => ffi::arg(i),
238 CArg::ISize(i) => ffi::arg(i),
239 CArg::UInt8(i) => ffi::arg(i),
240 CArg::UInt16(i) => ffi::arg(i),
241 CArg::UInt32(i) => ffi::arg(i),
242 CArg::UInt64(i) => ffi::arg(i),
243 CArg::USize(i) => ffi::arg(i),
244 CArg::RawPtr(i) => ffi::arg(i),
245 }
246 }
247}
248
249fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
252 interp_ok(match v.layout.ty.kind() {
253 ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
257 ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
258 ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
259 ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
260 ty::Int(IntTy::Isize) =>
261 CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
262 ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
264 ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
265 ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
266 ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
267 ty::Uint(UintTy::Usize) =>
268 CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
269 ty::RawPtr(..) => {
270 let s = v.to_scalar().to_pointer(cx)?.addr();
271 CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
273 }
274 _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
275 })
276}