1use rustc_abi::ExternAbi;
4use rustc_middle::ty::{Binder, FnSig, FnSigKind, Ty};
5use rustc_span::Symbol;
6use rustc_target::callconv::FnAbi;
7
8use crate::*;
9
10pub struct ShimSig<'tcx, const ARGS: usize> {
12 pub abi: ExternAbi,
13 pub args: [Ty<'tcx>; ARGS],
14 pub ret: Ty<'tcx>,
15 pub c_variadic: bool,
16}
17
18#[macro_export]
30macro_rules! shim_sig {
31 (extern $abi:literal fn($($args:tt)*) -> $($ret:tt)*) => {
32 |ecx| {
33 let (args, c_variadic) = shim_sig_args_sep!(ecx, [$($args)*]);
34 $crate::shims::sig::ShimSig {
35 abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
36 args,
37 ret: shim_sig_arg!(ecx, $($ret)*),
38 c_variadic,
39 }
40 }
41 };
42}
43
44#[macro_export]
46macro_rules! shim_varargs {
47 ($($args:tt)*) => {
48 |ecx| {
49 let (args, c_variadic) = shim_sig_args_sep!(ecx, [$($args)*]);
50 assert!(!c_variadic); args
52 }
53 };
54}
55
56#[macro_export]
69macro_rules! shim_sig_args_sep {
70 ($ecx:ident, [$($tt:tt)*]) => {
71 shim_sig_args_sep!(@ $ecx [] [] $($tt)*)
72 };
73
74 (@ $ecx:ident [$($final:tt)*] [$($collected:tt)*] , $($tt:tt)*) => {
82 shim_sig_args_sep!(@ $ecx [$($final)* shim_sig_arg!($ecx, $($collected)*), ] [] $($tt)*)
83 };
84 (@ $ecx:ident [$($final:tt)*] [$($collected:tt)*] $first:tt $($tt:tt)*) => {
86 shim_sig_args_sep!(@ $ecx [$($final)*] [$($collected)* $first] $($tt)*)
87 };
88 (@ $ecx:ident [$($final:tt)*] [...] ) => {
90 ([$($final)*], true)
91 };
92 (@ $ecx:ident [$($final:tt)*] [$($collected:tt)+] ) => {
94 ([$($final)* shim_sig_arg!($ecx, $($collected)*)], false)
95 };
96 (@ $ecx:ident [$($final:tt)*] [] ) => {
98 ([$($final)*], false)
99 };
100}
101
102#[macro_export]
106macro_rules! shim_sig_arg {
107 ($ecx:ident, i8) => {
108 $ecx.tcx.types.i8
109 };
110 ($ecx:ident, i16) => {
111 $ecx.tcx.types.i16
112 };
113 ($ecx:ident, i32) => {
114 $ecx.tcx.types.i32
115 };
116 ($ecx:ident, i64) => {
117 $ecx.tcx.types.i64
118 };
119 ($ecx:ident, i128) => {
120 $ecx.tcx.types.i128
121 };
122 ($ecx:ident, isize) => {
123 $ecx.tcx.types.isize
124 };
125 ($ecx:ident, u8) => {
126 $ecx.tcx.types.u8
127 };
128 ($ecx:ident, u16) => {
129 $ecx.tcx.types.u16
130 };
131 ($ecx:ident, u32) => {
132 $ecx.tcx.types.u32
133 };
134 ($ecx:ident, u64) => {
135 $ecx.tcx.types.u64
136 };
137 ($ecx:ident, u128) => {
138 $ecx.tcx.types.u128
139 };
140 ($ecx:ident, usize) => {
141 $ecx.tcx.types.usize
142 };
143 ($ecx:ident, f32) => {
144 $ecx.tcx.types.f32
145 };
146 ($ecx:ident, f64) => {
147 $ecx.tcx.types.f64
148 };
149 ($ecx:ident, ()) => {
150 $ecx.tcx.types.unit
151 };
152 ($ecx:ident, !) => {
153 $ecx.tcx.types.never
154 };
155 ($ecx:ident, bool) => {
156 $ecx.tcx.types.bool
157 };
158 ($ecx:ident, *_) => {
159 $ecx.machine.layouts.void_ptr_mut.ty
161 };
162 ($ecx:ident, *$($ty:tt)*) => {
163 rustc_middle::ty::Ty::new_ptr(
166 *$ecx.tcx,
167 shim_sig_arg!($ecx, $($ty)*),
168 rustc_middle::mir::Mutability::Mut,
169 )
170 };
171 ($ecx:ident, fn(..) -> _) => {
172 $ecx.machine.layouts.fn_ptr.ty
174 };
175 ($ecx:ident, &[$($ty:tt)*]) => {
176 rustc_middle::ty::Ty::new_ref(
177 *$ecx.tcx,
178 $ecx.tcx.lifetimes.re_erased,
179 rustc_middle::ty::Ty::new_slice(*$ecx.tcx, shim_sig_arg!($ecx, $($ty)*)),
180 rustc_middle::mir::Mutability::Not,
181 )
182 };
183 ($ecx:ident, winapi::$ty:ident) => {
184 $ecx.windows_ty_layout(stringify!($ty)).ty
185 };
186 ($ecx:ident, $krate:ident :: $($path:ident)::+) => {
187 helpers::path_ty_layout($ecx, &[stringify!($krate), $(stringify!($path)),*]).ty
188 };
189 ($ecx:ident, $($other:tt)*) => {
190 compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
191 }
192}
193
194impl<'tcx, const ARGS: usize> ShimSig<'tcx, ARGS> {
195 fn as_abi(&self, ecx: &MiriInterpCx<'tcx>) -> &FnAbi<'tcx, Ty<'tcx>> {
196 let mut inputs_and_output = Vec::with_capacity(ARGS.strict_add(1));
197 inputs_and_output.extend(&self.args);
198 inputs_and_output.push(self.ret);
199 let fn_sig_binder = Binder::dummy(FnSig {
200 inputs_and_output: ecx.machine.tcx.mk_type_list(&inputs_and_output),
201 fn_sig_kind: FnSigKind::default().set_c_variadic(self.c_variadic).set_abi(self.abi),
202 });
203 ecx.fn_abi_of_fn_ptr(fn_sig_binder, Default::default()).unwrap()
204 }
205}
206
207fn check_shim_abi<'tcx>(
209 ecx: &MiriInterpCx<'tcx>,
210 link_name: Symbol,
211 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
212 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
213) -> InterpResult<'tcx> {
214 if callee_abi.conv != caller_abi.conv {
215 throw_ub_format!(
216 r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#,
217 callee = callee_abi.conv,
218 caller = caller_abi.conv,
219 );
220 }
221 if caller_abi.c_variadic && !callee_abi.c_variadic {
225 throw_ub_format!(
226 "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a c-variadic signature"
227 );
228 }
229 if !caller_abi.c_variadic && callee_abi.c_variadic {
230 throw_ub_format!(
231 "ABI mismatch: `{link_name}` is a c-variadic function, but the caller is using a non-variadic signature"
232 );
233 }
234
235 if callee_abi.fixed_count != caller_abi.fixed_count {
236 throw_ub_format!(
237 "ABI mismatch: calling `{link_name}` which takes {} {}argument{}, but {} argument{} given",
238 callee_abi.fixed_count,
239 if callee_abi.c_variadic { "fixed (non-variadic) " } else { "" },
240 if callee_abi.fixed_count == 1 { "" } else { "s" },
241 caller_abi.fixed_count,
242 if caller_abi.fixed_count == 1 { " was" } else { "s were" },
243 );
244 }
245
246 if !ecx.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
247 throw_ub!(AbiMismatchReturn {
248 caller_ty: caller_abi.ret.layout.ty,
249 callee_ty: callee_abi.ret.layout.ty
250 });
251 }
252
253 for (idx, (caller_arg, callee_arg)) in
254 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
255 {
256 if !ecx.check_argument_compat(caller_arg, callee_arg)? {
257 throw_ub!(AbiMismatchArgument {
258 arg_idx: idx,
259 caller_ty: caller_abi.args[idx].layout.ty,
260 callee_ty: callee_abi.args[idx].layout.ty
261 });
262 }
263 }
264
265 interp_ok(())
266}
267
268pub struct Varargs<'tcx, 'a> {
271 args: &'a [OpTy<'tcx>],
272 already_gone: usize,
274}
275
276impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
277pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
278 fn check_shim_symbol_clash(&self, link_name: Symbol) -> InterpResult<'tcx, ()> {
280 let this = self.eval_context_ref();
281 if let Some(instance) = this.lookup_exported_symbol(link_name)? {
282 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
288 return interp_ok(());
289 }
290
291 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
292 link_name,
293 span: this.tcx.def_span(instance.def_id()),
294 })
295 }
296 interp_ok(())
297 }
298
299 fn check_shim_sig<const N: usize>(
302 &self,
303 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
304 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &[OpTy<'tcx>]),
306 ) -> InterpResult<'tcx, [OpTy<'tcx>; N]> {
307 let this = self.eval_context_ref();
308
309 let shim_sig = shim_sig(this);
311 assert!(!shim_sig.c_variadic);
312 let callee_fn_abi = shim_sig.as_abi(this);
313
314 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
316 this.check_shim_symbol_clash(link_name)?;
317
318 let transmuted = std::array::try_from_fn(|i| {
320 caller_args[i].transmute(callee_fn_abi.args[i].layout, this)
321 })?;
322 interp_ok(transmuted)
323 }
324
325 fn check_shim_sig_variadic<'a, const N: usize>(
329 &self,
330 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
331 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
333 ) -> InterpResult<'tcx, ([OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
334 let this = self.eval_context_ref();
335
336 let shim_sig = shim_sig(this);
338 assert!(shim_sig.c_variadic);
339 let callee_fn_abi = shim_sig.as_abi(this);
340
341 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
343 this.check_shim_symbol_clash(link_name)?;
344
345 if let Some((fixed, var)) = caller_args.split_first_chunk::<N>() {
347 let transmuted = std::array::try_from_fn(|i| {
348 fixed[i].transmute(callee_fn_abi.args[i].layout, this)
349 })?;
350 return interp_ok((transmuted, Varargs { args: var, already_gone: N }));
351 }
352 unreachable!()
353 }
354
355 fn check_varargs<'a, const N: usize>(
359 &self,
360 tys: fn(&MiriInterpCx<'tcx>) -> [Ty<'tcx>; N],
361 varargs: Varargs<'tcx, 'a>,
362 fn_name: &str,
363 ) -> InterpResult<'tcx, ([OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
364 let this = self.eval_context_ref();
365 let tys = tys(this);
366
367 let Some((now, tail)) = varargs.args.split_first_chunk::<N>() else {
368 throw_ub_format!(
369 "not enough arguments for `{fn_name}`: got {}, expected at least {}",
370 varargs.already_gone.strict_add(varargs.args.len()),
371 varargs.already_gone.strict_add(N),
372 )
373 };
374
375 for (n, (caller_gave, callee_expected)) in now.iter().zip(tys).enumerate() {
376 let compatible =
378 this.validate_c_variadic_compatible_ty(caller_gave.layout.ty, callee_expected)?;
379 match compatible {
380 VarArgCompatible::Compatible => {}
381 VarArgCompatible::Incompatible => {
382 throw_ub_format!(
383 "incorrect c-variadic argument type for `{fn_name}`: \
384 expected argument #{n} to have type `{callee_expected}` but got incompatible type `{caller_ty}`",
385 n = varargs.already_gone.strict_add(n).strict_add(1),
386 caller_ty = caller_gave.layout.ty,
387 );
388 }
389 VarArgCompatible::CastIntTo { source_is_signed } => {
390 let size = caller_gave.layout.size;
392 let scalar = this.read_scalar(caller_gave)?;
393 if scalar.to_int(size)? < 0 {
394 throw_ub_format!(
395 "incorrect c-variadic argument type for `{fn_name}`: \
396 argument #{n} has value `{value}_{caller_ty}` which cannot be represented in expected type `{callee_expected}`",
397 n = varargs.already_gone.strict_add(n).strict_add(1),
398 caller_ty = caller_gave.layout.ty,
399 value = if source_is_signed {
400 scalar.to_int(size)?.to_string()
401 } else {
402 scalar.to_uint(size)?.to_string()
403 }
404 )
405 }
406 }
407 }
408 }
409
410 let transmuted = std::array::try_from_fn(|i| {
411 let layout = this.layout_of(tys[i])?;
412 now[i].transmute(layout, this)
413 })?;
414
415 interp_ok((
416 transmuted,
417 Varargs { args: tail, already_gone: varargs.already_gone.strict_add(N) },
418 ))
419 }
420
421 fn check_shim_sig_llvm_intrinsic<'a, const N: usize>(
426 &mut self,
427 link_name: Symbol,
428 args: &'a [OpTy<'tcx>],
429 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
430 assert!(link_name.as_str().starts_with("llvm."));
431
432 self.check_shim_symbol_clash(link_name)?;
433
434 if let Ok(ops) = args.try_into() {
435 return interp_ok(ops);
436 }
437 throw_ub_format!(
438 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
439 args.len(),
440 N
441 )
442 }
443}