miri/shims/
native_lib.rs
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(..)) {
166 let ptr = imm.to_scalar().to_pointer(this)?;
167 let Some(prov) = ptr.provenance else {
168 continue;
170 };
171 if !this.machine.native_call_mem_warned.replace(true) {
173 this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem);
175 }
176
177 this.expose_provenance(prov)?;
178 }
179 }
180
181 this.prepare_exposed_for_native_call()?;
183
184 let libffi_args = libffi_args
186 .iter()
187 .map(|arg| arg.arg_downcast())
188 .collect::<Vec<libffi::high::Arg<'_>>>();
189
190 let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
192 this.write_immediate(*ret, dest)?;
193 interp_ok(true)
194 }
195}
196
197#[derive(Debug, Clone)]
198enum CArg {
204 Int8(i8),
206 Int16(i16),
208 Int32(i32),
210 Int64(i64),
212 ISize(isize),
214 UInt8(u8),
216 UInt16(u16),
218 UInt32(u32),
220 UInt64(u64),
222 USize(usize),
224 RawPtr(*mut std::ffi::c_void),
226}
227
228impl<'a> CArg {
229 fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
231 match self {
232 CArg::Int8(i) => ffi::arg(i),
233 CArg::Int16(i) => ffi::arg(i),
234 CArg::Int32(i) => ffi::arg(i),
235 CArg::Int64(i) => ffi::arg(i),
236 CArg::ISize(i) => ffi::arg(i),
237 CArg::UInt8(i) => ffi::arg(i),
238 CArg::UInt16(i) => ffi::arg(i),
239 CArg::UInt32(i) => ffi::arg(i),
240 CArg::UInt64(i) => ffi::arg(i),
241 CArg::USize(i) => ffi::arg(i),
242 CArg::RawPtr(i) => ffi::arg(i),
243 }
244 }
245}
246
247fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
250 interp_ok(match v.layout.ty.kind() {
251 ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
255 ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
256 ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
257 ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
258 ty::Int(IntTy::Isize) =>
259 CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
260 ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
262 ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
263 ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
264 ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
265 ty::Uint(UintTy::Usize) =>
266 CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
267 ty::RawPtr(..) => {
268 let s = v.to_scalar().to_pointer(cx)?.addr();
269 CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
271 }
272 _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
273 })
274}