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]
23macro_rules! shim_sig {
24 (extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => {
25 |this| $crate::shims::sig::ShimSig {
26 abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
27 args: [$(shim_sig_arg!(this, $arg)),*],
28 ret: shim_sig_arg!(this, $ret),
29 }
30 };
31}
32
33#[macro_export]
35macro_rules! shim_sig_arg {
36 ($this:ident, $x:ty) => {{
39 match stringify!($x) {
40 "i8" => $this.tcx.types.i8,
41 "i16" => $this.tcx.types.i16,
42 "i32" => $this.tcx.types.i32,
43 "i64" => $this.tcx.types.i64,
44 "i128" => $this.tcx.types.i128,
45 "isize" => $this.tcx.types.isize,
46 "u8" => $this.tcx.types.u8,
47 "u16" => $this.tcx.types.u16,
48 "u32" => $this.tcx.types.u32,
49 "u64" => $this.tcx.types.u64,
50 "u128" => $this.tcx.types.u128,
51 "usize" => $this.tcx.types.usize,
52 "()" => $this.tcx.types.unit,
53 "*const _" => $this.machine.layouts.const_raw_ptr.ty,
54 "*mut _" => $this.machine.layouts.mut_raw_ptr.ty,
55 ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty,
56 ty => panic!("unsupported signature type {ty:?}"),
57 }
58 }};
59}
60
61fn check_shim_abi<'tcx>(
63 this: &MiriInterpCx<'tcx>,
64 callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
65 caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
66) -> InterpResult<'tcx> {
67 if callee_abi.conv != caller_abi.conv {
68 throw_ub_format!(
69 r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#,
70 callee = callee_abi.conv,
71 caller = caller_abi.conv,
72 );
73 }
74 if callee_abi.can_unwind && !caller_abi.can_unwind {
75 throw_ub_format!(
76 "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
77 );
78 }
79 if caller_abi.c_variadic && !callee_abi.c_variadic {
80 throw_ub_format!(
81 "ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
82 );
83 }
84 if !caller_abi.c_variadic && callee_abi.c_variadic {
85 throw_ub_format!(
86 "ABI mismatch: calling a variadic function with a non-variadic caller-side signature"
87 );
88 }
89
90 if callee_abi.fixed_count != caller_abi.fixed_count {
91 throw_ub_format!(
92 "ABI mismatch: expected {} arguments, found {} arguments ",
93 callee_abi.fixed_count,
94 caller_abi.fixed_count
95 );
96 }
97
98 if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
99 throw_ub!(AbiMismatchReturn {
100 caller_ty: caller_abi.ret.layout.ty,
101 callee_ty: callee_abi.ret.layout.ty
102 });
103 }
104
105 for (idx, (caller_arg, callee_arg)) in
106 caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
107 {
108 if !this.check_argument_compat(caller_arg, callee_arg)? {
109 throw_ub!(AbiMismatchArgument {
110 arg_idx: idx,
111 caller_ty: caller_abi.args[idx].layout.ty,
112 callee_ty: callee_abi.args[idx].layout.ty
113 });
114 }
115 }
116
117 interp_ok(())
118}
119
120fn check_shim_symbol_clash<'tcx>(
121 this: &mut MiriInterpCx<'tcx>,
122 link_name: Symbol,
123) -> InterpResult<'tcx, ()> {
124 if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? {
125 if this.tcx.is_compiler_builtins(instance.def_id().krate) {
131 return interp_ok(());
132 }
133
134 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
135 link_name,
136 span: body.span.data(),
137 })
138 }
139 interp_ok(())
140}
141
142impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
143pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
144 fn check_shim_sig_lenient<'a, const N: usize>(
145 &mut self,
146 abi: &FnAbi<'tcx, Ty<'tcx>>,
147 exp_abi: CanonAbi,
148 link_name: Symbol,
149 args: &'a [OpTy<'tcx>],
150 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
151 let this = self.eval_context_mut();
152 check_shim_symbol_clash(this, link_name)?;
153
154 if abi.conv != exp_abi {
155 throw_ub_format!(
156 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
157 abi.conv
158 );
159 }
160 if abi.c_variadic {
161 throw_ub_format!(
162 "calling a non-variadic function with a variadic caller-side signature"
163 );
164 }
165
166 if let Ok(ops) = args.try_into() {
167 return interp_ok(ops);
168 }
169 throw_ub_format!(
170 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
171 args.len(),
172 N
173 )
174 }
175
176 fn check_shim_sig<'a, const N: usize>(
179 &mut self,
180 shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
181 link_name: Symbol,
182 caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
183 caller_args: &'a [OpTy<'tcx>],
184 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
185 let this = self.eval_context_mut();
186 let shim_sig = shim_sig(this);
187
188 let mut inputs_and_output = Vec::with_capacity(N.strict_add(1));
190 inputs_and_output.extend(&shim_sig.args);
191 inputs_and_output.push(shim_sig.ret);
192 let fn_sig_binder = Binder::dummy(FnSig {
193 inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
194 c_variadic: false,
195 safety: Safety::Safe,
197 abi: shim_sig.abi,
198 });
199 let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
200
201 check_shim_abi(this, callee_fn_abi, caller_fn_abi)?;
203 check_shim_symbol_clash(this, link_name)?;
204
205 if let Ok(ops) = caller_args.try_into() {
207 return interp_ok(ops);
208 }
209 unreachable!()
210 }
211
212 fn check_shim_sig_variadic_lenient<'a, const N: usize>(
215 &mut self,
216 abi: &FnAbi<'tcx, Ty<'tcx>>,
217 exp_abi: CanonAbi,
218 link_name: Symbol,
219 args: &'a [OpTy<'tcx>],
220 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
221 where
222 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
223 {
224 let this = self.eval_context_mut();
225 check_shim_symbol_clash(this, link_name)?;
226
227 if abi.conv != exp_abi {
228 throw_ub_format!(
229 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
230 abi.conv
231 );
232 }
233 if !abi.c_variadic {
234 throw_ub_format!(
235 "calling a variadic function with a non-variadic caller-side signature"
236 );
237 }
238 if abi.fixed_count != u32::try_from(N).unwrap() {
239 throw_ub_format!(
240 "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
241 link_name.as_str(),
242 abi.fixed_count
243 )
244 }
245 if let Some(args) = args.split_first_chunk() {
246 return interp_ok(args);
247 }
248 panic!("mismatch between signature and `args` slice");
249 }
250}
251
252pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
255 name: &'a str,
256 args: &'a [OpTy<'tcx>],
257) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
258 if let Some((ops, _)) = args.split_first_chunk() {
259 return interp_ok(ops);
260 }
261 throw_ub_format!(
262 "not enough variadic arguments for `{name}`: got {}, expected at least {}",
263 args.len(),
264 N
265 )
266}