1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
use core::{
fmt::{self, Debug, Display},
str::FromStr,
};
/// The abi or calling convention of a function pointer.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
// from https://github.com/rust-lang/rust/blob/4fa80a5e733e2202d7ca4c203c2fdfda41cfe7dc/compiler/rustc_abi/src/extern_abi.rs#L21
pub enum AbiValue {
/* universal */
/// This is the same as `extern fn foo()`; whatever the default your C compiler supports.
C {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// Usually the same as [`extern "C"`](AbiValue::C), except on Win32, in which case it's
/// [`"stdcall"`](AbiValue::Stdcall), or what you should use to link to the Windows API itself.
System {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The default abi when you write a normal `fn foo()` in any Rust code.
Rust,
/* arm */
/// The default for ARM.
Aapcs {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/* x86 */
/// The default for `x86_32` C code.
Cdecl {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The default for the Win32 API on `x86_32`.
Stdcall {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The `fastcall` abi.
Fastcall {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The Windows C++ abi.
Thiscall {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The `vectorcall` abi.
Vectorcall {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/* x86_64 */
/// The default for C code on non-Windows `x86_64`.
SysV64 {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/// The default for C code on `x86_64` Windows.
Win64 {
/// Whether unwinding across this abi boundary is allowed (`*-unwind`).
unwind: bool,
},
/* Other */
/// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias.
EfiApi,
}
impl AbiValue {
/// Returns whether unwinding after a panic is allowed inside the called function.
#[must_use]
pub const fn allows_unwind(&self) -> bool {
match *self {
AbiValue::Rust => true,
AbiValue::EfiApi => false,
AbiValue::C { unwind }
| AbiValue::System { unwind }
| AbiValue::Aapcs { unwind }
| AbiValue::Cdecl { unwind }
| AbiValue::Stdcall { unwind }
| AbiValue::Fastcall { unwind }
| AbiValue::Thiscall { unwind }
| AbiValue::Vectorcall { unwind }
| AbiValue::SysV64 { unwind }
| AbiValue::Win64 { unwind } => unwind,
}
}
/// Canonicalize this abi for the current target.
///
/// Maps aliases (e.g. `system`, `cdecl`) to the concrete abi actually used on
/// the current OS/architecture, following Rust compiler rules.
///
/// Returns [`None`] if this abi is not supported on the current target.
#[must_use]
pub fn canonize(self, has_c_varargs: bool) -> Option<AbiValue> {
// from https://github.com/rust-lang/rust/blob/4fa80a5e733e2202d7ca4c203c2fdfda41cfe7dc/compiler/rustc_target/src/spec/abi_map.rs#L79
let os_windows = cfg!(target_os = "windows");
let os_vexos = cfg!(target_os = "vexos");
let arch_x86 = cfg!(target_arch = "x86");
let arch_x86_64 = cfg!(target_arch = "x86_64");
let arch_arm = cfg!(target_arch = "arm");
let arch_aarch64 = cfg!(target_arch = "aarch64");
let arch_arm_any = arch_arm || arch_aarch64;
let arch_riscv32 = cfg!(target_arch = "riscv32");
let arch_riscv64 = cfg!(target_arch = "riscv64");
#[allow(clippy::match_same_arms)]
let out = match self {
AbiValue::C { unwind } => AbiValue::C { unwind },
AbiValue::Rust => AbiValue::Rust,
AbiValue::System { unwind } if arch_x86 && os_windows && !has_c_varargs => {
AbiValue::Stdcall { unwind }
}
AbiValue::System { unwind } if arch_arm && os_vexos => AbiValue::Aapcs { unwind },
AbiValue::System { unwind } => AbiValue::C { unwind },
// arm
AbiValue::Aapcs { unwind } if arch_arm_any => AbiValue::Aapcs { unwind },
AbiValue::Aapcs { .. } => return None,
// x86
AbiValue::Cdecl { unwind } if arch_x86 => AbiValue::C { unwind },
AbiValue::Cdecl { unwind } => AbiValue::C { unwind },
AbiValue::Fastcall { unwind } if arch_x86 => AbiValue::Fastcall { unwind },
AbiValue::Fastcall { unwind } if os_windows => AbiValue::C { unwind },
AbiValue::Fastcall { .. } => return None,
AbiValue::Stdcall { unwind } if arch_x86 => AbiValue::Stdcall { unwind },
AbiValue::Stdcall { unwind } if os_windows => AbiValue::C { unwind },
AbiValue::Stdcall { .. } => return None,
AbiValue::Thiscall { unwind } if arch_x86 => AbiValue::Thiscall { unwind },
AbiValue::Thiscall { .. } => return None,
AbiValue::Vectorcall { unwind } if arch_x86 || arch_x86_64 => {
AbiValue::Vectorcall { unwind }
}
AbiValue::Vectorcall { .. } => return None,
AbiValue::SysV64 { unwind } if arch_x86_64 => AbiValue::SysV64 { unwind },
AbiValue::Win64 { unwind } if arch_x86_64 => AbiValue::Win64 { unwind },
AbiValue::SysV64 { .. } | AbiValue::Win64 { .. } => return None,
AbiValue::EfiApi if arch_x86_64 => AbiValue::Win64 { unwind: false },
AbiValue::EfiApi if arch_arm => AbiValue::Aapcs { unwind: false },
AbiValue::EfiApi if arch_x86 || arch_aarch64 || arch_riscv32 || arch_riscv64 => {
AbiValue::C { unwind: false }
}
AbiValue::EfiApi => return None,
};
Some(out)
}
}
impl Display for AbiValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_str())
}
}
macro_rules! abi_kind_impl {
(
$t:ty => {
$(
$variant:ident $( { unwind: $uw:literal } )? => $tok:literal
),* $(,)?
}
) => {
impl $t {
/// Returns the string representation of this abi.
#[must_use]
pub const fn to_str(&self) -> &'static str {
match self {
$( Self::$variant $( { unwind: $uw } )? => $tok, )*
}
}
/// The same as the [`FromStr`] implementation, but (only!) for use in `const` contexts.
#[must_use]
pub const fn from_str_const(conv: &'static str) -> Option<Self> {
$(
if konst::eq_str(conv, $tok) {
return Some(Self::$variant $( { unwind: $uw } )?);
}
)*
None
}
}
impl FromStr for $t {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$( $tok => Ok(Self::$variant $( { unwind: $uw } )?), )*
_ => Err(()),
}
}
}
};
}
abi_kind_impl!(AbiValue => {
Rust => "Rust",
C { unwind: false } => "C",
C { unwind: true } => "C-unwind",
System { unwind: false } => "system",
System { unwind: true } => "system-unwind",
Aapcs { unwind: false } => "aapcs",
Aapcs { unwind: true } => "aapcs-unwind",
Cdecl { unwind: false } => "cdecl",
Cdecl { unwind: true } => "cdecl-unwind",
Stdcall { unwind: false } => "stdcall",
Stdcall { unwind: true } => "stdcall-unwind",
Fastcall { unwind: false } => "fastcall",
Fastcall { unwind: true } => "fastcall-unwind",
Thiscall { unwind: false } => "thiscall",
Thiscall { unwind: true } => "thiscall-unwind",
Vectorcall { unwind: false } => "vectorcall",
Vectorcall { unwind: true } => "vectorcall-unwind",
SysV64 { unwind: false } => "sysv64",
SysV64 { unwind: true } => "sysv64-unwind",
Win64 { unwind: false } => "win64",
Win64 { unwind: true } => "win64-unwind",
EfiApi => "efiapi"
});