1use rustc_abi::{CanonAbi, 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 |this| {
33 let (args, c_variadic) = shim_sig_args_sep!(this, [$($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!(this, $($ret)*),
38 c_variadic,
39 }
40 }
41 };
42}
43
44#[macro_export]
46macro_rules! shim_varargs {
47 ($($args:tt)*) => {
48 |this| {
49 let (args, c_variadic) = shim_sig_args_sep!(this, [$($args)*]);
50 assert!(!c_variadic); args
52 }
53 };
54}
55
56#[macro_export]
69macro_rules! shim_sig_args_sep {
70 ($this:ident, [$($tt:tt)*]) => {
71 shim_sig_args_sep!(@ $this [] [] $($tt)*)
72 };
73
74 (@ $this:ident [$($final:tt)*] [$($collected:tt)*] , $($tt:tt)*) => {
82 shim_sig_args_sep!(@ $this [$($final)* shim_sig_arg!($this, $($collected)*), ] [] $($tt)*)
83 };
84 (@ $this:ident [$($final:tt)*] [$($collected:tt)*] $first:tt $($tt:tt)*) => {
86 shim_sig_args_sep!(@ $this [$($final)*] [$($collected)* $first] $($tt)*)
87 };
88 (@ $this:ident [$($final:tt)*] [...] ) => {
90 ([$($final)*], true)
91 };
92 (@ $this:ident [$($final:tt)*] [$($collected:tt)+] ) => {
94 ([$($final)* shim_sig_arg!($this, $($collected)*)], false)
95 };
96 (@ $this:ident [$($final:tt)*] [] ) => {
98 ([$($final)*], false)
99 };
100}
101
102#[macro_export]
106macro_rules! shim_sig_arg {
107 ($this:ident, i8) => {
108 $this.tcx.types.i8
109 };
110 ($this:ident, i16) => {
111 $this.tcx.types.i16
112 };
113 ($this:ident, i32) => {
114 $this.tcx.types.i32
115 };
116 ($this:ident, i64) => {
117 $this.tcx.types.i64
118 };
119 ($this:ident, i128) => {
120 $this.tcx.types.i128
121 };
122 ($this:ident, isize) => {
123 $this.tcx.types.isize
124 };
125 ($this:ident, u8) => {
126 $this.tcx.types.u8
127 };
128 ($this:ident, u16) => {
129 $this.tcx.types.u16
130 };
131 ($this:ident, u32) => {
132 $this.tcx.types.u32
133 };
134 ($this:ident, u64) => {
135 $this.tcx.types.u64
136 };
137 ($this:ident, u128) => {
138 $this.tcx.types.u128
139 };
140 ($this:ident, usize) => {
141 $this.tcx.types.usize
142 };
143 ($this:ident, ()) => {
144 $this.tcx.types.unit
145 };
146 ($this:ident, !) => {
147 $this.tcx.types.never
148 };
149 ($this:ident, bool) => {
150 $this.tcx.types.bool
151 };
152 ($this:ident, *_) => {
153 $this.machine.layouts.mut_raw_ptr.ty
156 };
157 ($this:ident, *$($ty:tt)*) => {
158 rustc_middle::ty::Ty::new_ptr(
161 *$this.tcx,
162 shim_sig_arg!($this, $($ty)*),
163 rustc_middle::mir::Mutability::Mut,
164 )
165 };
166 ($this:ident, fn(..) -> _) => {
167 $this.machine.layouts.const_raw_ptr.ty
169 };
170 ($this:ident, &[$($ty:tt)*]) => {
171 rustc_middle::ty::Ty::new_ref(
172 *$this.tcx,
173 $this.tcx.lifetimes.re_erased,
174 rustc_middle::ty::Ty::new_slice(*$this.tcx, shim_sig_arg!($this, $($ty)*)),
175 rustc_middle::mir::Mutability::Not,
176 )
177 };
178 ($this:ident, winapi::$ty:ident) => {
179 $this.windows_ty_layout(stringify!($ty)).ty
180 };
181 ($this:ident, $krate:ident :: $($path:ident)::+) => {
182 helpers::path_ty_layout($this, &[stringify!($krate), $(stringify!($path)),*]).ty
183 };
184 ($this:ident, $($other:tt)*) => {
185 compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
186 }
187}
188
189impl<'tcx, const ARGS: usize> ShimSig<'tcx, ARGS> {
190 fn as_abi(&self, ecx: &MiriInterpCx<'tcx>) -> &FnAbi<'tcx, Ty<'tcx>> {
191 let mut inputs_and_output = Vec::with_capacity(ARGS.strict_add(1));
192 inputs_and_output.extend(&self.args);
193 inputs_and_output.push(self.ret);
194 let fn_sig_binder = Binder::dummy(FnSig {
195 inputs_and_output: ecx.machine.tcx.mk_type_list(&inputs_and_output),
196 fn_sig_kind: FnSigKind::default().set_c_variadic(self.c_variadic).set_abi(self.abi),
197 });
198 ecx.fn_abi_of_fn_ptr(fn_sig_binder, Default::default()).unwrap()
199 }
200}
201
202fn check_shim_abi<'tcx>(
204 this: &MiriInterpCx<'tcx>,
205 link_name: Symbol,
206 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
207 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
208) -> InterpResult<'tcx> {
209 if callee_abi.conv != caller_abi.conv {
210 throw_ub_format!(
211 r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#,
212 callee = callee_abi.conv,
213 caller = caller_abi.conv,
214 );
215 }
216 if caller_abi.c_variadic && !callee_abi.c_variadic {
220 throw_ub_format!(
221 "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a c-variadic signature"
222 );
223 }
224 if !caller_abi.c_variadic && callee_abi.c_variadic {
225 throw_ub_format!(
226 "ABI mismatch: `{link_name}` is a c-variadic function, but the caller is using a non-variadic signature"
227 );
228 }
229
230 if callee_abi.fixed_count != caller_abi.fixed_count {
231 throw_ub_format!(
232 "ABI mismatch: calling `{link_name}` which takes {} {}argument{}, but {} argument{} given",
233 callee_abi.fixed_count,
234 if callee_abi.c_variadic { "fixed (non-variadic) " } else { "" },
235 if callee_abi.fixed_count == 1 { "" } else { "s" },
236 caller_abi.fixed_count,
237 if caller_abi.fixed_count == 1 { " was" } else { "s were" },
238 );
239 }
240
241 if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
242 throw_ub!(AbiMismatchReturn {
243 caller_ty: caller_abi.ret.layout.ty,
244 callee_ty: callee_abi.ret.layout.ty
245 });
246 }
247
248 for (idx, (caller_arg, callee_arg)) in
249 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
250 {
251 if !this.check_argument_compat(caller_arg, callee_arg)? {
252 throw_ub!(AbiMismatchArgument {
253 arg_idx: idx,
254 caller_ty: caller_abi.args[idx].layout.ty,
255 callee_ty: callee_abi.args[idx].layout.ty
256 });
257 }
258 }
259
260 interp_ok(())
261}
262
263pub struct Varargs<'tcx, 'a> {
266 args: &'a [OpTy<'tcx>],
267 already_gone: usize,
269}
270
271impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
272pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
273 fn check_shim_symbol_clash(&self, link_name: Symbol) -> InterpResult<'tcx, ()> {
275 let this = self.eval_context_ref();
276 if let Some(instance) = this.lookup_exported_symbol(link_name)? {
277 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
283 return interp_ok(());
284 }
285
286 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
287 link_name,
288 span: this.tcx.def_span(instance.def_id()).data(),
289 })
290 }
291 interp_ok(())
292 }
293
294 fn check_shim_sig_deprecated<'a, const N: usize>(
296 &mut self,
297 abi: &FnAbi<'tcx, Ty<'tcx>>,
298 exp_abi: CanonAbi,
299 link_name: Symbol,
300 args: &'a [OpTy<'tcx>],
301 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
302 self.check_shim_symbol_clash(link_name)?;
303
304 if abi.conv != exp_abi {
305 throw_ub_format!(
306 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
307 abi.conv
308 );
309 }
310 if abi.c_variadic {
311 throw_ub_format!(
312 "calling a non-variadic function with a c-variadic caller-side signature"
313 );
314 }
315
316 if let Ok(ops) = args.try_into() {
317 return interp_ok(ops);
318 }
319 throw_ub_format!(
320 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
321 args.len(),
322 N
323 )
324 }
325
326 fn check_shim_sig<'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, &'a [OpTy<'tcx>; N]> {
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 Ok(ops) = caller_args.try_into() {
347 return interp_ok(ops);
348 }
349 unreachable!()
350 }
351
352 fn check_shim_sig_variadic<'a, const N: usize>(
355 &self,
356 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
357 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
359 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
360 let this = self.eval_context_ref();
361
362 let shim_sig = shim_sig(this);
364 assert!(shim_sig.c_variadic);
365 let callee_fn_abi = shim_sig.as_abi(this);
366
367 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
369 this.check_shim_symbol_clash(link_name)?;
370
371 if let Some((fixed, var)) = caller_args.split_first_chunk() {
373 return interp_ok((fixed, Varargs { args: var, already_gone: N }));
374 }
375 unreachable!()
376 }
377
378 fn check_varargs<'a, const N: usize>(
381 &self,
382 tys: fn(&MiriInterpCx<'tcx>) -> [Ty<'tcx>; N],
383 varargs: Varargs<'tcx, 'a>,
384 fn_name: &str,
385 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
386 let this = self.eval_context_ref();
387 let tys = tys(this);
388
389 let Some((now, tail)) = varargs.args.split_first_chunk::<N>() else {
390 throw_ub_format!(
391 "not enough arguments for `{fn_name}`: got {}, expected at least {}",
392 varargs.already_gone.strict_add(varargs.args.len()),
393 varargs.already_gone.strict_add(N),
394 )
395 };
396
397 for (n, (caller_gave, callee_expected)) in now.iter().zip(tys).enumerate() {
398 let compatible =
400 this.validate_c_variadic_compatible_ty(caller_gave.layout.ty, callee_expected)?;
401 match compatible {
402 VarArgCompatible::Compatible => {}
403 VarArgCompatible::Incompatible => {
404 throw_ub_format!(
405 "incorrect c-variadic argument type for `{fn_name}`: \
406 expected argument #{n} to have type `{callee_expected}` but got incompatible type `{caller_ty}`",
407 n = varargs.already_gone.strict_add(n).strict_add(1),
408 caller_ty = caller_gave.layout.ty,
409 );
410 }
411 VarArgCompatible::CastIntTo { source_is_signed } => {
412 let size = caller_gave.layout.size;
414 let scalar = this.read_scalar(caller_gave)?;
415 if scalar.to_int(size)? < 0 {
416 throw_ub_format!(
417 "incorrect c-variadic argument type for `{fn_name}`: \
418 argument #{n} has value `{value}_{caller_ty}` which cannot be represented in expected type `{callee_expected}`",
419 n = varargs.already_gone.strict_add(n).strict_add(1),
420 caller_ty = caller_gave.layout.ty,
421 value = if source_is_signed {
422 scalar.to_int(size)?.to_string()
423 } else {
424 scalar.to_uint(size)?.to_string()
425 }
426 )
427 }
428 }
429 }
430 }
431
432 interp_ok((now, Varargs { args: tail, already_gone: varargs.already_gone.strict_add(N) }))
433 }
434
435 fn check_shim_sig_llvm_intrinsic<'a, const N: usize>(
440 &mut self,
441 link_name: Symbol,
442 args: &'a [OpTy<'tcx>],
443 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
444 assert!(link_name.as_str().starts_with("llvm."));
445
446 self.check_shim_symbol_clash(link_name)?;
447
448 if let Ok(ops) = args.try_into() {
449 return interp_ok(ops);
450 }
451 throw_ub_format!(
452 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
453 args.len(),
454 N
455 )
456 }
457}