1 use libffi::{high::call as ffi, low::CodePtr};
4 use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy};
5 use rustc_span::Symbol;
6 use rustc_target::abi::HasDataLayout;
10 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
12 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
13 /// Extract the scalar value from the result of reading a scalar from the machine,
14 /// and convert it to a `CArg`.
16 k: Scalar<Provenance>,
18 cx: &impl HasDataLayout,
19 ) -> InterpResult<'tcx, CArg> {
20 match arg_type.kind() {
21 // If the primitive provided can be converted to a type matching the type pattern
22 // then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
24 ty::Int(IntTy::I8) => {
25 return Ok(CArg::Int8(k.to_i8()?));
27 ty::Int(IntTy::I16) => {
28 return Ok(CArg::Int16(k.to_i16()?));
30 ty::Int(IntTy::I32) => {
31 return Ok(CArg::Int32(k.to_i32()?));
33 ty::Int(IntTy::I64) => {
34 return Ok(CArg::Int64(k.to_i64()?));
36 ty::Int(IntTy::Isize) => {
37 // This will fail if host != target, but then the entire FFI thing probably won't work well
39 return Ok(CArg::ISize(k.to_machine_isize(cx)?.try_into().unwrap()));
42 ty::Uint(UintTy::U8) => {
43 return Ok(CArg::UInt8(k.to_u8()?));
45 ty::Uint(UintTy::U16) => {
46 return Ok(CArg::UInt16(k.to_u16()?));
48 ty::Uint(UintTy::U32) => {
49 return Ok(CArg::UInt32(k.to_u32()?));
51 ty::Uint(UintTy::U64) => {
52 return Ok(CArg::UInt64(k.to_u64()?));
54 ty::Uint(UintTy::Usize) => {
55 // This will fail if host != target, but then the entire FFI thing probably won't work well
57 return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap()));
61 // If no primitives were returned then we have an unsupported type.
63 "unsupported scalar argument type to external C function: {:?}",
68 /// Call external C function and
69 /// store output, depending on return type in the function signature.
70 fn call_external_c_and_store_return<'a>(
73 dest: &PlaceTy<'tcx, Provenance>,
75 libffi_args: Vec<libffi::high::Arg<'a>>,
76 ) -> InterpResult<'tcx, ()> {
77 let this = self.eval_context_mut();
79 // Unsafe because of the call to external C code.
80 // Because this is calling a C function it is not necessarily sound,
81 // but there is no way around this and we've checked as much as we can.
83 // If the return type of a function is a primitive integer type,
84 // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified
85 // primitive integer type, and then write this value out to the miri memory as an integer.
86 match dest.layout.ty.kind() {
88 ty::Int(IntTy::I8) => {
89 let x = ffi::call::<i8>(ptr, libffi_args.as_slice());
90 this.write_int(x, dest)?;
93 ty::Int(IntTy::I16) => {
94 let x = ffi::call::<i16>(ptr, libffi_args.as_slice());
95 this.write_int(x, dest)?;
98 ty::Int(IntTy::I32) => {
99 let x = ffi::call::<i32>(ptr, libffi_args.as_slice());
100 this.write_int(x, dest)?;
103 ty::Int(IntTy::I64) => {
104 let x = ffi::call::<i64>(ptr, libffi_args.as_slice());
105 this.write_int(x, dest)?;
108 ty::Int(IntTy::Isize) => {
109 let x = ffi::call::<isize>(ptr, libffi_args.as_slice());
110 // `isize` doesn't `impl Into<i128>`, so convert manually.
111 // Convert to `i64` since this covers both 32- and 64-bit machines.
112 this.write_int(i64::try_from(x).unwrap(), dest)?;
116 ty::Uint(UintTy::U8) => {
117 let x = ffi::call::<u8>(ptr, libffi_args.as_slice());
118 this.write_int(x, dest)?;
121 ty::Uint(UintTy::U16) => {
122 let x = ffi::call::<u16>(ptr, libffi_args.as_slice());
123 this.write_int(x, dest)?;
126 ty::Uint(UintTy::U32) => {
127 let x = ffi::call::<u32>(ptr, libffi_args.as_slice());
128 this.write_int(x, dest)?;
131 ty::Uint(UintTy::U64) => {
132 let x = ffi::call::<u64>(ptr, libffi_args.as_slice());
133 this.write_int(x, dest)?;
136 ty::Uint(UintTy::Usize) => {
137 let x = ffi::call::<usize>(ptr, libffi_args.as_slice());
138 // `usize` doesn't `impl Into<i128>`, so convert manually.
139 // Convert to `u64` since this covers both 32- and 64-bit machines.
140 this.write_int(u64::try_from(x).unwrap(), dest)?;
143 // Functions with no declared return type (i.e., the default return)
144 // have the output_type `Tuple([])`.
146 if t_list.len() == 0 {
147 ffi::call::<()>(ptr, libffi_args.as_slice());
152 // FIXME ellen! deal with all the other return types
153 throw_unsup_format!("unsupported return type to external C function: {:?}", link_name);
157 /// Get the pointer to the function of the specified name in the shared object file,
158 /// if it exists. The function must be in the shared object file specified: we do *not*
159 /// return pointers to functions in dependencies of the library.
160 fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> {
161 let this = self.eval_context_mut();
162 // Try getting the function from the shared library.
163 // On windows `_lib_path` will be unused, hence the name starting with `_`.
164 let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap();
165 let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe {
166 match lib.get(link_name.as_str().as_bytes()) {
174 // FIXME: this is a hack!
175 // The `libloading` crate will automatically load system libraries like `libc`.
176 // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202
177 // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the
178 // library if it can't find the symbol in the library itself.
179 // So, in order to check if the function was actually found in the specified
180 // `machine.external_so_lib` we need to check its `dli_fname` and compare it to
181 // the specified SO file path.
182 // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`,
183 // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411
184 // using the `libc` crate where this interface is public.
185 // No `libc::dladdr` on windows.
186 let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
188 if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
189 if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
190 != _lib_path.to_str().unwrap()
196 // Return a pointer to the function.
197 Some(CodePtr(*func.deref() as *mut _))
200 /// Call specified external C function, with supplied arguments.
201 /// Need to convert all the arguments from their hir representations to
202 /// a form compatible with C (through `libffi` call).
203 /// Then, convert return from the C call into a corresponding form that
204 /// can be stored in Miri internal memory.
205 fn call_external_c_fct(
208 dest: &PlaceTy<'tcx, Provenance>,
209 args: &[OpTy<'tcx, Provenance>],
210 ) -> InterpResult<'tcx, bool> {
211 // Get the pointer to the function in the shared object file if it exists.
212 let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) {
215 // Shared object file does not export this function -- try the shims next.
220 let this = self.eval_context_mut();
222 // Get the function arguments, and convert them to `libffi`-compatible form.
223 let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
224 for cur_arg in args.iter() {
225 libffi_args.push(Self::scalar_to_carg(
226 this.read_scalar(cur_arg)?,
232 // Convert them to `libffi::high::Arg` type.
233 let libffi_args = libffi_args
235 .map(|cur_arg| cur_arg.arg_downcast())
236 .collect::<Vec<libffi::high::Arg<'_>>>();
238 // Call the function and store output, depending on return type in the function signature.
239 self.call_external_c_and_store_return(link_name, dest, code_ptr, libffi_args)?;
244 #[derive(Debug, Clone)]
245 /// Enum of supported arguments to external C functions.
246 // We introduce this enum instead of just calling `ffi::arg` and storing a list
247 // of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference
248 // to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html
249 // and we need to store a copy of the value, and pass a reference to this copy to C instead.
251 /// 8-bit signed integer.
253 /// 16-bit signed integer.
255 /// 32-bit signed integer.
257 /// 64-bit signed integer.
261 /// 8-bit unsigned integer.
263 /// 16-bit unsigned integer.
265 /// 32-bit unsigned integer.
267 /// 64-bit unsigned integer.
274 /// Convert a `CArg` to a `libffi` argument type.
275 fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
277 CArg::Int8(i) => ffi::arg(i),
278 CArg::Int16(i) => ffi::arg(i),
279 CArg::Int32(i) => ffi::arg(i),
280 CArg::Int64(i) => ffi::arg(i),
281 CArg::ISize(i) => ffi::arg(i),
282 CArg::UInt8(i) => ffi::arg(i),
283 CArg::UInt16(i) => ffi::arg(i),
284 CArg::UInt32(i) => ffi::arg(i),
285 CArg::UInt64(i) => ffi::arg(i),
286 CArg::USize(i) => ffi::arg(i),