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