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();
97 let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe {
98 match lib.get(link_name.as_str().as_bytes()) {
99 Ok(x) => x,
100 Err(_) => {
101 return None;
102 }
103 }
104 };
105
106 let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
118 unsafe {
119 if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
120 if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
121 != _lib_path.to_str().unwrap()
122 {
123 return None;
124 }
125 }
126 }
127 Some(CodePtr(*func.deref() as *mut _))
129 }
130}
131
132impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
133pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
134 fn call_native_fn(
140 &mut self,
141 link_name: Symbol,
142 dest: &MPlaceTy<'tcx>,
143 args: &[OpTy<'tcx>],
144 ) -> InterpResult<'tcx, bool> {
145 let this = self.eval_context_mut();
146 let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) {
148 Some(ptr) => ptr,
149 None => {
150 return interp_ok(false);
152 }
153 };
154
155 let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
157 for arg in args.iter() {
158 if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
159 throw_unsup_format!("only scalar argument types are support for native calls")
160 }
161 let imm = this.read_immediate(arg)?;
162 libffi_args.push(imm_to_carg(&imm, this)?);
163 if matches!(arg.layout.ty.kind(), ty::RawPtr(..)) {
165 let ptr = imm.to_scalar().to_pointer(this)?;
166 let Some(prov) = ptr.provenance else {
167 continue;
169 };
170 let Some(alloc_id) = prov.get_alloc_id() else {
172 continue;
174 };
175 if !this.machine.native_call_mem_warned.replace(true) {
177 this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
179 }
180
181 this.prepare_for_native_call(alloc_id, prov)?;
182 }
183 }
184
185 let libffi_args = libffi_args
190 .iter()
191 .map(|arg| arg.arg_downcast())
192 .collect::<Vec<libffi::high::Arg<'_>>>();
193
194 let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
196 this.write_immediate(*ret, dest)?;
197 interp_ok(true)
198 }
199}
200
201#[derive(Debug, Clone)]
202enum CArg {
208 Int8(i8),
210 Int16(i16),
212 Int32(i32),
214 Int64(i64),
216 ISize(isize),
218 UInt8(u8),
220 UInt16(u16),
222 UInt32(u32),
224 UInt64(u64),
226 USize(usize),
228 RawPtr(*mut std::ffi::c_void),
230}
231
232impl<'a> CArg {
233 fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
235 match self {
236 CArg::Int8(i) => ffi::arg(i),
237 CArg::Int16(i) => ffi::arg(i),
238 CArg::Int32(i) => ffi::arg(i),
239 CArg::Int64(i) => ffi::arg(i),
240 CArg::ISize(i) => ffi::arg(i),
241 CArg::UInt8(i) => ffi::arg(i),
242 CArg::UInt16(i) => ffi::arg(i),
243 CArg::UInt32(i) => ffi::arg(i),
244 CArg::UInt64(i) => ffi::arg(i),
245 CArg::USize(i) => ffi::arg(i),
246 CArg::RawPtr(i) => ffi::arg(i),
247 }
248 }
249}
250
251fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
254 interp_ok(match v.layout.ty.kind() {
255 ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
259 ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
260 ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
261 ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
262 ty::Int(IntTy::Isize) =>
263 CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
264 ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
266 ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
267 ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
268 ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
269 ty::Uint(UintTy::Usize) =>
270 CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
271 ty::RawPtr(..) => {
272 let s = v.to_scalar().to_pointer(cx)?.addr();
273 CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
275 }
276 _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
277 })
278}