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.void_ptr_mut.ty
155 };
156 ($this:ident, *$($ty:tt)*) => {
157 rustc_middle::ty::Ty::new_ptr(
160 *$this.tcx,
161 shim_sig_arg!($this, $($ty)*),
162 rustc_middle::mir::Mutability::Mut,
163 )
164 };
165 ($this:ident, fn(..) -> _) => {
166 $this.machine.layouts.fn_ptr.ty
168 };
169 ($this:ident, &[$($ty:tt)*]) => {
170 rustc_middle::ty::Ty::new_ref(
171 *$this.tcx,
172 $this.tcx.lifetimes.re_erased,
173 rustc_middle::ty::Ty::new_slice(*$this.tcx, shim_sig_arg!($this, $($ty)*)),
174 rustc_middle::mir::Mutability::Not,
175 )
176 };
177 ($this:ident, winapi::$ty:ident) => {
178 $this.windows_ty_layout(stringify!($ty)).ty
179 };
180 ($this:ident, $krate:ident :: $($path:ident)::+) => {
181 helpers::path_ty_layout($this, &[stringify!($krate), $(stringify!($path)),*]).ty
182 };
183 ($this:ident, $($other:tt)*) => {
184 compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
185 }
186}
187
188impl<'tcx, const ARGS: usize> ShimSig<'tcx, ARGS> {
189 fn as_abi(&self, ecx: &MiriInterpCx<'tcx>) -> &FnAbi<'tcx, Ty<'tcx>> {
190 let mut inputs_and_output = Vec::with_capacity(ARGS.strict_add(1));
191 inputs_and_output.extend(&self.args);
192 inputs_and_output.push(self.ret);
193 let fn_sig_binder = Binder::dummy(FnSig {
194 inputs_and_output: ecx.machine.tcx.mk_type_list(&inputs_and_output),
195 fn_sig_kind: FnSigKind::default().set_c_variadic(self.c_variadic).set_abi(self.abi),
196 });
197 ecx.fn_abi_of_fn_ptr(fn_sig_binder, Default::default()).unwrap()
198 }
199}
200
201fn check_shim_abi<'tcx>(
203 this: &MiriInterpCx<'tcx>,
204 link_name: Symbol,
205 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
206 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
207) -> InterpResult<'tcx> {
208 if callee_abi.conv != caller_abi.conv {
209 throw_ub_format!(
210 r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#,
211 callee = callee_abi.conv,
212 caller = caller_abi.conv,
213 );
214 }
215 if caller_abi.c_variadic && !callee_abi.c_variadic {
219 throw_ub_format!(
220 "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a c-variadic signature"
221 );
222 }
223 if !caller_abi.c_variadic && callee_abi.c_variadic {
224 throw_ub_format!(
225 "ABI mismatch: `{link_name}` is a c-variadic function, but the caller is using a non-variadic signature"
226 );
227 }
228
229 if callee_abi.fixed_count != caller_abi.fixed_count {
230 throw_ub_format!(
231 "ABI mismatch: calling `{link_name}` which takes {} {}argument{}, but {} argument{} given",
232 callee_abi.fixed_count,
233 if callee_abi.c_variadic { "fixed (non-variadic) " } else { "" },
234 if callee_abi.fixed_count == 1 { "" } else { "s" },
235 caller_abi.fixed_count,
236 if caller_abi.fixed_count == 1 { " was" } else { "s were" },
237 );
238 }
239
240 if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
241 throw_ub!(AbiMismatchReturn {
242 caller_ty: caller_abi.ret.layout.ty,
243 callee_ty: callee_abi.ret.layout.ty
244 });
245 }
246
247 for (idx, (caller_arg, callee_arg)) in
248 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
249 {
250 if !this.check_argument_compat(caller_arg, callee_arg)? {
251 throw_ub!(AbiMismatchArgument {
252 arg_idx: idx,
253 caller_ty: caller_abi.args[idx].layout.ty,
254 callee_ty: callee_abi.args[idx].layout.ty
255 });
256 }
257 }
258
259 interp_ok(())
260}
261
262pub struct Varargs<'tcx, 'a> {
265 args: &'a [OpTy<'tcx>],
266 already_gone: usize,
268}
269
270impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
271pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
272 fn check_shim_symbol_clash(&self, link_name: Symbol) -> InterpResult<'tcx, ()> {
274 let this = self.eval_context_ref();
275 if let Some(instance) = this.lookup_exported_symbol(link_name)? {
276 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
282 return interp_ok(());
283 }
284
285 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
286 link_name,
287 span: this.tcx.def_span(instance.def_id()).data(),
288 })
289 }
290 interp_ok(())
291 }
292
293 fn check_shim_sig_deprecated<'a, const N: usize>(
295 &mut self,
296 abi: &FnAbi<'tcx, Ty<'tcx>>,
297 exp_abi: CanonAbi,
298 link_name: Symbol,
299 args: &'a [OpTy<'tcx>],
300 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
301 self.check_shim_symbol_clash(link_name)?;
302
303 if abi.conv != exp_abi {
304 throw_ub_format!(
305 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
306 abi.conv
307 );
308 }
309 if abi.c_variadic {
310 throw_ub_format!(
311 "calling a non-variadic function with a c-variadic caller-side signature"
312 );
313 }
314
315 if let Ok(ops) = args.try_into() {
316 return interp_ok(ops);
317 }
318 throw_ub_format!(
319 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
320 args.len(),
321 N
322 )
323 }
324
325 fn check_shim_sig<'a, const N: usize>(
328 &self,
329 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
330 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
332 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
333 let this = self.eval_context_ref();
334
335 let shim_sig = shim_sig(this);
337 assert!(!shim_sig.c_variadic);
338 let callee_fn_abi = shim_sig.as_abi(this);
339
340 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
342 this.check_shim_symbol_clash(link_name)?;
343
344 if let Ok(ops) = caller_args.try_into() {
346 return interp_ok(ops);
347 }
348 unreachable!()
349 }
350
351 fn check_shim_sig_variadic<'a, const N: usize>(
354 &self,
355 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
356 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
358 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
359 let this = self.eval_context_ref();
360
361 let shim_sig = shim_sig(this);
363 assert!(shim_sig.c_variadic);
364 let callee_fn_abi = shim_sig.as_abi(this);
365
366 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
368 this.check_shim_symbol_clash(link_name)?;
369
370 if let Some((fixed, var)) = caller_args.split_first_chunk() {
372 return interp_ok((fixed, Varargs { args: var, already_gone: N }));
373 }
374 unreachable!()
375 }
376
377 fn check_varargs<'a, const N: usize>(
380 &self,
381 tys: fn(&MiriInterpCx<'tcx>) -> [Ty<'tcx>; N],
382 varargs: Varargs<'tcx, 'a>,
383 fn_name: &str,
384 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
385 let this = self.eval_context_ref();
386 let tys = tys(this);
387
388 let Some((now, tail)) = varargs.args.split_first_chunk::<N>() else {
389 throw_ub_format!(
390 "not enough arguments for `{fn_name}`: got {}, expected at least {}",
391 varargs.already_gone.strict_add(varargs.args.len()),
392 varargs.already_gone.strict_add(N),
393 )
394 };
395
396 for (n, (caller_gave, callee_expected)) in now.iter().zip(tys).enumerate() {
397 let compatible =
399 this.validate_c_variadic_compatible_ty(caller_gave.layout.ty, callee_expected)?;
400 match compatible {
401 VarArgCompatible::Compatible => {}
402 VarArgCompatible::Incompatible => {
403 throw_ub_format!(
404 "incorrect c-variadic argument type for `{fn_name}`: \
405 expected argument #{n} to have type `{callee_expected}` but got incompatible type `{caller_ty}`",
406 n = varargs.already_gone.strict_add(n).strict_add(1),
407 caller_ty = caller_gave.layout.ty,
408 );
409 }
410 VarArgCompatible::CastIntTo { source_is_signed } => {
411 let size = caller_gave.layout.size;
413 let scalar = this.read_scalar(caller_gave)?;
414 if scalar.to_int(size)? < 0 {
415 throw_ub_format!(
416 "incorrect c-variadic argument type for `{fn_name}`: \
417 argument #{n} has value `{value}_{caller_ty}` which cannot be represented in expected type `{callee_expected}`",
418 n = varargs.already_gone.strict_add(n).strict_add(1),
419 caller_ty = caller_gave.layout.ty,
420 value = if source_is_signed {
421 scalar.to_int(size)?.to_string()
422 } else {
423 scalar.to_uint(size)?.to_string()
424 }
425 )
426 }
427 }
428 }
429 }
430
431 interp_ok((now, Varargs { args: tail, already_gone: varargs.already_gone.strict_add(N) }))
432 }
433
434 fn check_shim_sig_llvm_intrinsic<'a, const N: usize>(
439 &mut self,
440 link_name: Symbol,
441 args: &'a [OpTy<'tcx>],
442 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
443 assert!(link_name.as_str().starts_with("llvm."));
444
445 self.check_shim_symbol_clash(link_name)?;
446
447 if let Ok(ops) = args.try_into() {
448 return interp_ok(ops);
449 }
450 throw_ub_format!(
451 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
452 args.len(),
453 N
454 )
455 }
456}