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
155 };
156 ($this:ident, fn(..) -> _) => {
157 $this.machine.layouts.const_raw_ptr.ty
159 };
160 ($this:ident, &[$($ty:tt)*]) => {
161 rustc_middle::ty::Ty::new_ref(
162 *$this.tcx,
163 $this.tcx.lifetimes.re_erased,
164 rustc_middle::ty::Ty::new_slice(*$this.tcx, shim_sig_arg!($this, $($ty)*)),
165 rustc_middle::mir::Mutability::Not,
166 )
167 };
168 ($this:ident, winapi::$ty:ident) => {
169 $this.windows_ty_layout(stringify!($ty)).ty
170 };
171 ($this:ident, $krate:ident :: $($path:ident)::+) => {
172 helpers::path_ty_layout($this, &[stringify!($krate), $(stringify!($path)),*]).ty
173 };
174 ($this:ident, $($other:tt)*) => {
175 compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
176 }
177}
178
179impl<'tcx, const ARGS: usize> ShimSig<'tcx, ARGS> {
180 fn as_abi(&self, ecx: &MiriInterpCx<'tcx>) -> &FnAbi<'tcx, Ty<'tcx>> {
181 let mut inputs_and_output = Vec::with_capacity(ARGS.strict_add(1));
182 inputs_and_output.extend(&self.args);
183 inputs_and_output.push(self.ret);
184 let fn_sig_binder = Binder::dummy(FnSig {
185 inputs_and_output: ecx.machine.tcx.mk_type_list(&inputs_and_output),
186 fn_sig_kind: FnSigKind::default().set_c_variadic(self.c_variadic).set_abi(self.abi),
187 });
188 ecx.fn_abi_of_fn_ptr(fn_sig_binder, Default::default()).unwrap()
189 }
190}
191
192fn check_shim_abi<'tcx>(
194 this: &MiriInterpCx<'tcx>,
195 link_name: Symbol,
196 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
197 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
198) -> InterpResult<'tcx> {
199 if callee_abi.conv != caller_abi.conv {
200 throw_ub_format!(
201 r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#,
202 callee = callee_abi.conv,
203 caller = caller_abi.conv,
204 );
205 }
206 if caller_abi.c_variadic && !callee_abi.c_variadic {
210 throw_ub_format!(
211 "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a variadic signature"
212 );
213 }
214 if !caller_abi.c_variadic && callee_abi.c_variadic {
215 throw_ub_format!(
216 "ABI mismatch: `{link_name}` is a variadic function, but the caller is using a non-variadic signature"
217 );
218 }
219
220 if callee_abi.fixed_count != caller_abi.fixed_count {
221 throw_ub_format!(
222 "ABI mismatch: calling `{link_name}` which takes {} {}argument{}, but {} argument{} given",
223 callee_abi.fixed_count,
224 if callee_abi.c_variadic { "fixed (non-variadic) " } else { "" },
225 if callee_abi.fixed_count == 1 { "" } else { "s" },
226 caller_abi.fixed_count,
227 if caller_abi.fixed_count == 1 { " was" } else { "s were" },
228 );
229 }
230
231 if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
232 throw_ub!(AbiMismatchReturn {
233 caller_ty: caller_abi.ret.layout.ty,
234 callee_ty: callee_abi.ret.layout.ty
235 });
236 }
237
238 for (idx, (caller_arg, callee_arg)) in
239 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
240 {
241 if !this.check_argument_compat(caller_arg, callee_arg)? {
242 throw_ub!(AbiMismatchArgument {
243 arg_idx: idx,
244 caller_ty: caller_abi.args[idx].layout.ty,
245 callee_ty: callee_abi.args[idx].layout.ty
246 });
247 }
248 }
249
250 interp_ok(())
251}
252
253pub struct Varargs<'tcx, 'a> {
256 args: &'a [OpTy<'tcx>],
257 already_gone: usize,
259}
260
261impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
262pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
263 fn check_shim_symbol_clash(&self, link_name: Symbol) -> InterpResult<'tcx, ()> {
265 let this = self.eval_context_ref();
266 if let Some(instance) = this.lookup_exported_symbol(link_name)? {
267 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
273 return interp_ok(());
274 }
275
276 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
277 link_name,
278 span: this.tcx.def_span(instance.def_id()).data(),
279 })
280 }
281 interp_ok(())
282 }
283
284 fn check_shim_sig_deprecated<'a, const N: usize>(
286 &mut self,
287 abi: &FnAbi<'tcx, Ty<'tcx>>,
288 exp_abi: CanonAbi,
289 link_name: Symbol,
290 args: &'a [OpTy<'tcx>],
291 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
292 self.check_shim_symbol_clash(link_name)?;
293
294 if abi.conv != exp_abi {
295 throw_ub_format!(
296 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
297 abi.conv
298 );
299 }
300 if abi.c_variadic {
301 throw_ub_format!(
302 "calling a non-variadic function with a variadic caller-side signature"
303 );
304 }
305
306 if let Ok(ops) = args.try_into() {
307 return interp_ok(ops);
308 }
309 throw_ub_format!(
310 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
311 args.len(),
312 N
313 )
314 }
315
316 fn check_shim_sig<'a, const N: usize>(
319 &self,
320 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
321 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
323 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
324 let this = self.eval_context_ref();
325
326 let shim_sig = shim_sig(this);
328 assert!(!shim_sig.c_variadic);
329 let callee_fn_abi = shim_sig.as_abi(this);
330
331 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
333 this.check_shim_symbol_clash(link_name)?;
334
335 if let Ok(ops) = caller_args.try_into() {
337 return interp_ok(ops);
338 }
339 unreachable!()
340 }
341
342 fn check_shim_sig_variadic<'a, const N: usize>(
345 &self,
346 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
347 (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
349 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
350 let this = self.eval_context_ref();
351
352 let shim_sig = shim_sig(this);
354 assert!(shim_sig.c_variadic);
355 let callee_fn_abi = shim_sig.as_abi(this);
356
357 check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
359 this.check_shim_symbol_clash(link_name)?;
360
361 if let Some((fixed, var)) = caller_args.split_first_chunk() {
363 return interp_ok((fixed, Varargs { args: var, already_gone: 0 }));
364 }
365 unreachable!()
366 }
367
368 fn check_varargs<'a, const N: usize>(
371 &self,
372 tys: fn(&MiriInterpCx<'tcx>) -> [Ty<'tcx>; N],
373 varargs: Varargs<'tcx, 'a>,
374 fn_name: &str,
375 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
376 let this = self.eval_context_ref();
377 let tys = tys(this);
378
379 let Some((now, tail)) = varargs.args.split_first_chunk::<N>() else {
380 throw_ub_format!(
381 "not enough variadic arguments for `{fn_name}`: got {}, expected at least {}",
382 varargs.already_gone.strict_add(varargs.args.len()),
383 varargs.already_gone.strict_add(N),
384 )
385 };
386
387 for (n, (caller_gave, callee_expected)) in now.iter().zip(tys).enumerate() {
388 let callee_expected = this.layout_of(callee_expected)?;
391
392 let _unused = (n, caller_gave, callee_expected);
395 }
396
397 interp_ok((now, Varargs { args: tail, already_gone: varargs.already_gone.strict_add(N) }))
398 }
399
400 fn check_shim_sig_llvm_intrinsic<'a, const N: usize>(
405 &mut self,
406 link_name: Symbol,
407 args: &'a [OpTy<'tcx>],
408 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
409 assert!(link_name.as_str().starts_with("llvm."));
410
411 self.check_shim_symbol_clash(link_name)?;
412
413 if let Ok(ops) = args.try_into() {
414 return interp_ok(ops);
415 }
416 throw_ub_format!(
417 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
418 args.len(),
419 N
420 )
421 }
422}