/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/isa/unwind/winx64.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! Windows x64 ABI unwind information. |
2 | | |
3 | | use crate::result::{CodegenError, CodegenResult}; |
4 | | use alloc::vec::Vec; |
5 | | use log::warn; |
6 | | #[cfg(feature = "enable-serde")] |
7 | | use serde::{Deserialize, Serialize}; |
8 | | |
9 | | use crate::binemit::CodeOffset; |
10 | | use crate::isa::unwind::UnwindInst; |
11 | | |
12 | | /// Maximum (inclusive) size of a "small" stack allocation |
13 | | const SMALL_ALLOC_MAX_SIZE: u32 = 128; |
14 | | /// Maximum (inclusive) size of a "large" stack allocation that can represented in 16-bits |
15 | | const LARGE_ALLOC_16BIT_MAX_SIZE: u32 = 524280; |
16 | | |
17 | | struct Writer<'a> { |
18 | | buf: &'a mut [u8], |
19 | | offset: usize, |
20 | | } |
21 | | |
22 | | impl<'a> Writer<'a> { |
23 | 0 | pub fn new(buf: &'a mut [u8]) -> Self { |
24 | 0 | Self { buf, offset: 0 } |
25 | 0 | } |
26 | | |
27 | 0 | fn write_u8(&mut self, v: u8) { |
28 | 0 | self.buf[self.offset] = v; |
29 | 0 | self.offset += 1; |
30 | 0 | } |
31 | | |
32 | 0 | fn write_u16_le(&mut self, v: u16) { |
33 | 0 | self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_le_bytes()); |
34 | 0 | self.offset += 2; |
35 | 0 | } |
36 | | |
37 | 0 | fn write_u32_le(&mut self, v: u32) { |
38 | 0 | self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_le_bytes()); |
39 | 0 | self.offset += 4; |
40 | 0 | } |
41 | | } |
42 | | |
43 | | /// The supported unwind codes for the x64 Windows ABI. |
44 | | /// |
45 | | /// See: <https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64> |
46 | | /// Only what is needed to describe the prologues generated by the Cranelift x86 ISA are represented here. |
47 | | /// Note: the Cranelift x86 ISA RU enum matches the Windows unwind GPR encoding values. |
48 | | #[allow(dead_code)] |
49 | | #[derive(Clone, Debug, PartialEq, Eq)] |
50 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
51 | | pub(crate) enum UnwindCode { |
52 | | PushRegister { |
53 | | instruction_offset: u8, |
54 | | reg: u8, |
55 | | }, |
56 | | SaveReg { |
57 | | instruction_offset: u8, |
58 | | reg: u8, |
59 | | stack_offset: u32, |
60 | | }, |
61 | | SaveXmm { |
62 | | instruction_offset: u8, |
63 | | reg: u8, |
64 | | stack_offset: u32, |
65 | | }, |
66 | | StackAlloc { |
67 | | instruction_offset: u8, |
68 | | size: u32, |
69 | | }, |
70 | | SetFPReg { |
71 | | instruction_offset: u8, |
72 | | }, |
73 | | } |
74 | | |
75 | | impl UnwindCode { |
76 | 0 | fn emit(&self, writer: &mut Writer) { |
77 | 0 | enum UnwindOperation { |
78 | 0 | PushNonvolatileRegister = 0, |
79 | 0 | LargeStackAlloc = 1, |
80 | 0 | SmallStackAlloc = 2, |
81 | 0 | SetFPReg = 3, |
82 | 0 | SaveNonVolatileRegister = 4, |
83 | 0 | SaveNonVolatileRegisterFar = 5, |
84 | 0 | SaveXmm128 = 8, |
85 | 0 | SaveXmm128Far = 9, |
86 | 0 | } |
87 | 0 |
|
88 | 0 | match self { |
89 | | Self::PushRegister { |
90 | 0 | instruction_offset, |
91 | 0 | reg, |
92 | 0 | } => { |
93 | 0 | writer.write_u8(*instruction_offset); |
94 | 0 | writer.write_u8((*reg << 4) | (UnwindOperation::PushNonvolatileRegister as u8)); |
95 | 0 | } |
96 | | Self::SaveReg { |
97 | 0 | instruction_offset, |
98 | 0 | reg, |
99 | 0 | stack_offset, |
100 | | } |
101 | | | Self::SaveXmm { |
102 | 0 | instruction_offset, |
103 | 0 | reg, |
104 | 0 | stack_offset, |
105 | | } => { |
106 | 0 | let is_xmm = match self { |
107 | 0 | Self::SaveXmm { .. } => true, |
108 | 0 | _ => false, |
109 | | }; |
110 | 0 | let (op_small, op_large) = if is_xmm { |
111 | 0 | (UnwindOperation::SaveXmm128, UnwindOperation::SaveXmm128Far) |
112 | | } else { |
113 | 0 | ( |
114 | 0 | UnwindOperation::SaveNonVolatileRegister, |
115 | 0 | UnwindOperation::SaveNonVolatileRegisterFar, |
116 | 0 | ) |
117 | | }; |
118 | 0 | writer.write_u8(*instruction_offset); |
119 | 0 | let scaled_stack_offset = stack_offset / 16; |
120 | 0 | if scaled_stack_offset <= core::u16::MAX as u32 { |
121 | 0 | writer.write_u8((*reg << 4) | (op_small as u8)); |
122 | 0 | writer.write_u16_le(scaled_stack_offset as u16); |
123 | 0 | } else { |
124 | 0 | writer.write_u8((*reg << 4) | (op_large as u8)); |
125 | 0 | writer.write_u16_le(*stack_offset as u16); |
126 | 0 | writer.write_u16_le((stack_offset >> 16) as u16); |
127 | 0 | } |
128 | | } |
129 | | Self::StackAlloc { |
130 | 0 | instruction_offset, |
131 | 0 | size, |
132 | 0 | } => { |
133 | 0 | // Stack allocations on Windows must be a multiple of 8 and be at least 1 slot |
134 | 0 | assert!(*size >= 8); |
135 | 0 | assert!((*size % 8) == 0); |
136 | | |
137 | 0 | writer.write_u8(*instruction_offset); |
138 | 0 | if *size <= SMALL_ALLOC_MAX_SIZE { |
139 | 0 | writer.write_u8( |
140 | 0 | ((((*size - 8) / 8) as u8) << 4) | UnwindOperation::SmallStackAlloc as u8, |
141 | 0 | ); |
142 | 0 | } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE { |
143 | 0 | writer.write_u8(UnwindOperation::LargeStackAlloc as u8); |
144 | 0 | writer.write_u16_le((*size / 8) as u16); |
145 | 0 | } else { |
146 | 0 | writer.write_u8((1 << 4) | (UnwindOperation::LargeStackAlloc as u8)); |
147 | 0 | writer.write_u32_le(*size); |
148 | 0 | } |
149 | | } |
150 | 0 | Self::SetFPReg { instruction_offset } => { |
151 | 0 | writer.write_u8(*instruction_offset); |
152 | 0 | writer.write_u8(UnwindOperation::SetFPReg as u8); |
153 | 0 | } |
154 | | } |
155 | 0 | } |
156 | | |
157 | 0 | fn node_count(&self) -> usize { |
158 | 0 | match self { |
159 | 0 | Self::StackAlloc { size, .. } => { |
160 | 0 | if *size <= SMALL_ALLOC_MAX_SIZE { |
161 | 0 | 1 |
162 | 0 | } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE { |
163 | 0 | 2 |
164 | | } else { |
165 | 0 | 3 |
166 | | } |
167 | | } |
168 | 0 | Self::SaveXmm { stack_offset, .. } | Self::SaveReg { stack_offset, .. } => { |
169 | 0 | if *stack_offset <= core::u16::MAX as u32 { |
170 | 0 | 2 |
171 | | } else { |
172 | 0 | 3 |
173 | | } |
174 | | } |
175 | 0 | _ => 1, |
176 | | } |
177 | 0 | } |
178 | | } |
179 | | |
180 | | pub(crate) enum MappedRegister { |
181 | | Int(u8), |
182 | | Xmm(u8), |
183 | | } |
184 | | |
185 | | /// Maps UnwindInfo register to Windows x64 unwind data. |
186 | | pub(crate) trait RegisterMapper<Reg> { |
187 | | /// Maps a Reg to a Windows unwind register number. |
188 | | fn map(reg: Reg) -> MappedRegister; |
189 | | } |
190 | | |
191 | | /// Represents Windows x64 unwind information. |
192 | | /// |
193 | | /// For information about Windows x64 unwind info, see: |
194 | | /// <https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64> |
195 | | #[derive(Clone, Debug, PartialEq, Eq)] |
196 | | #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] |
197 | | pub struct UnwindInfo { |
198 | | pub(crate) flags: u8, |
199 | | pub(crate) prologue_size: u8, |
200 | | pub(crate) frame_register: Option<u8>, |
201 | | pub(crate) frame_register_offset: u8, |
202 | | pub(crate) unwind_codes: Vec<UnwindCode>, |
203 | | } |
204 | | |
205 | | impl UnwindInfo { |
206 | | /// Gets the emit size of the unwind information, in bytes. |
207 | 0 | pub fn emit_size(&self) -> usize { |
208 | 0 | let node_count = self.node_count(); |
209 | 0 |
|
210 | 0 | // Calculation of the size requires no SEH handler or chained info |
211 | 0 | assert!(self.flags == 0); |
212 | | |
213 | | // Size of fixed part of UNWIND_INFO is 4 bytes |
214 | | // Then comes the UNWIND_CODE nodes (2 bytes each) |
215 | | // Then comes 2 bytes of padding for the unwind codes if necessary |
216 | | // Next would come the SEH data, but we assert above that the function doesn't have SEH data |
217 | | |
218 | 0 | 4 + (node_count * 2) + if (node_count & 1) == 1 { 2 } else { 0 } |
219 | 0 | } |
220 | | |
221 | | /// Emits the unwind information into the given mutable byte slice. |
222 | | /// |
223 | | /// This function will panic if the slice is not at least `emit_size` in length. |
224 | 0 | pub fn emit(&self, buf: &mut [u8]) { |
225 | 0 | const UNWIND_INFO_VERSION: u8 = 1; |
226 | 0 |
|
227 | 0 | let node_count = self.node_count(); |
228 | 0 | assert!(node_count <= 256); |
229 | | |
230 | 0 | let mut writer = Writer::new(buf); |
231 | 0 |
|
232 | 0 | writer.write_u8((self.flags << 3) | UNWIND_INFO_VERSION); |
233 | 0 | writer.write_u8(self.prologue_size); |
234 | 0 | writer.write_u8(node_count as u8); |
235 | | |
236 | 0 | if let Some(reg) = self.frame_register { |
237 | 0 | writer.write_u8((self.frame_register_offset << 4) | reg); |
238 | 0 | } else { |
239 | 0 | writer.write_u8(0); |
240 | 0 | } |
241 | | |
242 | | // Unwind codes are written in reverse order (prologue offset descending) |
243 | 0 | for code in self.unwind_codes.iter().rev() { |
244 | 0 | code.emit(&mut writer); |
245 | 0 | } |
246 | | |
247 | | // To keep a 32-bit alignment, emit 2 bytes of padding if there's an odd number of 16-bit nodes |
248 | 0 | if (node_count & 1) == 1 { |
249 | 0 | writer.write_u16_le(0); |
250 | 0 | } |
251 | | |
252 | | // Ensure the correct number of bytes was emitted |
253 | 0 | assert_eq!(writer.offset, self.emit_size()); |
254 | 0 | } |
255 | | |
256 | 0 | fn node_count(&self) -> usize { |
257 | 0 | self.unwind_codes |
258 | 0 | .iter() |
259 | 0 | .fold(0, |nodes, c| nodes + c.node_count()) |
260 | 0 | } |
261 | | } |
262 | | |
263 | | const UNWIND_RBP_REG: u8 = 5; |
264 | | |
265 | 0 | pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<crate::machinst::Reg>>( |
266 | 0 | insts: &[(CodeOffset, UnwindInst)], |
267 | 0 | ) -> CodegenResult<UnwindInfo> { |
268 | 0 | let mut unwind_codes = vec![]; |
269 | 0 | let mut frame_register_offset = 0; |
270 | 0 | let mut max_unwind_offset = 0; |
271 | 0 | for &(instruction_offset, ref inst) in insts { |
272 | 0 | let instruction_offset = ensure_unwind_offset(instruction_offset)?; |
273 | 0 | match inst { |
274 | 0 | &UnwindInst::PushFrameRegs { .. } => { |
275 | 0 | unwind_codes.push(UnwindCode::PushRegister { |
276 | 0 | instruction_offset, |
277 | 0 | reg: UNWIND_RBP_REG, |
278 | 0 | }); |
279 | 0 | } |
280 | | &UnwindInst::DefineNewFrame { |
281 | 0 | offset_downward_to_clobbers, |
282 | 0 | .. |
283 | 0 | } => { |
284 | 0 | frame_register_offset = ensure_unwind_offset(offset_downward_to_clobbers)?; |
285 | 0 | unwind_codes.push(UnwindCode::SetFPReg { instruction_offset }); |
286 | | } |
287 | 0 | &UnwindInst::StackAlloc { size } => { |
288 | 0 | unwind_codes.push(UnwindCode::StackAlloc { |
289 | 0 | instruction_offset, |
290 | 0 | size, |
291 | 0 | }); |
292 | 0 | } |
293 | | &UnwindInst::SaveReg { |
294 | 0 | clobber_offset, |
295 | 0 | reg, |
296 | 0 | } => match MR::map(reg.into()) { |
297 | 0 | MappedRegister::Int(reg) => { |
298 | 0 | unwind_codes.push(UnwindCode::SaveReg { |
299 | 0 | instruction_offset, |
300 | 0 | reg, |
301 | 0 | stack_offset: clobber_offset, |
302 | 0 | }); |
303 | 0 | } |
304 | 0 | MappedRegister::Xmm(reg) => { |
305 | 0 | unwind_codes.push(UnwindCode::SaveXmm { |
306 | 0 | instruction_offset, |
307 | 0 | reg, |
308 | 0 | stack_offset: clobber_offset, |
309 | 0 | }); |
310 | 0 | } |
311 | | }, |
312 | | &UnwindInst::Aarch64SetPointerAuth { .. } => { |
313 | 0 | unreachable!("no aarch64 on x64"); |
314 | | } |
315 | | } |
316 | 0 | max_unwind_offset = instruction_offset; |
317 | | } |
318 | | |
319 | 0 | Ok(UnwindInfo { |
320 | 0 | flags: 0, |
321 | 0 | prologue_size: max_unwind_offset, |
322 | 0 | frame_register: Some(UNWIND_RBP_REG), |
323 | 0 | frame_register_offset, |
324 | 0 | unwind_codes, |
325 | 0 | }) |
326 | 0 | } |
327 | | |
328 | 0 | fn ensure_unwind_offset(offset: u32) -> CodegenResult<u8> { |
329 | 0 | if offset > 255 { |
330 | 0 | warn!("function prologues cannot exceed 255 bytes in size for Windows x64"); |
331 | 0 | return Err(CodegenError::CodeTooLarge); |
332 | 0 | } |
333 | 0 | Ok(offset as u8) |
334 | 0 | } |