1use rustc_abi::{CanonAbi, ExternAbi};
4use rustc_hir::Safety;
5use rustc_middle::ty::{Binder, FnSig, Ty};
6use rustc_span::Symbol;
7use rustc_target::callconv::FnAbi;
8
9use crate::*;
10
11pub struct ShimSig<'tcx, const ARGS: usize> {
13 pub abi: ExternAbi,
14 pub args: [Ty<'tcx>; ARGS],
15 pub ret: Ty<'tcx>,
16}
17
18#[macro_export]
30macro_rules! shim_sig {
31 (extern $abi:literal fn($($args:tt)*) -> $($ret:tt)*) => {
32 |this| $crate::shims::sig::ShimSig {
33 abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
34 args: shim_sig_args_sep!(this, [$($args)*]),
35 ret: shim_sig_arg!(this, $($ret)*),
36 }
37 };
38}
39
40#[macro_export]
52macro_rules! shim_sig_args_sep {
53 ($this:ident, [$($tt:tt)*]) => {
54 shim_sig_args_sep!(@ $this [] [] $($tt)*)
55 };
56
57 (@ $this:ident [$($final:tt)*] [$($collected:tt)*] , $($tt:tt)*) => {
65 shim_sig_args_sep!(@ $this [$($final)* shim_sig_arg!($this, $($collected)*), ] [] $($tt)*)
66 };
67 (@ $this:ident [$($final:tt)*] [$($collected:tt)*] $first:tt $($tt:tt)*) => {
69 shim_sig_args_sep!(@ $this [$($final)*] [$($collected)* $first] $($tt)*)
70 };
71 (@ $this:ident [$($final:tt)*] [$($collected:tt)+] ) => {
73 [$($final)* shim_sig_arg!($this, $($collected)*)]
74 };
75 (@ $this:ident [$($final:tt)*] [] ) => {
77 [$($final)*]
78 };
79}
80
81#[macro_export]
85macro_rules! shim_sig_arg {
86 ($this:ident, i8) => {
87 $this.tcx.types.i8
88 };
89 ($this:ident, i16) => {
90 $this.tcx.types.i16
91 };
92 ($this:ident, i32) => {
93 $this.tcx.types.i32
94 };
95 ($this:ident, i64) => {
96 $this.tcx.types.i64
97 };
98 ($this:ident, i128) => {
99 $this.tcx.types.i128
100 };
101 ($this:ident, isize) => {
102 $this.tcx.types.isize
103 };
104 ($this:ident, u8) => {
105 $this.tcx.types.u8
106 };
107 ($this:ident, u16) => {
108 $this.tcx.types.u16
109 };
110 ($this:ident, u32) => {
111 $this.tcx.types.u32
112 };
113 ($this:ident, u64) => {
114 $this.tcx.types.u64
115 };
116 ($this:ident, u128) => {
117 $this.tcx.types.u128
118 };
119 ($this:ident, usize) => {
120 $this.tcx.types.usize
121 };
122 ($this:ident, ()) => {
123 $this.tcx.types.unit
124 };
125 ($this:ident, bool) => {
126 $this.tcx.types.bool
127 };
128 ($this:ident, *const _) => {
129 $this.machine.layouts.const_raw_ptr.ty
130 };
131 ($this:ident, *mut _) => {
132 $this.machine.layouts.mut_raw_ptr.ty
133 };
134 ($this:ident, winapi::$ty:ident) => {
135 $this.windows_ty_layout(stringify!($ty)).ty
136 };
137 ($this:ident, $krate:ident :: $($path:ident)::+) => {
138 helpers::path_ty_layout($this, &[stringify!($krate), $(stringify!($path)),*]).ty
139 };
140 ($this:ident, $($other:tt)*) => {
141 compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
142 }
143}
144
145fn check_shim_abi<'tcx>(
147 this: &MiriInterpCx<'tcx>,
148 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
149 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
150) -> InterpResult<'tcx> {
151 if callee_abi.conv != caller_abi.conv {
152 throw_ub_format!(
153 r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#,
154 callee = callee_abi.conv,
155 caller = caller_abi.conv,
156 );
157 }
158 if callee_abi.can_unwind && !caller_abi.can_unwind {
159 throw_ub_format!(
160 "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
161 );
162 }
163 if caller_abi.c_variadic && !callee_abi.c_variadic {
164 throw_ub_format!(
165 "ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
166 );
167 }
168 if !caller_abi.c_variadic && callee_abi.c_variadic {
169 throw_ub_format!(
170 "ABI mismatch: calling a variadic function with a non-variadic caller-side signature"
171 );
172 }
173
174 if callee_abi.fixed_count != caller_abi.fixed_count {
175 throw_ub_format!(
176 "ABI mismatch: expected {} arguments, found {} arguments ",
177 callee_abi.fixed_count,
178 caller_abi.fixed_count
179 );
180 }
181
182 if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
183 throw_ub!(AbiMismatchReturn {
184 caller_ty: caller_abi.ret.layout.ty,
185 callee_ty: callee_abi.ret.layout.ty
186 });
187 }
188
189 for (idx, (caller_arg, callee_arg)) in
190 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
191 {
192 if !this.check_argument_compat(caller_arg, callee_arg)? {
193 throw_ub!(AbiMismatchArgument {
194 arg_idx: idx,
195 caller_ty: caller_abi.args[idx].layout.ty,
196 callee_ty: callee_abi.args[idx].layout.ty
197 });
198 }
199 }
200
201 interp_ok(())
202}
203
204fn check_shim_symbol_clash<'tcx>(
205 this: &mut MiriInterpCx<'tcx>,
206 link_name: Symbol,
207) -> InterpResult<'tcx, ()> {
208 if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? {
209 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
215 return interp_ok(());
216 }
217
218 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
219 link_name,
220 span: body.span.data(),
221 })
222 }
223 interp_ok(())
224}
225
226impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
227pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
228 fn check_shim_sig_lenient<'a, const N: usize>(
229 &mut self,
230 abi: &FnAbi<'tcx, Ty<'tcx>>,
231 exp_abi: CanonAbi,
232 link_name: Symbol,
233 args: &'a [OpTy<'tcx>],
234 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
235 let this = self.eval_context_mut();
236 check_shim_symbol_clash(this, link_name)?;
237
238 if abi.conv != exp_abi {
239 throw_ub_format!(
240 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
241 abi.conv
242 );
243 }
244 if abi.c_variadic {
245 throw_ub_format!(
246 "calling a non-variadic function with a variadic caller-side signature"
247 );
248 }
249
250 if let Ok(ops) = args.try_into() {
251 return interp_ok(ops);
252 }
253 throw_ub_format!(
254 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
255 args.len(),
256 N
257 )
258 }
259
260 fn check_shim_sig<'a, const N: usize>(
263 &mut self,
264 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
265 link_name: Symbol,
266 caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
267 caller_args: &'a [OpTy<'tcx>],
268 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
269 let this = self.eval_context_mut();
270 let shim_sig = shim_sig(this);
271
272 let mut inputs_and_output = Vec::with_capacity(N.strict_add(1));
274 inputs_and_output.extend(&shim_sig.args);
275 inputs_and_output.push(shim_sig.ret);
276 let fn_sig_binder = Binder::dummy(FnSig {
277 inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
278 c_variadic: false,
279 safety: Safety::Safe,
281 abi: shim_sig.abi,
282 });
283 let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
284
285 check_shim_abi(this, callee_fn_abi, caller_fn_abi)?;
287 check_shim_symbol_clash(this, link_name)?;
288
289 if let Ok(ops) = caller_args.try_into() {
291 return interp_ok(ops);
292 }
293 unreachable!()
294 }
295
296 fn check_shim_sig_variadic_lenient<'a, const N: usize>(
299 &mut self,
300 abi: &FnAbi<'tcx, Ty<'tcx>>,
301 exp_abi: CanonAbi,
302 link_name: Symbol,
303 args: &'a [OpTy<'tcx>],
304 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
305 where
306 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
307 {
308 let this = self.eval_context_mut();
309 check_shim_symbol_clash(this, link_name)?;
310
311 if abi.conv != exp_abi {
312 throw_ub_format!(
313 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
314 abi.conv
315 );
316 }
317 if !abi.c_variadic {
318 throw_ub_format!(
319 "calling a variadic function with a non-variadic caller-side signature"
320 );
321 }
322 if abi.fixed_count != u32::try_from(N).unwrap() {
323 throw_ub_format!(
324 "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
325 link_name.as_str(),
326 abi.fixed_count
327 )
328 }
329 if let Some(args) = args.split_first_chunk() {
330 return interp_ok(args);
331 }
332 panic!("mismatch between signature and `args` slice");
333 }
334}
335
336pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
339 name: &'a str,
340 args: &'a [OpTy<'tcx>],
341) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
342 if let Some((ops, _)) = args.split_first_chunk() {
343 return interp_ok(ops);
344 }
345 throw_ub_format!(
346 "not enough variadic arguments for `{name}`: got {}, expected at least {}",
347 args.len(),
348 N
349 )
350}