Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/goblin-0.10.7/src/pe/exception.rs
Line
Count
Source
1
//! Exception handling and stack unwinding for x64 and ARM64.
2
//!
3
//! Exception information is exposed via the [`ExceptionData`] structure. If present in a PE file,
4
//! it contains a list of [`RuntimeFunction`] entries that can be used to get [`UnwindInfo`] for a
5
//! particular code location.
6
//!
7
//! Unwind information contains a list of unwind codes which specify the operations that are
8
//! necessary to restore registers (including the stack pointer RSP) when unwinding out of a
9
//! function.
10
//!
11
//! Depending on where the instruction pointer lies, there are three strategies to unwind:
12
//!
13
//!  1. If the RIP is within an epilog, then control is leaving the function, there can be no
14
//!     exception handler associated with this exception for this function, and the effects of the
15
//!     epilog must be continued to compute the context of the caller function. To determine if the
16
//!     RIP is within an epilog, the code stream from RIP on is examined. If that code stream can be
17
//!     matched to the trailing portion of a legitimate epilog, then it's in an epilog, and the
18
//!     remaining portion of the epilog is simulated, with the context record updated as each
19
//!     instruction is processed. After this, step 1 is repeated.
20
//!
21
//!  2. Case b) If the RIP lies within the prologue, then control has not entered the function,
22
//!     there can be no exception handler associated with this exception for this function, and the
23
//!     effects of the prolog must be undone to compute the context of the caller function. The RIP
24
//!     is within the prolog if the distance from the function start to the RIP is less than or
25
//!     equal to the prolog size encoded in the unwind info. The effects of the prolog are unwound
26
//!     by scanning forward through the unwind codes array for the first entry with an offset less
27
//!     than or equal to the offset of the RIP from the function start, then undoing the effect of
28
//!     all remaining items in the unwind code array. Step 1 is then repeated.
29
//!
30
//!  3. If the RIP is not within a prolog or epilog and the function has an exception handler, then
31
//!     the language-specific handler is called. The handler scans its data and calls filter
32
//!     functions as appropriate. The language-specific handler can return that the exception was
33
//!     handled or that the search is to be continued. It can also initiate an unwind directly.
34
//!
35
//! For more information, see [x64 exception handling].
36
//!
37
//! [`ExceptionData`]: struct.ExceptionData.html
38
//! [`RuntimeFunction`]: struct.RuntimeFunction.html
39
//! [`UnwindInfo`]: struct.UnwindInfo.html
40
//! [x64 exception handling]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2017
41
42
use core::cmp::Ordering;
43
use core::fmt;
44
use core::iter::FusedIterator;
45
46
use scroll::ctx::TryFromCtx;
47
use scroll::{self, Pread, Pwrite, SizeWith};
48
49
use crate::error;
50
51
use crate::pe::data_directories;
52
use crate::pe::options;
53
use crate::pe::section_table;
54
use crate::pe::utils;
55
56
/// **N**o handlers.
57
#[allow(unused)]
58
const UNW_FLAG_NHANDLER: u8 = 0x00;
59
/// The function has an exception handler that should be called when looking for functions that need
60
/// to examine exceptions.
61
const UNW_FLAG_EHANDLER: u8 = 0x01;
62
/// The function has a termination handler that should be called when unwinding an exception.
63
const UNW_FLAG_UHANDLER: u8 = 0x02;
64
/// This unwind info structure is not the primary one for the procedure. Instead, the chained unwind
65
/// info entry is the contents of a previous `RUNTIME_FUNCTION` entry. If this flag is set, then the
66
/// `UNW_FLAG_EHANDLER` and `UNW_FLAG_UHANDLER` flags must be cleared. Also, the frame register and
67
/// fixed-stack allocation fields must have the same values as in the primary unwind info.
68
const UNW_FLAG_CHAININFO: u8 = 0x04;
69
70
/// info == register number
71
const UWOP_PUSH_NONVOL: u8 = 0;
72
/// no info, alloc size in next 2 slots
73
const UWOP_ALLOC_LARGE: u8 = 1;
74
/// info == size of allocation / 8 - 1
75
const UWOP_ALLOC_SMALL: u8 = 2;
76
/// no info, FP = RSP + UNWIND_INFO.FPRegOffset*16
77
const UWOP_SET_FPREG: u8 = 3;
78
/// info == register number, offset in next slot
79
const UWOP_SAVE_NONVOL: u8 = 4;
80
/// info == register number, offset in next 2 slots
81
const UWOP_SAVE_NONVOL_FAR: u8 = 5;
82
/// changes the structure of unwind codes to `struct Epilogue`.
83
/// (was UWOP_SAVE_XMM in version 1, but deprecated and removed)
84
const UWOP_EPILOG: u8 = 6;
85
/// reserved
86
/// (was UWOP_SAVE_XMM_FAR in version 1, but deprecated and removed)
87
const UWOP_SPARE_CODE: u8 = 7;
88
/// info == XMM reg number, offset in next slot
89
const UWOP_SAVE_XMM128: u8 = 8;
90
/// info == XMM reg number, offset in next 2 slots
91
const UWOP_SAVE_XMM128_FAR: u8 = 9;
92
/// info == 0: no error-code, 1: error-code
93
const UWOP_PUSH_MACHFRAME: u8 = 10;
94
95
/// Size of `RuntimeFunction` entries.
96
const RUNTIME_FUNCTION_SIZE: usize = 12;
97
/// Size of unwind code slots. Codes take 1 - 3 slots.
98
const UNWIND_CODE_SIZE: usize = 2;
99
100
/// Represents a single entry in a Windows PE exception handling scope table `C_SCOPE_TABLE_ENTRY`.
101
///
102
/// Each entry defines a protected range of code and its associated exception handler.
103
/// These entries are typically found in the scope table associated with `UNWIND_INFO`
104
/// structures in Windows x64 exception handling.
105
#[derive(Debug, Copy, Clone, Default, PartialEq, Hash, Pread, Pwrite, SizeWith)]
106
#[repr(C)]
107
pub struct ScopeTableEntry {
108
    /// The starting RVA (relative virtual address) of the protected code region.
109
    ///
110
    /// This marks the beginning of a `try` block.
111
    pub begin: u32,
112
113
    /// The ending RVA (exclusive) of the protected code region.
114
    ///
115
    /// This marks the end of the `try` block.
116
    pub end: u32,
117
118
    /// The RVA of the exception handler function.
119
    ///
120
    /// e.g., be invoked when an exception occurs in the associated code range.
121
    pub handler: u32,
122
123
    /// The RVA of the continuation target after the handler is executed.
124
    ///
125
    /// This is used for control transfer (e.g., continuation blocks, to resume execution after `finally`).
126
    pub target: u32,
127
}
128
129
/// Iterator over [ScopeTableEntry] entries in `C_SCOPE_TABLE`.
130
#[derive(Debug)]
131
pub struct ScopeTableIterator<'a> {
132
    data: &'a [u8],
133
    offset: usize,
134
}
135
136
impl Iterator for ScopeTableIterator<'_> {
137
    type Item = ScopeTableEntry;
138
139
0
    fn next(&mut self) -> Option<Self::Item> {
140
0
        if self.offset >= self.data.len() {
141
0
            return None;
142
0
        }
143
144
        // It is guaranteed that .expect here is really a unreachable.
145
        // See: that we do `num_entries * core::mem::size_of::<ScopeTableEntry>() as u32;`
146
0
        Some(
147
0
            self.data
148
0
                .gread_with(&mut self.offset, scroll::LE)
149
0
                .expect("Scope table is not aligned"),
150
0
        )
151
0
    }
152
153
0
    fn size_hint(&self) -> (usize, Option<usize>) {
154
0
        let len = self.data.len() / core::mem::size_of::<ScopeTableEntry>();
155
0
        (len, Some(len))
156
0
    }
157
}
158
159
impl FusedIterator for ScopeTableIterator<'_> {}
160
impl ExactSizeIterator for ScopeTableIterator<'_> {}
161
162
/// An unwind entry for a range of a function.
163
///
164
/// Unwind information for this function can be loaded with [`ExceptionData::get_unwind_info`].
165
///
166
/// [`ExceptionData::get_unwind_info`]: struct.ExceptionData.html#method.get_unwind_info
167
#[repr(C)]
168
#[derive(Copy, Clone, PartialEq, Default, Pread, Pwrite)]
169
pub struct RuntimeFunction {
170
    /// Function start address.
171
    pub begin_address: u32,
172
    /// Function end address.
173
    pub end_address: u32,
174
    /// Unwind info address.
175
    pub unwind_info_address: u32,
176
}
177
178
impl fmt::Debug for RuntimeFunction {
179
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180
0
        f.debug_struct("RuntimeFunction")
181
0
            .field("begin_address", &format_args!("{:#x}", self.begin_address))
182
0
            .field("end_address", &format_args!("{:#x}", self.end_address))
183
0
            .field(
184
0
                "unwind_info_address",
185
0
                &format_args!("{:#x}", self.unwind_info_address),
186
0
            )
187
0
            .finish()
188
0
    }
189
}
190
191
/// Iterator over runtime function entries in [`ExceptionData`](struct.ExceptionData.html).
192
#[derive(Debug)]
193
pub struct RuntimeFunctionIterator<'a> {
194
    data: &'a [u8],
195
}
196
197
impl Iterator for RuntimeFunctionIterator<'_> {
198
    type Item = error::Result<RuntimeFunction>;
199
200
0
    fn next(&mut self) -> Option<Self::Item> {
201
0
        if self.data.is_empty() {
202
0
            return None;
203
0
        }
204
205
0
        Some(match self.data.pread_with(0, scroll::LE) {
206
0
            Ok(func) => {
207
0
                self.data = &self.data[RUNTIME_FUNCTION_SIZE..];
208
0
                Ok(func)
209
            }
210
0
            Err(error) => {
211
0
                self.data = &[];
212
0
                Err(error.into())
213
            }
214
        })
215
0
    }
216
217
0
    fn size_hint(&self) -> (usize, Option<usize>) {
218
0
        let len = self.data.len() / RUNTIME_FUNCTION_SIZE;
219
0
        (len, Some(len))
220
0
    }
221
}
222
223
impl FusedIterator for RuntimeFunctionIterator<'_> {}
224
impl ExactSizeIterator for RuntimeFunctionIterator<'_> {}
225
226
/// An x64 register used during unwinding.
227
///
228
///  - `0` - `15`: General purpose registers
229
///  - `17` - `32`: XMM registers
230
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
231
pub struct Register(pub u8);
232
233
impl Register {
234
0
    fn xmm(number: u8) -> Self {
235
0
        Register(number + 17)
236
0
    }
237
238
    /// Returns the x64 register name.
239
0
    pub fn name(self) -> &'static str {
240
0
        match self.0 {
241
0
            0 => "$rax",
242
0
            1 => "$rcx",
243
0
            2 => "$rdx",
244
0
            3 => "$rbx",
245
0
            4 => "$rsp",
246
0
            5 => "$rbp",
247
0
            6 => "$rsi",
248
0
            7 => "$rdi",
249
0
            8 => "$r8",
250
0
            9 => "$r9",
251
0
            10 => "$r10",
252
0
            11 => "$r11",
253
0
            12 => "$r12",
254
0
            13 => "$r13",
255
0
            14 => "$r14",
256
0
            15 => "$r15",
257
0
            16 => "$rip",
258
0
            17 => "$xmm0",
259
0
            18 => "$xmm1",
260
0
            19 => "$xmm2",
261
0
            20 => "$xmm3",
262
0
            21 => "$xmm4",
263
0
            22 => "$xmm5",
264
0
            23 => "$xmm6",
265
0
            24 => "$xmm7",
266
0
            25 => "$xmm8",
267
0
            26 => "$xmm9",
268
0
            27 => "$xmm10",
269
0
            28 => "$xmm11",
270
0
            29 => "$xmm12",
271
0
            30 => "$xmm13",
272
0
            31 => "$xmm14",
273
0
            32 => "$xmm15",
274
0
            _ => "",
275
        }
276
0
    }
277
}
278
279
/// An unsigned offset to a value in the local stack frame.
280
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
281
pub enum StackFrameOffset {
282
    /// Offset from the current RSP, that is, the lowest address of the fixed stack allocation.
283
    ///
284
    /// To restore this register, read the value at the given offset from the RSP.
285
    RSP(u32),
286
287
    /// Offset from the value of the frame pointer register.
288
    ///
289
    /// To restore this register, read the value at the given offset from the FP register, reduced
290
    /// by the `frame_register_offset` value specified in the `UnwindInfo` structure. By definition,
291
    /// the frame pointer register is any register other than RAX (`0`).
292
    FP(u32),
293
}
294
295
impl StackFrameOffset {
296
0
    fn with_ctx(offset: u32, ctx: UnwindOpContext) -> Self {
297
0
        match ctx.frame_register {
298
0
            Register(0) => StackFrameOffset::RSP(offset),
299
0
            Register(_) => StackFrameOffset::FP(offset),
300
        }
301
0
    }
302
}
303
304
impl fmt::Display for Register {
305
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306
0
        f.write_str(self.name())
307
0
    }
308
}
309
310
/// An unwind operation corresponding to code in the function prolog.
311
///
312
/// Unwind operations can be used to reverse the effects of the function prolog and restore register
313
/// values of parent stack frames that have been saved to the stack.
314
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
315
#[non_exhaustive]
316
pub enum UnwindOperation {
317
    /// Push a nonvolatile integer register, decrementing `RSP` by 8.
318
    PushNonVolatile(Register),
319
320
    /// Allocate a fixed-size area on the stack.
321
    Alloc(u32),
322
323
    /// Establish the frame pointer register by setting the register to some offset of the current
324
    /// RSP. The use of an offset permits establishing a frame pointer that points to the middle of
325
    /// the fixed stack allocation, helping code density by allowing more accesses to use short
326
    /// instruction forms.
327
    SetFPRegister,
328
329
    /// Save a nonvolatile integer register on the stack using a MOV instead of a PUSH. This code is
330
    /// primarily used for shrink-wrapping, where a nonvolatile register is saved to the stack in a
331
    /// position that was previously allocated.
332
    SaveNonVolatile(Register, StackFrameOffset),
333
334
    /// Save the lower 64 bits of a nonvolatile XMM register on the stack.
335
    SaveXMM(Register, StackFrameOffset),
336
337
    /// Describes the function epilog location and size (Version 2).
338
    ///
339
    /// [UWOP_EPILOG] entries work together to describe where epilogues are located in the function,
340
    /// but not what operations they perform. The actual operations performed during epilogue
341
    /// execution are the reverse of the prolog operations.
342
    ///
343
    /// These entries always appear at the beginning of the [UnwindCode] array, with a minimum of
344
    /// two entries required for alignment purposes, even when only one epilogue exists. The first
345
    /// [UWOP_EPILOG] entry is special as it contains the size of the epilogue in its `offset_low_or_size`
346
    /// field. If bit `0` of its `offset_high_or_flags` field is set, this first entry also describes
347
    /// an epilogue located at the function's end, with `offset_low_or_size` serving dual purpose as
348
    /// both size and offset. When bit `0` is not set, the first entry contains only the size, and
349
    /// subsequent [UWOP_EPILOG] entries provide the epilogue locations.
350
    ///
351
    /// Each subsequent [UWOP_EPILOG] entry describes an additional epilogue location using a 12-bit
352
    /// offset from the function's end address. The offset is formed by combining `offset_low_or_size`
353
    /// (lower 8 bits) with the lower 4 bits of `offset_high_or_flags` (upper 4 bits). The epilogue's
354
    /// starting address is calculated by subtracting this offset from the function's `EndAddress`.
355
    Epilog {
356
        /// For the first [UWOP_EPILOG] entry:
357
        /// * Size of the epilogue in bytes
358
        /// * If `offset_high_or_flags` bit 0 is set, also serves as offset
359
        ///
360
        /// For subsequent entries:
361
        /// * Lower 8 bits of the offset from function end
362
        offset_low_or_size: u8,
363
364
        /// For the first [UWOP_EPILOG] entry:
365
        /// * Bit 0: If set, epilogue is at function end and `offset_low_or_size`
366
        ///   is also the offset
367
        /// * Bits 1-3: Reserved/unused
368
        ///
369
        /// For subsequent entries:
370
        /// * Upper 4 bits of the offset from function end (bits 0-3)
371
        ///
372
        /// The complete offset is computed as:
373
        /// `EndAddress` - (`offset_high_or_flags` << 8 | `offset_low_or_size`)
374
        offset_high_or_flags: u8,
375
    },
376
377
    /// Save all 128 bits of a nonvolatile XMM register on the stack.
378
    SaveXMM128(Register, StackFrameOffset),
379
380
    /// Push a machine frame. This is used to record the effect of a hardware interrupt or
381
    /// exception. Depending on the error flag, this frame has two different layouts.
382
    ///
383
    /// This unwind code always appears in a dummy prolog, which is never actually executed but
384
    /// instead appears before the real entry point of an interrupt routine, and exists only to
385
    /// provide a place to simulate the push of a machine frame. This operation records that
386
    /// simulation, which indicates the machine has conceptually done this:
387
    ///
388
    ///  1. Pop RIP return address from top of stack into `temp`
389
    ///  2. `$ss`, Push old `$rsp`, `$rflags`, `$cs`, `temp`
390
    ///  3. If error flag is `true`, push the error code
391
    ///
392
    /// Without an error code, RSP was incremented by `40` and the following was frame pushed:
393
    ///
394
    /// Offset   | Value
395
    /// ---------|--------
396
    /// RSP + 32 | `$ss`
397
    /// RSP + 24 | old `$rsp`
398
    /// RSP + 16 | `$rflags`
399
    /// RSP +  8 | `$cs`
400
    /// RSP +  0 | `$rip`
401
    ///
402
    /// With an error code, RSP was incremented by `48` and the following was frame pushed:
403
    ///
404
    /// Offset   | Value
405
    /// ---------|--------
406
    /// RSP + 40 | `$ss`
407
    /// RSP + 32 | old `$rsp`
408
    /// RSP + 24 | `$rflags`
409
    /// RSP + 16 | `$cs`
410
    /// RSP +  8 | `$rip`
411
    /// RSP +  0 | error code
412
    PushMachineFrame(bool),
413
414
    /// A reserved operation without effect.
415
    Noop,
416
}
417
418
/// Context used to parse unwind operation.
419
#[derive(Clone, Copy, Debug, PartialEq)]
420
struct UnwindOpContext {
421
    /// Version of the unwind info.
422
    version: u8,
423
424
    /// The nonvolatile register used as the frame pointer of this function.
425
    ///
426
    /// If this register is non-zero, all stack frame offsets used in unwind operations are of type
427
    /// `StackFrameOffset::FP`. When loading these offsets, they have to be based off the value of
428
    /// this frame register instead of the conventional RSP. This allows the RSP to be modified.
429
    frame_register: Register,
430
}
431
432
/// An unwind operation that is executed at a particular place in the function prolog.
433
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
434
pub struct UnwindCode {
435
    /// Offset of the corresponding instruction in the function prolog.
436
    ///
437
    /// To be precise, this is the offset from the beginning of the prolog of the end of the
438
    /// instruction that performs this operation, plus 1 (that is, the offset of the start of the
439
    /// next instruction).
440
    ///
441
    /// Unwind codes are ordered by this offset in reverse order, suitable for unwinding.
442
    pub code_offset: u8,
443
444
    /// The operation that was performed by the code in the prolog.
445
    pub operation: UnwindOperation,
446
}
447
448
impl<'a> TryFromCtx<'a, UnwindOpContext> for UnwindCode {
449
    type Error = error::Error;
450
    #[inline]
451
0
    fn try_from_ctx(bytes: &'a [u8], ctx: UnwindOpContext) -> Result<(Self, usize), Self::Error> {
452
0
        let mut read = 0;
453
0
        let code_offset = bytes.gread_with::<u8>(&mut read, scroll::LE)?;
454
0
        let operation = bytes.gread_with::<u8>(&mut read, scroll::LE)?;
455
456
0
        let operation_code = operation & 0xf;
457
0
        let operation_info = operation >> 4;
458
459
0
        let operation = match operation_code {
460
            self::UWOP_PUSH_NONVOL => {
461
0
                let register = Register(operation_info);
462
0
                UnwindOperation::PushNonVolatile(register)
463
            }
464
            self::UWOP_ALLOC_LARGE => {
465
0
                let offset = match operation_info {
466
0
                    0 => u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 8,
467
0
                    1 => bytes.gread_with::<u32>(&mut read, scroll::LE)?,
468
0
                    i => {
469
0
                        let msg = format!("invalid op info ({}) for UWOP_ALLOC_LARGE", i);
470
0
                        return Err(error::Error::Malformed(msg));
471
                    }
472
                };
473
0
                UnwindOperation::Alloc(offset)
474
            }
475
            self::UWOP_ALLOC_SMALL => {
476
0
                let offset = u32::from(operation_info) * 8 + 8;
477
0
                UnwindOperation::Alloc(offset)
478
            }
479
0
            self::UWOP_SET_FPREG => UnwindOperation::SetFPRegister,
480
            self::UWOP_SAVE_NONVOL => {
481
0
                let register = Register(operation_info);
482
0
                let offset = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 8;
483
0
                UnwindOperation::SaveNonVolatile(register, StackFrameOffset::with_ctx(offset, ctx))
484
            }
485
            self::UWOP_SAVE_NONVOL_FAR => {
486
0
                let register = Register(operation_info);
487
0
                let offset = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
488
0
                UnwindOperation::SaveNonVolatile(register, StackFrameOffset::with_ctx(offset, ctx))
489
            }
490
            self::UWOP_EPILOG => {
491
0
                if ctx.version == 1 {
492
                    // Version 1: This was UWOP_SAVE_XMM
493
0
                    let data = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 16;
494
0
                    let register = Register::xmm(operation_info);
495
0
                    UnwindOperation::SaveXMM(register, StackFrameOffset::with_ctx(data, ctx))
496
0
                } else if ctx.version == 2 {
497
                    // Version 2: UWOP_EPILOG - describes epilogue locations
498
                    // See https://github.com/BlancLoup/weekly-geekly.github.io/blob/1cbdf1c6127fcdaeda1f01bcbb006febd17d5a95/articles/322956/index.html
499
                    // See https://github.com/Montura/cpp/blob/4393c678ee8e44dd98feb7a198c3983a6108cef8/src/exception_handling/msvc/eh_msvc_cxx_EH_x64.md?plain=1#L119
500
0
                    UnwindOperation::Epilog {
501
0
                        offset_low_or_size: code_offset,
502
0
                        offset_high_or_flags: operation_info,
503
0
                    }
504
                } else {
505
0
                    let msg = format!(
506
0
                        "Unwind info version has to be either one of `1` or `2`: {}",
507
                        ctx.version
508
                    );
509
0
                    return Err(error::Error::Malformed(msg));
510
                }
511
            }
512
            self::UWOP_SPARE_CODE => {
513
0
                let data = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
514
0
                if ctx.version == 1 {
515
0
                    let register = Register::xmm(operation_info);
516
0
                    UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(data, ctx))
517
0
                } else if ctx.version == 2 {
518
0
                    UnwindOperation::Noop
519
                } else {
520
0
                    let msg = format!(
521
0
                        "Unwind info version has to be either one of `1` or `2`: {}",
522
                        ctx.version
523
                    );
524
0
                    return Err(error::Error::Malformed(msg));
525
                }
526
            }
527
            self::UWOP_SAVE_XMM128 => {
528
0
                let register = Register::xmm(operation_info);
529
0
                let offset = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 16;
530
0
                UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(offset, ctx))
531
            }
532
            self::UWOP_SAVE_XMM128_FAR => {
533
0
                let register = Register::xmm(operation_info);
534
0
                let offset = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
535
0
                UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(offset, ctx))
536
            }
537
            self::UWOP_PUSH_MACHFRAME => {
538
0
                let is_error = match operation_info {
539
0
                    0 => false,
540
0
                    1 => true,
541
0
                    i => {
542
0
                        let msg = format!("invalid op info ({}) for UWOP_PUSH_MACHFRAME", i);
543
0
                        return Err(error::Error::Malformed(msg));
544
                    }
545
                };
546
0
                UnwindOperation::PushMachineFrame(is_error)
547
            }
548
0
            op => {
549
0
                let msg = format!("unknown unwind op code ({})", op);
550
0
                return Err(error::Error::Malformed(msg));
551
            }
552
        };
553
554
0
        let code = UnwindCode {
555
0
            code_offset,
556
0
            operation,
557
0
        };
558
559
0
        Ok((code, read))
560
0
    }
561
}
562
563
/// An iterator over unwind codes for a function or part of a function, returned from
564
/// [`UnwindInfo`].
565
///
566
/// [`UnwindInfo`]: struct.UnwindInfo.html
567
#[derive(Clone, Debug)]
568
pub struct UnwindCodeIterator<'a> {
569
    bytes: &'a [u8],
570
    offset: usize,
571
    context: UnwindOpContext,
572
}
573
574
impl Iterator for UnwindCodeIterator<'_> {
575
    type Item = error::Result<UnwindCode>;
576
577
0
    fn next(&mut self) -> Option<Self::Item> {
578
0
        if self.offset >= self.bytes.len() {
579
0
            return None;
580
0
        }
581
582
0
        Some(self.bytes.gread_with(&mut self.offset, self.context))
583
0
    }
584
585
0
    fn size_hint(&self) -> (usize, Option<usize>) {
586
0
        let upper = (self.bytes.len() - self.offset) / UNWIND_CODE_SIZE;
587
        // the largest codes take up three slots
588
0
        let lower = (upper + 3 - (upper % 3)) / 3;
589
0
        (lower, Some(upper))
590
0
    }
591
}
592
593
impl FusedIterator for UnwindCodeIterator<'_> {}
594
595
/// A language-specific handler that is called as part of the search for an exception handler or as
596
/// part of an unwind.
597
#[derive(Copy, Clone, Debug, PartialEq)]
598
pub enum UnwindHandler<'a> {
599
    /// The image-relative address of an exception handler and its implementation-defined data.
600
    ExceptionHandler(u32, &'a [u8]),
601
    /// The image-relative address of a termination handler and its implementation-defined data.
602
    TerminationHandler(u32, &'a [u8]),
603
}
604
605
/// Unwind information for a function or portion of a function.
606
///
607
/// The unwind info structure is used to record the effects a function has on the stack pointer and
608
/// where the nonvolatile registers are saved on the stack. The unwind codes can be enumerated with
609
/// [`unwind_codes`].
610
///
611
/// This unwind info might only be secondary information, and link to a [chained unwind handler].
612
/// For unwinding, this link shall be followed until the root unwind info record has been resolved.
613
///
614
/// [`unwind_codes`]: struct.UnwindInfo.html#method.unwind_codes
615
/// [chained unwind handler]: struct.UnwindInfo.html#structfield.chained_info
616
#[derive(Clone)]
617
pub struct UnwindInfo<'a> {
618
    /// Version of this unwind info.
619
    pub version: u8,
620
621
    /// Length of the function prolog in bytes.
622
    pub size_of_prolog: u8,
623
624
    /// The nonvolatile register used as the frame pointer of this function.
625
    ///
626
    /// If this register is non-zero, all stack frame offsets used in unwind operations are of type
627
    /// `StackFrameOffset::FP`. When loading these offsets, they have to be based off the value of
628
    /// this frame register instead of the conventional RSP. This allows the RSP to be modified.
629
    pub frame_register: Register,
630
631
    /// Offset from RSP that is applied to the FP register when it is established.
632
    ///
633
    /// When loading offsets of type `StackFrameOffset::FP` from the stack, this offset has to be
634
    /// subtracted before loading the value since the actual RSP was lower by that amount in the
635
    /// prolog.
636
    pub frame_register_offset: u32,
637
638
    /// A record pointing to chained unwind information.
639
    ///
640
    /// If chained unwind info is present, then this unwind info is a secondary one and the linked
641
    /// unwind info contains primary information. Chained info is useful in two situations. First,
642
    /// it is used for noncontiguous code segments. Second, this mechanism is sometimes used to
643
    /// group volatile register saves.
644
    ///
645
    /// The referenced unwind info can itself specify chained unwind information, until it arrives
646
    /// at the root unwind info. Generally, the entire chain should be considered when unwinding.
647
    pub chained_info: Option<RuntimeFunction>,
648
649
    /// An exception or termination handler called as part of the unwind.
650
    pub handler: Option<UnwindHandler<'a>>,
651
652
    /// A list of unwind codes, sorted descending by code offset.
653
    code_bytes: &'a [u8],
654
}
655
656
impl<'a> UnwindInfo<'a> {
657
    /// Parses unwind information from the image at the given offset.
658
0
    pub fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
659
        // Read the version and flags fields, which are combined into a single byte.
660
0
        let version_flags: u8 = bytes.gread_with(&mut offset, scroll::LE)?;
661
0
        let version = version_flags & 0b111;
662
0
        let flags = version_flags >> 3;
663
664
0
        if version < 1 || version > 2 {
665
0
            let msg = format!("unsupported unwind code version ({})", version);
666
0
            return Err(error::Error::Malformed(msg));
667
0
        }
668
669
0
        let size_of_prolog = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
670
0
        let count_of_codes = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
671
672
        // Parse the frame register and frame register offset values, that are combined into a
673
        // single byte.
674
0
        let frame_info = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
675
        // If nonzero, then the function uses a frame pointer (FP), and this field is the number
676
        // of the nonvolatile register used as the frame pointer. The zero register value does
677
        // not need special casing since it will not be referenced by the unwind operations.
678
0
        let frame_register = Register(frame_info & 0xf);
679
        // The the scaled offset from RSP that is applied to the FP register when it's
680
        // established. The actual FP register is set to RSP + 16 * this number, allowing
681
        // offsets from 0 to 240.
682
0
        let frame_register_offset = u32::from((frame_info >> 4) * 16);
683
684
        // An array of items that explains the effect of the prolog on the nonvolatile registers and
685
        // RSP. Some unwind codes require more than one slot in the array.
686
0
        let codes_size = count_of_codes as usize * UNWIND_CODE_SIZE;
687
0
        let code_bytes = bytes.gread_with(&mut offset, codes_size)?;
688
689
        // For alignment purposes, the codes array typically has an even number of entries, and the
690
        // final entry is potentially unused. In that case, the array is one longer than indicated
691
        // by the count of unwind codes field.
692
        //
693
        // Sometimes, developers decided to handy craft unwind info in their assembly forget to align
694
        // unwind infos. This is not really desirable behavior for such cases anyway.
695
0
        if count_of_codes % 2 != 0 {
696
0
            offset += 2;
697
0
        }
698
699
0
        let mut chained_info = None;
700
0
        let mut handler = None;
701
702
        // If flag UNW_FLAG_CHAININFO is set then the UNWIND_INFO structure ends with three UWORDs.
703
        // These UWORDs represent the RUNTIME_FUNCTION information for the function of the chained
704
        // unwind.
705
0
        if flags & UNW_FLAG_CHAININFO != 0 {
706
0
            chained_info = Some(bytes.gread_with(&mut offset, scroll::LE)?);
707
708
        // The relative address of the language-specific handler is present in the UNWIND_INFO
709
        // whenever flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER are set. The language-specific
710
        // handler is called as part of the search for an exception handler or as part of an unwind.
711
0
        } else if flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER) != 0 {
712
0
            let address = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
713
0
            if offset > bytes.len() {
714
0
                return Err(error::Error::Malformed(format!(
715
0
                    "Offset {offset:#x} is too big to contain unwind handlers",
716
0
                )));
717
0
            }
718
0
            let data = &bytes[offset..];
719
720
0
            handler = Some(if flags & UNW_FLAG_EHANDLER != 0 {
721
0
                UnwindHandler::ExceptionHandler(address, data)
722
            } else {
723
0
                UnwindHandler::TerminationHandler(address, data)
724
            });
725
0
        }
726
727
0
        Ok(UnwindInfo {
728
0
            version,
729
0
            size_of_prolog,
730
0
            frame_register,
731
0
            frame_register_offset,
732
0
            chained_info,
733
0
            handler,
734
0
            code_bytes,
735
0
        })
736
0
    }
737
738
    /// Returns an iterator over unwind codes in this unwind info.
739
    ///
740
    /// Unwind codes are iterated in descending `code_offset` order suitable for unwinding. If the
741
    /// optional [`chained_info`](Self::chained_info) is present, codes of that unwind info should be interpreted
742
    /// immediately afterwards.
743
0
    pub fn unwind_codes(&self) -> UnwindCodeIterator<'a> {
744
0
        UnwindCodeIterator {
745
0
            bytes: self.code_bytes,
746
0
            offset: 0,
747
0
            context: UnwindOpContext {
748
0
                version: self.version,
749
0
                frame_register: self.frame_register,
750
0
            },
751
0
        }
752
0
    }
753
754
    /// Returns an iterator over C scope table entries in this unwind info.
755
    ///
756
    /// If this unwind info has no [UnwindHandler::ExceptionHandler], this will always return `None`.
757
0
    pub fn c_scope_table_entries(&self) -> Option<ScopeTableIterator<'a>> {
758
0
        let UnwindHandler::ExceptionHandler(_, data) = self.handler? else {
759
0
            return None;
760
        };
761
0
        let mut offset = 0;
762
0
        let num_entries = data.gread_with::<u32>(&mut offset, scroll::LE).ok()?;
763
0
        let table_size = num_entries * core::mem::size_of::<ScopeTableEntry>() as u32;
764
0
        let data = data.pread_with::<&[u8]>(offset, table_size as usize).ok()?;
765
0
        Some(ScopeTableIterator { data, offset: 0 })
766
0
    }
767
}
768
769
impl fmt::Debug for UnwindInfo<'_> {
770
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
771
0
        let count_of_codes = self.code_bytes.len() / UNWIND_CODE_SIZE;
772
773
0
        f.debug_struct("UnwindInfo")
774
0
            .field("version", &self.version)
775
0
            .field("size_of_prolog", &self.size_of_prolog)
776
0
            .field("frame_register", &self.frame_register)
777
0
            .field("frame_register_offset", &self.frame_register_offset)
778
0
            .field("count_of_codes", &count_of_codes)
779
0
            .field("chained_info", &self.chained_info)
780
0
            .field("handler", &self.handler)
781
0
            .finish()
782
0
    }
783
}
784
785
impl<'a> IntoIterator for &'_ UnwindInfo<'a> {
786
    type Item = error::Result<UnwindCode>;
787
    type IntoIter = UnwindCodeIterator<'a>;
788
789
    #[inline]
790
0
    fn into_iter(self) -> Self::IntoIter {
791
0
        self.unwind_codes()
792
0
    }
793
}
794
795
/// Exception handling and stack unwind information for functions in the image.
796
pub struct ExceptionData<'a> {
797
    bytes: &'a [u8],
798
    offset: usize,
799
    size: usize,
800
    file_alignment: u32,
801
}
802
803
impl<'a> ExceptionData<'a> {
804
    /// Parses exception data from the image at the given offset.
805
0
    pub fn parse(
806
0
        bytes: &'a [u8],
807
0
        directory: data_directories::DataDirectory,
808
0
        sections: &[section_table::SectionTable],
809
0
        file_alignment: u32,
810
0
    ) -> error::Result<Self> {
811
0
        Self::parse_with_opts(
812
0
            bytes,
813
0
            directory,
814
0
            sections,
815
0
            file_alignment,
816
0
            &options::ParseOptions::default(),
817
        )
818
0
    }
819
820
    /// Parses exception data from the image at the given offset.
821
295
    pub fn parse_with_opts(
822
295
        bytes: &'a [u8],
823
295
        directory: data_directories::DataDirectory,
824
295
        sections: &[section_table::SectionTable],
825
295
        file_alignment: u32,
826
295
        opts: &options::ParseOptions,
827
295
    ) -> error::Result<Self> {
828
295
        let size = directory.size as usize;
829
830
295
        if size % RUNTIME_FUNCTION_SIZE != 0 {
831
74
            return Err(error::Error::from(scroll::Error::BadInput {
832
74
                size,
833
74
                msg: "invalid exception directory table size",
834
74
            }));
835
221
        }
836
837
221
        Self::parse_inner(bytes, directory, sections, file_alignment, opts)
838
295
    }
839
840
    /// Parses ARM64 exception data from the image at the given offset.
841
189
    pub fn parse_arm64_with_opts(
842
189
        bytes: &'a [u8],
843
189
        directory: data_directories::DataDirectory,
844
189
        sections: &[section_table::SectionTable],
845
189
        file_alignment: u32,
846
189
        opts: &options::ParseOptions,
847
189
    ) -> error::Result<Self> {
848
189
        let size = directory.size as usize;
849
850
189
        if size % size_of::<Arm64RuntimeFunction>() != 0 {
851
162
            return Err(error::Error::from(scroll::Error::BadInput {
852
162
                size,
853
162
                msg: "invalid exception directory table size",
854
162
            }));
855
27
        }
856
857
27
        Self::parse_inner(bytes, directory, sections, file_alignment, opts)
858
189
    }
859
860
248
    fn parse_inner(
861
248
        bytes: &'a [u8],
862
248
        directory: data_directories::DataDirectory,
863
248
        sections: &[section_table::SectionTable],
864
248
        file_alignment: u32,
865
248
        opts: &options::ParseOptions,
866
248
    ) -> error::Result<Self> {
867
248
        let size = directory.size as usize;
868
248
        let rva = directory.virtual_address as usize;
869
248
        let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
870
30
            error::Error::Malformed(format!("cannot map exception_rva ({:#x}) into offset", rva))
871
30
        })?;
872
873
218
        if offset % 4 != 0 {
874
38
            return Err(error::Error::from(scroll::Error::BadOffset(offset)));
875
180
        }
876
877
180
        Ok(ExceptionData {
878
180
            bytes,
879
180
            offset,
880
180
            size,
881
180
            file_alignment,
882
180
        })
883
248
    }
884
885
    /// The number of x64 function entries described by this exception data.
886
176
    pub fn len(&self) -> usize {
887
176
        self.size / RUNTIME_FUNCTION_SIZE
888
176
    }
889
890
    /// The number of ARM64 function entries described by this exception data.
891
0
    pub fn len_arm64(&self) -> usize {
892
0
        self.size / size_of::<Arm64RuntimeFunction>()
893
0
    }
894
895
    /// Indicating whether there are functions in this entry.
896
176
    pub fn is_empty(&self) -> bool {
897
176
        self.len() == 0
898
176
    }
899
900
    /// Iterates all function entries in order of their code offset.
901
    ///
902
    /// To search for a function by relative instruction address, use [`find_function`]. To resolve
903
    /// unwind information, use [`get_unwind_info`].
904
    ///
905
    /// [`find_function`]: struct.ExceptionData.html#method.find_function
906
    /// [`get_unwind_info`]: struct.ExceptionData.html#method.get_unwind_info
907
0
    pub fn functions(&self) -> RuntimeFunctionIterator<'a> {
908
0
        RuntimeFunctionIterator {
909
0
            data: &self.bytes[self.offset..self.offset + self.size],
910
0
        }
911
0
    }
912
913
    /// Returns the function at the given index.
914
0
    pub fn get_function(&self, index: usize) -> error::Result<RuntimeFunction> {
915
0
        self.get_function_by_offset(self.offset + index * RUNTIME_FUNCTION_SIZE)
916
0
    }
917
918
    /// Returns an iterator over ARM64 runtime function entries.
919
0
    pub fn functions_arm64(&self) -> Arm64RuntimeFunctionIterator<'a> {
920
0
        Arm64RuntimeFunctionIterator {
921
0
            data: &self.bytes[self.offset..self.offset + self.size],
922
0
        }
923
0
    }
924
925
    /// Returns an ARM64 runtime function at the given index.
926
0
    pub fn get_function_arm64(&self, index: usize) -> error::Result<Arm64RuntimeFunction> {
927
0
        self.get_function_by_offset_arm64(self.offset + index * size_of::<Arm64RuntimeFunction>())
928
0
    }
929
930
    /// Performs a binary search to find a function entry covering the given RVA relative to the
931
    /// image.
932
0
    pub fn find_function(&self, rva: u32) -> error::Result<Option<RuntimeFunction>> {
933
        // NB: Binary search implementation copied from std::slice::binary_search_by and adapted.
934
        // Theoretically, there should be nothing that causes parsing runtime functions to fail and
935
        // all access to the bytes buffer is guaranteed to be in range. However, since all other
936
        // functions also return Results, this is much more ergonomic here.
937
938
0
        let mut size = self.len();
939
0
        if size == 0 {
940
0
            return Ok(None);
941
0
        }
942
943
0
        let mut base = 0;
944
0
        while size > 1 {
945
0
            let half = size / 2;
946
0
            let mid = base + half;
947
0
            let offset = self.offset + mid * RUNTIME_FUNCTION_SIZE;
948
0
            let addr = self.bytes.pread_with::<u32>(offset, scroll::LE)?;
949
0
            base = if addr > rva { base } else { mid };
950
0
            size -= half;
951
        }
952
953
0
        let offset = self.offset + base * RUNTIME_FUNCTION_SIZE;
954
0
        let addr = self.bytes.pread_with::<u32>(offset, scroll::LE)?;
955
0
        let function = match addr.cmp(&rva) {
956
0
            Ordering::Less | Ordering::Equal => self.get_function(base)?,
957
0
            Ordering::Greater if base == 0 => return Ok(None),
958
0
            Ordering::Greater => self.get_function(base - 1)?,
959
        };
960
961
0
        if function.end_address > rva {
962
0
            Ok(Some(function))
963
        } else {
964
0
            Ok(None)
965
        }
966
0
    }
967
968
    /// Resolves unwind information for the given function entry.
969
0
    pub fn get_unwind_info(
970
0
        &self,
971
0
        function: RuntimeFunction,
972
0
        sections: &[section_table::SectionTable],
973
0
    ) -> error::Result<UnwindInfo<'a>> {
974
0
        self.get_unwind_info_with_opts(function, sections, &options::ParseOptions::default())
975
0
    }
976
977
    /// Resolves unwind information for the given ARM64 function entry.
978
0
    pub fn get_unwind_info_arm64(
979
0
        &self,
980
0
        function: Arm64RuntimeFunction,
981
0
        sections: &[section_table::SectionTable],
982
0
    ) -> Option<error::Result<Arm64UnwindInfo<'a>>> {
983
0
        self.get_unwind_info_arm64_with_opts(function, sections, &options::ParseOptions::default())
984
0
    }
985
986
    /// Resolves unwind information for the given function entry.
987
0
    pub fn get_unwind_info_with_opts(
988
0
        &self,
989
0
        mut function: RuntimeFunction,
990
0
        sections: &[section_table::SectionTable],
991
0
        opts: &options::ParseOptions,
992
0
    ) -> error::Result<UnwindInfo<'a>> {
993
0
        while function.unwind_info_address % 2 != 0 {
994
0
            let rva = (function.unwind_info_address & !1) as usize;
995
0
            function = self.get_function_by_rva_with_opts(rva, sections, opts)?;
996
        }
997
998
0
        let rva = function.unwind_info_address as usize;
999
0
        let offset =
1000
0
            utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
1001
0
                error::Error::Malformed(format!("cannot map unwind rva ({:#x}) into offset", rva))
1002
0
            })?;
1003
1004
0
        UnwindInfo::parse(self.bytes, offset)
1005
0
    }
1006
1007
    /// Resolves unwind information for the given ARM64 function entry.
1008
0
    pub fn get_unwind_info_arm64_with_opts(
1009
0
        &self,
1010
0
        function: Arm64RuntimeFunction,
1011
0
        sections: &[section_table::SectionTable],
1012
0
        opts: &options::ParseOptions,
1013
0
    ) -> Option<error::Result<Arm64UnwindInfo<'a>>> {
1014
0
        if function.flag() == ARM64_PDATA_REF_TO_FULL_XDATA {
1015
0
            let rva = function.unwind_data_rva() as usize;
1016
0
            let offset = match utils::find_offset(rva, sections, self.file_alignment, opts)
1017
0
                .ok_or_else(|| {
1018
0
                    error::Error::Malformed(format!("cannot map unwind rva ({rva:#x}) into offset"))
1019
0
                }) {
1020
0
                Ok(v) => v,
1021
0
                Err(err) => return Some(Err(err)),
1022
            };
1023
0
            Some(Arm64UnwindInfo::parse(self.bytes, offset))
1024
        } else {
1025
            // Unwind info is packed into the runtime function entry.
1026
0
            None
1027
        }
1028
0
    }
1029
1030
    #[allow(dead_code)]
1031
0
    fn get_function_by_rva(
1032
0
        &self,
1033
0
        rva: usize,
1034
0
        sections: &[section_table::SectionTable],
1035
0
    ) -> error::Result<RuntimeFunction> {
1036
0
        self.get_function_by_rva_with_opts(rva, sections, &options::ParseOptions::default())
1037
0
    }
1038
1039
0
    fn get_function_by_rva_with_opts(
1040
0
        &self,
1041
0
        rva: usize,
1042
0
        sections: &[section_table::SectionTable],
1043
0
        opts: &options::ParseOptions,
1044
0
    ) -> error::Result<RuntimeFunction> {
1045
0
        let offset =
1046
0
            utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
1047
0
                error::Error::Malformed(format!(
1048
0
                    "cannot map exception rva ({:#x}) into offset",
1049
0
                    rva
1050
0
                ))
1051
0
            })?;
1052
1053
0
        self.get_function_by_offset(offset)
1054
0
    }
1055
1056
    #[inline]
1057
0
    fn get_function_by_offset(&self, offset: usize) -> error::Result<RuntimeFunction> {
1058
0
        debug_assert!((offset - self.offset) % RUNTIME_FUNCTION_SIZE == 0);
1059
0
        debug_assert!(offset < self.offset + self.size);
1060
1061
0
        Ok(self.bytes.pread_with(offset, scroll::LE)?)
1062
0
    }
1063
1064
    #[inline]
1065
0
    fn get_function_by_offset_arm64(&self, offset: usize) -> error::Result<Arm64RuntimeFunction> {
1066
0
        debug_assert!((offset - self.offset) % size_of::<Arm64RuntimeFunction>() == 0);
1067
0
        debug_assert!(offset < self.offset + self.size);
1068
1069
0
        Ok(self.bytes.pread_with(offset, scroll::LE)?)
1070
0
    }
1071
}
1072
1073
impl fmt::Debug for ExceptionData<'_> {
1074
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1075
0
        f.debug_struct("ExceptionData")
1076
0
            .field("file_alignment", &self.file_alignment)
1077
0
            .field("offset", &format_args!("{:#x}", self.offset))
1078
0
            .field("size", &format_args!("{:#x}", self.size))
1079
0
            .field("len", &self.len())
1080
0
            .finish()
1081
0
    }
1082
}
1083
1084
impl<'a> IntoIterator for &'_ ExceptionData<'a> {
1085
    type Item = error::Result<RuntimeFunction>;
1086
    type IntoIter = RuntimeFunctionIterator<'a>;
1087
1088
    #[inline]
1089
0
    fn into_iter(self) -> Self::IntoIter {
1090
0
        self.functions()
1091
0
    }
1092
}
1093
1094
// Arm64RuntimeFunction::flag
1095
1096
/// `unwind_data` is an RVA to a full unwind record.
1097
pub const ARM64_PDATA_REF_TO_FULL_XDATA: u32 = 0;
1098
/// `unwind_data` uses the packed unwind data for a function.
1099
pub const ARM64_PDATA_PACKED_UNWIND_FUNCTION: u32 = 1;
1100
/// `unwind_data` uses the packed unwind data for a fragment.
1101
pub const ARM64_PDATA_PACKED_UNWIND_FRAGMENT: u32 = 2;
1102
1103
// Arm64RuntimeFunction::cr
1104
1105
/// Unchained function `<x29, lr>` pair is not saved on the stack.
1106
pub const ARM64_PDATA_CR_UNCHAINED: u32 = 0;
1107
/// Unchained function but `lr` is saved on the stack.
1108
pub const ARM64_PDATA_CR_UNCHAINED_SAVED_LR: u32 = 1;
1109
/// Chained function with PAC (Pointer Authentication Code).
1110
pub const ARM64_PDATA_CR_CHAINED_WITH_PAC: u32 = 2;
1111
/// Chained function.
1112
pub const ARM64_PDATA_CR_CHAINED: u32 = 3;
1113
1114
/// An ARM64 unwind entry for a range.
1115
#[repr(C)]
1116
#[derive(Copy, Clone, PartialEq, Default, Pread, Pwrite)]
1117
pub struct Arm64RuntimeFunction {
1118
    /// Function start RVA.
1119
    pub begin_address: u32,
1120
    /// Unwind data for this function entry.
1121
    pub unwind_data: u32,
1122
}
1123
1124
const _: () = assert!(size_of::<Arm64RuntimeFunction>() == 8);
1125
1126
impl Arm64RuntimeFunction {
1127
0
    const fn mask(bits: u32) -> u32 {
1128
0
        (1u32 << bits) - 1
1129
0
    }
1130
1131
0
    const fn bits(v: u32, shr: u32, bits: u32) -> u32 {
1132
0
        (v >> shr) & Self::mask(bits)
1133
0
    }
1134
1135
    /// If zero, unwind data is an RVA, packed into the runtime function entry otherwise.
1136
    ///
1137
    /// Must be one of:
1138
    /// - [ARM64_PDATA_REF_TO_FULL_XDATA]
1139
    /// - [ARM64_PDATA_PACKED_UNWIND_FUNCTION]
1140
    /// - [ARM64_PDATA_PACKED_UNWIND_FRAGMENT]
1141
    #[inline]
1142
0
    pub const fn flag(&self) -> u32 {
1143
0
        Self::bits(self.unwind_data, 0, 2)
1144
0
    }
1145
1146
    /// Returns the length of the function in bytes.
1147
    #[inline]
1148
0
    pub const fn function_length(&self) -> u32 {
1149
0
        Self::bits(self.unwind_data, 2, 11).wrapping_mul(4)
1150
0
    }
1151
1152
    /// Returns the frame size in bytes (bits [31:23], multiplied by 16).
1153
    #[inline]
1154
0
    pub const fn frame_size(&self) -> u32 {
1155
0
        Self::bits(self.unwind_data, 23, 9).wrapping_mul(16)
1156
0
    }
1157
1158
    /// Returns the CR (chain/return) field (bits [22:21]).
1159
    ///
1160
    /// Must be one of:
1161
    /// - [ARM64_PDATA_CR_UNCHAINED]
1162
    /// - [ARM64_PDATA_CR_UNCHAINED_SAVED_LR]
1163
    /// - [ARM64_PDATA_CR_CHAINED_WITH_PAC]
1164
    /// - [ARM64_PDATA_CR_CHAINED]
1165
    #[inline]
1166
0
    pub const fn cr(&self) -> u32 {
1167
0
        Self::bits(self.unwind_data, 21, 2)
1168
0
    }
1169
1170
    /// Returns `true` if the function homes integer parameter registers x0-x7 (bit [20]).
1171
    #[inline]
1172
0
    pub const fn h(&self) -> bool {
1173
0
        Self::bits(self.unwind_data, 20, 1) != 0
1174
0
    }
1175
1176
    /// Returns the number of saved non-volatile INT registers (x19-x28) (bits [19:16]).
1177
    #[inline]
1178
0
    pub const fn reg_i(&self) -> u32 {
1179
0
        Self::bits(self.unwind_data, 16, 4)
1180
0
    }
1181
1182
    /// Returns the number of saved non-volatile FP registers (d8-d15) (bits [15:13]).
1183
    ///
1184
    /// 0 means no FP registers saved. Values 1-7 mean `reg_f + 1` registers saved.
1185
    #[inline]
1186
0
    pub const fn reg_f(&self) -> u32 {
1187
0
        Self::bits(self.unwind_data, 13, 3)
1188
0
    }
1189
1190
    /// Returns `true` if this unwind data is packed, `false` otherwise.
1191
    #[inline]
1192
0
    pub const fn is_packed(&self) -> bool {
1193
0
        self.flag() != ARM64_PDATA_REF_TO_FULL_XDATA
1194
0
    }
1195
1196
    /// Returns an RVA of the full unwind data.
1197
    #[inline]
1198
0
    pub const fn unwind_data_rva(&self) -> u32 {
1199
0
        self.unwind_data & !Self::mask(2)
1200
0
    }
1201
}
1202
1203
impl fmt::Debug for Arm64RuntimeFunction {
1204
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1205
0
        f.debug_struct("Arm64RuntimeFunction")
1206
0
            .field("begin_address", &format_args!("{:#x}", self.begin_address))
1207
0
            .field("unwind_data", &format_args!("{:#x}", self.unwind_data))
1208
0
            .finish()
1209
0
    }
1210
}
1211
1212
/// Iterator over [Arm64RuntimeFunction] ARM64 runtime function entries.
1213
#[derive(Debug)]
1214
pub struct Arm64RuntimeFunctionIterator<'a> {
1215
    data: &'a [u8],
1216
}
1217
1218
impl Iterator for Arm64RuntimeFunctionIterator<'_> {
1219
    type Item = error::Result<Arm64RuntimeFunction>;
1220
1221
0
    fn next(&mut self) -> Option<Self::Item> {
1222
0
        if self.data.is_empty() {
1223
0
            return None;
1224
0
        }
1225
1226
0
        Some(match self.data.pread_with(0, scroll::LE) {
1227
0
            Ok(func) => {
1228
0
                self.data = &self.data[size_of::<Arm64RuntimeFunction>()..];
1229
0
                Ok(func)
1230
            }
1231
0
            Err(error) => {
1232
0
                self.data = &[];
1233
0
                Err(error.into())
1234
            }
1235
        })
1236
0
    }
1237
1238
0
    fn size_hint(&self) -> (usize, Option<usize>) {
1239
0
        let len = self.data.len() / size_of::<Arm64RuntimeFunction>();
1240
0
        (len, Some(len))
1241
0
    }
1242
}
1243
1244
impl FusedIterator for Arm64RuntimeFunctionIterator<'_> {}
1245
impl ExactSizeIterator for Arm64RuntimeFunctionIterator<'_> {}
1246
1247
/// ARM64 unwind info header.
1248
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1249
pub struct Arm64UnwindHeader(pub u32);
1250
1251
const _: () = assert!(size_of::<Arm64UnwindHeader>() == 4);
1252
1253
impl Arm64UnwindHeader {
1254
0
    const fn mask(bits: u32) -> u32 {
1255
0
        (1u32 << bits) - 1
1256
0
    }
1257
1258
0
    const fn bits(v: u32, shr: u32, bits: u32) -> u32 {
1259
0
        (v >> shr) & Self::mask(bits)
1260
0
    }
1261
1262
    /// Returns a function length in bytes.
1263
    #[inline]
1264
0
    pub const fn function_length(&self) -> u32 {
1265
0
        Self::bits(self.0, 0, 18).wrapping_mul(4)
1266
0
    }
1267
1268
    /// Returns an unwind info version.
1269
    #[inline]
1270
0
    pub const fn version(&self) -> u32 {
1271
0
        Self::bits(self.0, 18, 2)
1272
0
    }
1273
1274
    /// Returns `true` if the unwind info has exception handler.
1275
    #[inline]
1276
0
    pub const fn exception_data_present(&self) -> bool {
1277
0
        Self::bits(self.0, 20, 1) != 0
1278
0
    }
1279
1280
    /// Returns `true` if an epilog is in the header.
1281
    #[inline]
1282
0
    pub const fn epilog_in_header(&self) -> bool {
1283
0
        Self::bits(self.0, 21, 1) != 0
1284
0
    }
1285
1286
    /// Returns bits indicates either an number of epilog scope or
1287
    /// the index of the first unwind code that describes the epilog.
1288
    #[inline]
1289
0
    pub const fn epilog_count(&self) -> u32 {
1290
0
        Self::bits(self.0, 22, 5)
1291
0
    }
1292
1293
    /// Returns the number of u32's with unwind codes.
1294
    #[inline]
1295
0
    pub const fn code_words(&self) -> u32 {
1296
0
        Self::bits(self.0, 27, 5)
1297
0
    }
1298
1299
    /// Returns `true` if "extension" bytes are in addition.
1300
    #[inline]
1301
0
    pub const fn has_extension(&self) -> bool {
1302
0
        self.epilog_count() == 0 && self.code_words() == 0
1303
0
    }
1304
}
1305
1306
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1307
pub struct Arm64UnwindExtension(pub u32);
1308
1309
const _: () = assert!(size_of::<Arm64UnwindExtension>() == 4);
1310
1311
impl Arm64UnwindExtension {
1312
    /// Returns the number of epilog.
1313
    #[inline]
1314
0
    pub const fn epilog_count(&self) -> u32 {
1315
0
        self.0 & 0xFFFF
1316
0
    }
1317
1318
    /// Returns the number of u32's with unwind codes.
1319
    #[inline]
1320
0
    pub const fn code_words(&self) -> u32 {
1321
0
        (self.0 >> 16) & 0xFF
1322
0
    }
1323
}
1324
1325
/// ARM64 epilog scope info.
1326
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1327
pub struct Arm64EpilogScope(pub u32);
1328
1329
const _: () = assert!(size_of::<Arm64EpilogScope>() == 4);
1330
1331
impl Arm64EpilogScope {
1332
0
    const fn mask(bits: u32) -> u32 {
1333
0
        (1u32 << bits) - 1
1334
0
    }
1335
1336
0
    const fn bits(v: u32, shr: u32, bits: u32) -> u32 {
1337
0
        (v >> shr) & Self::mask(bits)
1338
0
    }
1339
1340
    /// Returns an epulog start offset in bytes.
1341
    #[inline]
1342
0
    pub const fn start_offset_words(&self) -> u32 {
1343
0
        Self::bits(self.0, 0, 18).wrapping_mul(4)
1344
0
    }
1345
1346
    /// Returns the bits indicate condition.
1347
    #[inline]
1348
0
    pub const fn condition(&self) -> u32 {
1349
0
        Self::bits(self.0, 18, 4)
1350
0
    }
1351
1352
    /// Returns the index of the unwind code.
1353
    #[inline]
1354
0
    pub const fn start_index(&self) -> u32 {
1355
0
        Self::bits(self.0, 22, 10)
1356
0
    }
1357
}
1358
1359
/// Iterator over epilog scopes.
1360
#[derive(Debug)]
1361
pub struct Arm64EpilogScopeIterator<'a> {
1362
    bytes: &'a [u8],
1363
    offset: usize,
1364
}
1365
1366
impl Iterator for Arm64EpilogScopeIterator<'_> {
1367
    type Item = error::Result<Arm64EpilogScope>;
1368
1369
0
    fn next(&mut self) -> Option<Self::Item> {
1370
0
        if self.offset >= self.bytes.len() {
1371
0
            return None;
1372
0
        }
1373
0
        Some(
1374
0
            self.bytes
1375
0
                .gread_with::<u32>(&mut self.offset, scroll::LE)
1376
0
                .map(Arm64EpilogScope)
1377
0
                .map_err(Into::into),
1378
0
        )
1379
0
    }
1380
1381
0
    fn size_hint(&self) -> (usize, Option<usize>) {
1382
0
        let len = (self.bytes.len().saturating_sub(self.offset)) / size_of_val(&0u32);
1383
0
        (len, Some(len))
1384
0
    }
1385
}
1386
1387
impl FusedIterator for Arm64EpilogScopeIterator<'_> {}
1388
impl ExactSizeIterator for Arm64EpilogScopeIterator<'_> {}
1389
1390
/// ARM64 exception handler.
1391
#[derive(Clone, Debug, Eq, PartialEq)]
1392
pub struct Arm64ExceptionHandler<'a> {
1393
    /// RVA of the exception handler.
1394
    pub rva: u32,
1395
    /// Handler-specific data.
1396
    pub data: &'a [u8],
1397
}
1398
1399
/// ARM64 unwind info.
1400
#[derive(Clone, Debug)]
1401
pub struct Arm64UnwindInfo<'a> {
1402
    /// Unwind info header.
1403
    pub header: Arm64UnwindHeader,
1404
    /// Extension info if present.
1405
    pub extension: Option<Arm64UnwindExtension>,
1406
    /// Exception handler and bytes of handler specific data if present.
1407
    pub exception_handler: Option<Arm64ExceptionHandler<'a>>,
1408
    /// Bytes of epilog scope.
1409
    epilog_scope_bytes: &'a [u8],
1410
    /// Bytes of unwind codes.
1411
    unwind_code_bytes: &'a [u8],
1412
}
1413
1414
impl<'a> Arm64UnwindInfo<'a> {
1415
0
    fn checked_mul4(words: u32) -> error::Result<usize> {
1416
0
        let bytes = words
1417
0
            .checked_mul(4)
1418
0
            .ok_or_else(|| error::Error::Malformed("arm64 unwind info size overflow".into()))?;
1419
0
        usize::try_from(bytes)
1420
0
            .map_err(|_| error::Error::Malformed("arm64 unwind info size overflow".into()))
1421
0
    }
1422
1423
0
    fn checked_add(a: usize, b: usize) -> error::Result<usize> {
1424
0
        a.checked_add(b)
1425
0
            .ok_or_else(|| error::Error::Malformed("arm64 unwind info size overflow".into()))
1426
0
    }
1427
1428
    /// Precomputes the size of this xdata. This does _not_ count for handler-specific bytes after handler RVA.
1429
    /// <https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#xdata-records>
1430
0
    pub fn size(bytes: &[u8]) -> error::Result<usize> {
1431
0
        let w0 = bytes.pread_with::<u32>(0, scroll::LE)?;
1432
1433
0
        let (mut size_u32, epilog_scopes, unwind_words) = if (w0 >> 22) != 0 {
1434
0
            let size = 4u32;
1435
0
            let epilog_scopes = (w0 >> 22) & 0x1f;
1436
0
            let unwind_words = (w0 >> 27) & 0x1f;
1437
0
            (size, epilog_scopes, unwind_words)
1438
        } else {
1439
0
            let w1 = bytes.pread_with::<u32>(4, scroll::LE)?;
1440
0
            let size = 8u32;
1441
0
            let epilog_scopes = w1 & 0xffff;
1442
0
            let unwind_words = (w1 >> 16) & 0xff;
1443
0
            (size, epilog_scopes, unwind_words)
1444
        };
1445
1446
0
        let epilog_in_header = (w0 & (1u32 << 21)) != 0;
1447
0
        if !epilog_in_header {
1448
0
            size_u32 = Self::checked_add(size_u32 as _, Self::checked_mul4(epilog_scopes)?)? as _;
1449
0
        }
1450
1451
0
        size_u32 = Self::checked_add(size_u32 as _, Self::checked_mul4(unwind_words)?)? as _;
1452
1453
0
        let exception_data_present = (w0 & (1u32 << 20)) != 0;
1454
0
        if exception_data_present {
1455
0
            size_u32 = Self::checked_add(size_u32 as _, 4)? as _;
1456
0
        }
1457
1458
0
        Ok(size_u32 as usize)
1459
0
    }
1460
1461
    /// Parses a full ARM64 unwind xdata.
1462
0
    pub fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
1463
0
        let header_word = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
1464
0
        let header = Arm64UnwindHeader(header_word);
1465
1466
0
        if header.version() != 0 {
1467
0
            return Err(error::Error::Malformed(format!(
1468
0
                "unsupported ARM64 .xdata version ({})",
1469
0
                header.version()
1470
0
            )));
1471
0
        }
1472
1473
0
        let extension = if header.has_extension() {
1474
0
            let ext_word = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
1475
0
            Some(Arm64UnwindExtension(ext_word))
1476
        } else {
1477
0
            None
1478
        };
1479
1480
0
        let epilog_scopes = match (header.epilog_in_header(), extension) {
1481
0
            (false, Some(ext)) => ext.epilog_count(),
1482
0
            (false, None) => header.epilog_count(),
1483
0
            (true, _) => 0,
1484
        };
1485
1486
0
        let unwind_words = if let Some(ext) = extension {
1487
0
            ext.code_words()
1488
        } else {
1489
0
            header.code_words()
1490
        };
1491
1492
0
        let scope_bytes_len = Self::checked_mul4(epilog_scopes)?;
1493
0
        let epilog_scope_bytes = bytes.gread_with(&mut offset, scope_bytes_len)?;
1494
1495
0
        let unwind_bytes_len = Self::checked_mul4(unwind_words)?;
1496
0
        let unwind_code_bytes = bytes.gread_with(&mut offset, unwind_bytes_len)?;
1497
1498
0
        let exception_handler = if header.exception_data_present() {
1499
0
            let handler_rva = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
1500
0
            let data = bytes.get(offset..).unwrap_or(&[]);
1501
0
            Some(Arm64ExceptionHandler {
1502
0
                rva: handler_rva,
1503
0
                data,
1504
0
            })
1505
        } else {
1506
0
            None
1507
        };
1508
1509
0
        Ok(Self {
1510
0
            header,
1511
0
            extension,
1512
0
            epilog_scope_bytes,
1513
0
            unwind_code_bytes,
1514
0
            exception_handler,
1515
0
        })
1516
0
    }
1517
1518
    /// Returns an iterator over epilog scope words.
1519
0
    pub fn epilog_scopes(&self) -> Option<Arm64EpilogScopeIterator<'a>> {
1520
0
        if self.header.epilog_in_header() || self.epilog_scope_bytes.is_empty() {
1521
0
            return None;
1522
0
        }
1523
0
        Some(Arm64EpilogScopeIterator {
1524
0
            bytes: self.epilog_scope_bytes,
1525
0
            offset: 0,
1526
0
        })
1527
0
    }
1528
1529
    /// Returns an iterator over unwind codes.
1530
0
    pub fn unwind_codes(&self, start_index: u16) -> error::Result<Arm64UnwindCodeIterator<'a>> {
1531
0
        Arm64UnwindCodeIterator::new(self.unwind_code_bytes, start_index as usize)
1532
0
    }
1533
}
1534
1535
/// Custom stack cases reserved for specific Microsoft unwind opcodes.
1536
///
1537
/// For more info: <https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#unwind-codes>
1538
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1539
pub enum Arm64CustomStackCase {
1540
    TrapFrame,
1541
    MachineFrame,
1542
    Context,
1543
    EcContext,
1544
    ClearUnwoundToCall,
1545
}
1546
1547
/// ARM64 unwind codes.
1548
#[non_exhaustive]
1549
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1550
pub enum Arm64UnwindCode {
1551
    AllocSmall {
1552
        size_bytes: u32,
1553
    },
1554
    SaveR19R20Preindexed {
1555
        offset_bytes: u32,
1556
    },
1557
    SaveFpLr {
1558
        offset_bytes: u32,
1559
    },
1560
    SaveFpLrPreindexed {
1561
        offset_bytes: u32,
1562
    },
1563
    AllocMedium {
1564
        size_bytes: u32,
1565
    },
1566
    SaveRegPair {
1567
        first_reg: u8,
1568
        offset_bytes: u32,
1569
    },
1570
    SaveRegPairPreindexed {
1571
        first_reg: u8,
1572
        offset_bytes: u32,
1573
    },
1574
    SaveReg {
1575
        reg: u8,
1576
        offset_bytes: u32,
1577
    },
1578
    SaveRegPreindexed {
1579
        reg: u8,
1580
        offset_bytes: u32,
1581
    },
1582
    SaveLrPair {
1583
        first_reg: u8,
1584
        offset_bytes: u32,
1585
    },
1586
    SaveFRegPair {
1587
        first_reg: u8,
1588
        offset_bytes: u32,
1589
    },
1590
    SaveFRegPairPreindexed {
1591
        first_reg: u8,
1592
        offset_bytes: u32,
1593
    },
1594
    SaveFReg {
1595
        reg: u8,
1596
        offset_bytes: u32,
1597
    },
1598
    SaveFRegPreindexed {
1599
        reg: u8,
1600
        offset_bytes: u32,
1601
    },
1602
1603
    AllocZ {
1604
        count: u8,
1605
    },
1606
1607
    AllocLarge {
1608
        size_bytes: u32,
1609
    },
1610
    SetFp,
1611
    AddFp {
1612
        imm8: u8,
1613
        offset_bytes: u32,
1614
    },
1615
    Nop,
1616
    End,
1617
    EndC,
1618
    SaveNext,
1619
1620
    SaveAnyReg {
1621
        kind: u8,
1622
        pair: bool,
1623
        preindexed: bool,
1624
        reg: u8,
1625
        offset_bytes: u32,
1626
    },
1627
1628
    SaveZReg {
1629
        r: u8,
1630
        o: u16,
1631
    },
1632
1633
    SavePReg {
1634
        r: u8,
1635
        o: u16,
1636
    },
1637
1638
    CustomStack(Arm64CustomStackCase),
1639
1640
    PacSignLr,
1641
1642
    Reserved {
1643
        opcode: u8,
1644
        data: u32,
1645
        data_len: u8,
1646
    },
1647
}
1648
1649
/// Intermediate data to store offset and length of the unwind code.
1650
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1651
pub struct Arm64UnwindCodeData {
1652
    /// Offset of this unwind code.
1653
    pub offset: u16,
1654
    /// The unwind code.
1655
    pub code: Arm64UnwindCode,
1656
    /// Length of this unwind code in bytes.
1657
    pub len: u8,
1658
}
1659
1660
/// Iterator over ARM64 unwind codes.
1661
pub struct Arm64UnwindCodeIterator<'a> {
1662
    bytes: &'a [u8],
1663
    offset: usize,
1664
}
1665
1666
impl<'a> Arm64UnwindCodeIterator<'a> {
1667
    /// Create a new iterator starting at `start` (byte index in the MSB-first unwind-byte stream).
1668
0
    pub fn new(raw_unwind_bytes: &'a [u8], start: usize) -> error::Result<Self> {
1669
0
        if raw_unwind_bytes.len() % 4 != 0 {
1670
0
            return Err(error::Error::Malformed("length not a multiple of 4".into()));
1671
0
        }
1672
0
        if start > raw_unwind_bytes.len() {
1673
0
            return Err(error::Error::Malformed("start index out of range".into()));
1674
0
        }
1675
1676
0
        Ok(Self {
1677
0
            bytes: raw_unwind_bytes,
1678
0
            offset: start,
1679
0
        })
1680
0
    }
1681
}
1682
1683
impl<'a> Iterator for Arm64UnwindCodeIterator<'a> {
1684
    type Item = error::Result<Arm64UnwindCodeData>;
1685
1686
0
    fn next(&mut self) -> Option<Self::Item> {
1687
0
        if self.offset >= self.bytes.len() {
1688
0
            return None;
1689
0
        }
1690
1691
        macro_rules! fail {
1692
            ($msg:expr) => {{
1693
                self.offset = self.bytes.len();
1694
                return Some(Err($crate::error::Error::Malformed(($msg).into())));
1695
            }};
1696
            ($fmt:literal $(, $args:expr)* $(,)?) => {{
1697
                self.offset = self.bytes.len();
1698
                return Some(Err($crate::error::Error::Malformed(format!($fmt $(, $args)*))));
1699
            }};
1700
        }
1701
1702
        macro_rules! try_read_u8 {
1703
            ($at:expr) => {{
1704
                match self.bytes.pread::<u8>($at) {
1705
                    Ok(v) => v,
1706
                    Err(err) => fail!("{}", err),
1707
                }
1708
            }};
1709
            ($at:expr, $msg:expr) => {{
1710
                match self.bytes.pread::<u8>($at) {
1711
                    Ok(v) => v,
1712
                    Err(_) => fail!($msg),
1713
                }
1714
            }};
1715
            ($at:expr, $fmt:literal $(, $args:expr)* $(,)?) => {{
1716
                match self.bytes.pread::<u8>($at) {
1717
                    Ok(v) => v,
1718
                    Err(_) => fail!($fmt $(, $args)*),
1719
                }
1720
            }};
1721
        }
1722
1723
        // https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#unwind-codes
1724
1725
0
        let start = self.offset;
1726
0
        let b0 = try_read_u8!(start);
1727
1728
0
        let (code, len) = match b0 {
1729
0
            0x00..=0x1f => {
1730
0
                let x = (b0 & 0x1f) as u32;
1731
0
                (
1732
0
                    Arm64UnwindCode::AllocSmall {
1733
0
                        size_bytes: x.wrapping_mul(16),
1734
0
                    },
1735
0
                    1,
1736
0
                )
1737
            }
1738
1739
0
            0x20..=0x3f => {
1740
0
                let z = (b0 & 0x1f) as u32;
1741
0
                (
1742
0
                    Arm64UnwindCode::SaveR19R20Preindexed {
1743
0
                        offset_bytes: z.wrapping_mul(8),
1744
0
                    },
1745
0
                    1,
1746
0
                )
1747
            }
1748
1749
0
            0x40..=0x7f => {
1750
0
                let z = (b0 & 0x3f) as u32;
1751
0
                (
1752
0
                    Arm64UnwindCode::SaveFpLr {
1753
0
                        offset_bytes: z.wrapping_mul(8),
1754
0
                    },
1755
0
                    1,
1756
0
                )
1757
            }
1758
1759
0
            0x80..=0xbf => {
1760
0
                let z = (b0 & 0x3f) as u32;
1761
0
                (
1762
0
                    Arm64UnwindCode::SaveFpLrPreindexed {
1763
0
                        offset_bytes: (z.wrapping_add(1)).wrapping_mul(8),
1764
0
                    },
1765
0
                    1,
1766
0
                )
1767
            }
1768
1769
0
            0xc0..=0xc7 => {
1770
0
                let r = try_read_u8!(start + 1, "alloc_m is truncated");
1771
0
                let x = (((b0 & 0x07) as u32) << 8) | (r as u32);
1772
0
                (
1773
0
                    Arm64UnwindCode::AllocMedium {
1774
0
                        size_bytes: x.wrapping_mul(16),
1775
0
                    },
1776
0
                    2,
1777
0
                )
1778
            }
1779
1780
0
            0xc8..=0xcb => {
1781
0
                let b1 = try_read_u8!(start + 1, "save_regp is truncated");
1782
0
                let x = (((b0 & 0x03) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1783
0
                let z = (b1 & 0x3f) as u32;
1784
0
                let first_reg = 19u8.wrapping_add(x as u8);
1785
0
                (
1786
0
                    Arm64UnwindCode::SaveRegPair {
1787
0
                        first_reg,
1788
0
                        offset_bytes: z.wrapping_mul(8),
1789
0
                    },
1790
0
                    2,
1791
0
                )
1792
            }
1793
1794
0
            0xcc..=0xcf => {
1795
0
                let b1 = try_read_u8!(start + 1, "save_regp_x is truncated");
1796
0
                let x = (((b0 & 0x03) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1797
0
                let z = (b1 & 0x3f) as u32;
1798
0
                let first_reg = 19u8.wrapping_add(x as u8);
1799
0
                (
1800
0
                    Arm64UnwindCode::SaveRegPairPreindexed {
1801
0
                        first_reg,
1802
0
                        offset_bytes: (z.wrapping_add(1)).wrapping_mul(8),
1803
0
                    },
1804
0
                    2,
1805
0
                )
1806
            }
1807
1808
0
            0xd0..=0xd3 => {
1809
0
                let b1 = try_read_u8!(start + 1, "save_reg is truncated");
1810
0
                let x = (((b0 & 0x03) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1811
0
                let z = (b1 & 0x3f) as u32;
1812
0
                let reg = 19u8.wrapping_add(x as u8);
1813
0
                (
1814
0
                    Arm64UnwindCode::SaveReg {
1815
0
                        reg,
1816
0
                        offset_bytes: z.wrapping_mul(8),
1817
0
                    },
1818
0
                    2,
1819
0
                )
1820
            }
1821
1822
0
            0xd4..=0xd5 => {
1823
0
                let b1 = try_read_u8!(start + 1, "save_reg_x is truncated");
1824
0
                let x = (((b0 & 0x01) as u32) << 3) | (((b1 >> 5) & 0x07) as u32);
1825
0
                let z = (b1 & 0x1f) as u32;
1826
0
                let reg = 19u8.wrapping_add(x as u8);
1827
0
                (
1828
0
                    Arm64UnwindCode::SaveRegPreindexed {
1829
0
                        reg,
1830
0
                        offset_bytes: (z.wrapping_add(1)).wrapping_mul(8),
1831
0
                    },
1832
0
                    2,
1833
0
                )
1834
            }
1835
1836
0
            0xd6..=0xd7 => {
1837
0
                let b1 = try_read_u8!(start + 1, "save_lrpair is truncated");
1838
0
                let x = (((b0 & 0x01) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1839
0
                let z = (b1 & 0x3f) as u32;
1840
0
                let first_reg = 19u8.wrapping_add((2 * x) as u8);
1841
0
                (
1842
0
                    Arm64UnwindCode::SaveLrPair {
1843
0
                        first_reg,
1844
0
                        offset_bytes: z.wrapping_mul(8),
1845
0
                    },
1846
0
                    2,
1847
0
                )
1848
            }
1849
1850
0
            0xd8..=0xd9 => {
1851
0
                let b1 = try_read_u8!(start + 1, "save_fregp is truncated");
1852
0
                let x = (((b0 & 0x01) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1853
0
                let z = (b1 & 0x3f) as u32;
1854
0
                let first_reg = 8u8.wrapping_add(x as u8);
1855
0
                (
1856
0
                    Arm64UnwindCode::SaveFRegPair {
1857
0
                        first_reg,
1858
0
                        offset_bytes: z.wrapping_mul(8),
1859
0
                    },
1860
0
                    2,
1861
0
                )
1862
            }
1863
1864
0
            0xda..=0xdb => {
1865
0
                let b1 = try_read_u8!(start + 1, "save_fregp_x is truncated");
1866
0
                let x = (((b0 & 0x01) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1867
0
                let z = (b1 & 0x3f) as u32;
1868
0
                let first_reg = 8u8.wrapping_add(x as u8);
1869
0
                (
1870
0
                    Arm64UnwindCode::SaveFRegPairPreindexed {
1871
0
                        first_reg,
1872
0
                        offset_bytes: (z.wrapping_add(1)).wrapping_mul(8),
1873
0
                    },
1874
0
                    2,
1875
0
                )
1876
            }
1877
1878
0
            0xdc..=0xdd => {
1879
0
                let b1 = try_read_u8!(start + 1, "save_freg is truncated");
1880
0
                let x = (((b0 & 0x01) as u32) << 2) | (((b1 >> 6) & 0x03) as u32);
1881
0
                let z = (b1 & 0x3f) as u32;
1882
0
                let reg = 8u8.wrapping_add(x as u8);
1883
0
                (
1884
0
                    Arm64UnwindCode::SaveFReg {
1885
0
                        reg,
1886
0
                        offset_bytes: z.wrapping_mul(8),
1887
0
                    },
1888
0
                    2,
1889
0
                )
1890
            }
1891
1892
            0xde => {
1893
0
                let b1 = try_read_u8!(start + 1, "save_freg_x is truncated");
1894
0
                let x = ((b1 >> 5) & 0x07) as u32;
1895
0
                let z = (b1 & 0x1f) as u32;
1896
0
                let reg = 8u8.wrapping_add(x as u8);
1897
0
                (
1898
0
                    Arm64UnwindCode::SaveFRegPreindexed {
1899
0
                        reg,
1900
0
                        offset_bytes: (z.wrapping_add(1)).wrapping_mul(8),
1901
0
                    },
1902
0
                    2,
1903
0
                )
1904
            }
1905
1906
            0xdf => {
1907
0
                let z = try_read_u8!(start + 1, "alloc_z is truncated");
1908
0
                (Arm64UnwindCode::AllocZ { count: z }, 2)
1909
            }
1910
1911
            0xe0 => {
1912
0
                let imm = {
1913
0
                    let b0 = try_read_u8!(start + 1) as u32;
1914
0
                    let b1 = try_read_u8!(start + 2) as u32;
1915
0
                    let b2 = try_read_u8!(start + 3) as u32;
1916
1917
0
                    (b0 << 16) | (b1 << 8) | b2
1918
                };
1919
0
                (
1920
0
                    Arm64UnwindCode::AllocLarge {
1921
0
                        size_bytes: imm.wrapping_mul(16),
1922
0
                    },
1923
0
                    4,
1924
0
                )
1925
            }
1926
1927
0
            0xe1 => (Arm64UnwindCode::SetFp, 1),
1928
1929
            0xe2 => {
1930
0
                let imm8 = try_read_u8!(start + 1, "add_fp is truncated");
1931
0
                (
1932
0
                    Arm64UnwindCode::AddFp {
1933
0
                        imm8,
1934
0
                        offset_bytes: (imm8 as u32).wrapping_mul(8),
1935
0
                    },
1936
0
                    2,
1937
0
                )
1938
            }
1939
1940
0
            0xe3 => (Arm64UnwindCode::Nop, 1),
1941
0
            0xe4 => (Arm64UnwindCode::End, 1),
1942
0
            0xe5 => (Arm64UnwindCode::EndC, 1),
1943
0
            0xe6 => (Arm64UnwindCode::SaveNext, 1),
1944
1945
            0xe7 => {
1946
0
                let b1 = try_read_u8!(start + 1);
1947
1948
0
                if (b1 & 0x80) != 0 {
1949
0
                    let data = b1 as u32;
1950
0
                    (
1951
0
                        Arm64UnwindCode::Reserved {
1952
0
                            opcode: 0xe7,
1953
0
                            data,
1954
0
                            data_len: 1,
1955
0
                        },
1956
0
                        2,
1957
0
                    )
1958
                } else {
1959
0
                    let b2 = try_read_u8!(start + 2, "save_any_? is truncated");
1960
1961
0
                    let cls = (b2 >> 6) & 0x03;
1962
0
                    if cls != 3 {
1963
0
                        let p = ((b1 >> 6) & 1) != 0;
1964
0
                        let x = ((b1 >> 5) & 1) != 0;
1965
0
                        let r = (b1 & 0x1f) as u8;
1966
0
                        let o = (b2 & 0x3f) as u32;
1967
1968
0
                        let offset_bytes = match (cls, x || p) {
1969
0
                            (0 | 1, true) => match o.checked_mul(16) {
1970
0
                                Some(v) => v,
1971
0
                                None => fail!("save_any_reg offset overflow"),
1972
                            },
1973
0
                            (0 | 1, false) => match o.checked_mul(8) {
1974
0
                                Some(v) => v,
1975
0
                                None => fail!("save_any_reg offset overflow"),
1976
                            },
1977
0
                            (2, _) => match o.checked_mul(16) {
1978
0
                                Some(v) => v,
1979
0
                                None => fail!("save_any_reg offset overflow"),
1980
                            },
1981
0
                            _ => 0,
1982
                        };
1983
1984
0
                        let kind = cls;
1985
0
                        (
1986
0
                            Arm64UnwindCode::SaveAnyReg {
1987
0
                                kind,
1988
0
                                pair: p,
1989
0
                                preindexed: x,
1990
0
                                reg: r,
1991
0
                                offset_bytes,
1992
0
                            },
1993
0
                            3,
1994
0
                        )
1995
                    } else {
1996
0
                        let hi = ((b1 >> 5) & 0x03) as u16;
1997
0
                        let r = (b1 & 0x0f) as u8;
1998
0
                        let lo = (b2 & 0x3f) as u16;
1999
0
                        let o = (hi << 6) | lo;
2000
2001
0
                        if (b1 & 0x10) != 0 {
2002
0
                            (Arm64UnwindCode::SavePReg { r, o }, 3)
2003
                        } else {
2004
0
                            (Arm64UnwindCode::SaveZReg { r, o }, 3)
2005
                        }
2006
                    }
2007
                }
2008
            }
2009
2010
0
            0xe8 => (
2011
0
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::TrapFrame),
2012
0
                1,
2013
0
            ),
2014
0
            0xe9 => (
2015
0
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::MachineFrame),
2016
0
                1,
2017
0
            ),
2018
0
            0xea => (
2019
0
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::Context),
2020
0
                1,
2021
0
            ),
2022
0
            0xeb => (
2023
0
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::EcContext),
2024
0
                1,
2025
0
            ),
2026
0
            0xec => (
2027
0
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::ClearUnwoundToCall),
2028
0
                1,
2029
0
            ),
2030
2031
0
            0xfc => (Arm64UnwindCode::PacSignLr, 1),
2032
2033
            0xf8 => {
2034
0
                let b1 = try_read_u8!(start + 1);
2035
0
                (
2036
0
                    Arm64UnwindCode::Reserved {
2037
0
                        opcode: 0xf8,
2038
0
                        data: b1 as u32,
2039
0
                        data_len: 1,
2040
0
                    },
2041
0
                    2,
2042
0
                )
2043
            }
2044
            0xf9 => {
2045
0
                let b1 = try_read_u8!(start + 1);
2046
0
                let b2 = try_read_u8!(start + 2);
2047
0
                let data = ((b1 as u32) << 8) | (b2 as u32);
2048
0
                (
2049
0
                    Arm64UnwindCode::Reserved {
2050
0
                        opcode: 0xf9,
2051
0
                        data,
2052
0
                        data_len: 2,
2053
0
                    },
2054
0
                    3,
2055
0
                )
2056
            }
2057
            0xfa => {
2058
0
                let b1 = try_read_u8!(start + 1);
2059
0
                let b2 = try_read_u8!(start + 2);
2060
0
                let b3 = try_read_u8!(start + 3);
2061
0
                let data = ((b1 as u32) << 16) | ((b2 as u32) << 8) | (b3 as u32);
2062
0
                (
2063
0
                    Arm64UnwindCode::Reserved {
2064
0
                        opcode: 0xfa,
2065
0
                        data,
2066
0
                        data_len: 3,
2067
0
                    },
2068
0
                    4,
2069
0
                )
2070
            }
2071
            0xfb => {
2072
0
                let b1 = try_read_u8!(start + 1);
2073
0
                let b2 = try_read_u8!(start + 2);
2074
0
                let b3 = try_read_u8!(start + 3);
2075
0
                let b4 = try_read_u8!(start + 4);
2076
0
                let data =
2077
0
                    ((b1 as u32) << 24) | ((b2 as u32) << 16) | ((b3 as u32) << 8) | (b4 as u32);
2078
0
                (
2079
0
                    Arm64UnwindCode::Reserved {
2080
0
                        opcode: 0xfb,
2081
0
                        data,
2082
0
                        data_len: 4,
2083
0
                    },
2084
0
                    5,
2085
0
                )
2086
            }
2087
2088
0
            other => (
2089
0
                Arm64UnwindCode::Reserved {
2090
0
                    opcode: other,
2091
0
                    data: 0,
2092
0
                    data_len: 0,
2093
0
                },
2094
0
                1,
2095
0
            ),
2096
        };
2097
2098
0
        self.offset += len;
2099
2100
0
        Some(Ok(Arm64UnwindCodeData {
2101
0
            offset: start as u16,
2102
0
            code,
2103
0
            len: len as u8,
2104
0
        }))
2105
0
    }
2106
}
2107
2108
#[cfg(test)]
2109
mod tests {
2110
    use super::*;
2111
2112
    #[test]
2113
    fn test_size_of_runtime_function() {
2114
        assert_eq!(
2115
            std::mem::size_of::<RuntimeFunction>(),
2116
            RUNTIME_FUNCTION_SIZE
2117
        );
2118
    }
2119
2120
    // Tests disabled until there is a solution for handling binary test data
2121
    // See https://github.com/m4b/goblin/issues/185
2122
2123
    // macro_rules! microsoft_symbol {
2124
    //     ($name:literal, $id:literal) => {{
2125
    //         use std::fs::File;
2126
    //         use std::path::Path;
2127
2128
    //         let path = Path::new(concat!("cache/", $name));
2129
    //         if !path.exists() {
2130
    //             let url = format!(
2131
    //                 "https://msdl.microsoft.com/download/symbols/{}/{}/{}",
2132
    //                 $name, $id, $name
2133
    //             );
2134
2135
    //             let mut response = reqwest::get(&url).expect(concat!("get ", $name));
2136
    //             let mut target = File::create(path).expect(concat!("create ", $name));
2137
    //             response
2138
    //                 .copy_to(&mut target)
2139
    //                 .expect(concat!("download ", $name));
2140
    //         }
2141
2142
    //         std::fs::read(path).expect(concat!("open ", $name))
2143
    //     }};
2144
    // }
2145
2146
    // lazy_static::lazy_static! {
2147
    //     static ref PE_DATA: Vec<u8> = microsoft_symbol!("WSHTCPIP.DLL", "4a5be0b77000");
2148
    // }
2149
2150
    // #[test]
2151
    // fn test_parse() {
2152
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2153
    //     let exception_data = pe.exception_data.expect("get exception data");
2154
2155
    //     assert_eq!(exception_data.len(), 19);
2156
    //     assert!(!exception_data.is_empty());
2157
    // }
2158
2159
    // #[test]
2160
    // fn test_iter_functions() {
2161
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2162
    //     let exception_data = pe.exception_data.expect("get exception data");
2163
2164
    //     let functions: Vec<RuntimeFunction> = exception_data
2165
    //         .functions()
2166
    //         .map(|result| result.expect("parse runtime function"))
2167
    //         .collect();
2168
2169
    //     assert_eq!(functions.len(), 19);
2170
2171
    //     let expected = RuntimeFunction {
2172
    //         begin_address: 0x1355,
2173
    //         end_address: 0x1420,
2174
    //         unwind_info_address: 0x4019,
2175
    //     };
2176
2177
    //     assert_eq!(functions[4], expected);
2178
    // }
2179
2180
    // #[test]
2181
    // fn test_get_function() {
2182
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2183
    //     let exception_data = pe.exception_data.expect("get exception data");
2184
2185
    //     let expected = RuntimeFunction {
2186
    //         begin_address: 0x1355,
2187
    //         end_address: 0x1420,
2188
    //         unwind_info_address: 0x4019,
2189
    //     };
2190
2191
    //     assert_eq!(
2192
    //         exception_data.get_function(4).expect("find function"),
2193
    //         expected
2194
    //     );
2195
    // }
2196
2197
    // #[test]
2198
    // fn test_find_function() {
2199
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2200
    //     let exception_data = pe.exception_data.expect("get exception data");
2201
2202
    //     let expected = RuntimeFunction {
2203
    //         begin_address: 0x1355,
2204
    //         end_address: 0x1420,
2205
    //         unwind_info_address: 0x4019,
2206
    //     };
2207
2208
    //     assert_eq!(
2209
    //         exception_data.find_function(0x1400).expect("find function"),
2210
    //         Some(expected)
2211
    //     );
2212
    // }
2213
2214
    // #[test]
2215
    // fn test_find_function_none() {
2216
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2217
    //     let exception_data = pe.exception_data.expect("get exception data");
2218
2219
    //     // 0x1d00 is the end address of the last function.
2220
2221
    //     assert_eq!(
2222
    //         exception_data.find_function(0x1d00).expect("find function"),
2223
    //         None
2224
    //     );
2225
    // }
2226
2227
    // #[test]
2228
    // fn test_get_unwind_info() {
2229
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2230
    //     let exception_data = pe.exception_data.expect("get exception data");
2231
2232
    //     // runtime function #0 directly refers to unwind info
2233
    //     let rt_function = RuntimeFunction {
2234
    //         begin_address: 0x1010,
2235
    //         end_address: 0x1090,
2236
    //         unwind_info_address: 0x25d8,
2237
    //     };
2238
2239
    //     let unwind_info = exception_data
2240
    //         .get_unwind_info(rt_function, &pe.sections)
2241
    //         .expect("get unwind info");
2242
2243
    //     // Unwind codes just used to assert that the right unwind info was resolved
2244
    //     let expected = &[4, 98];
2245
2246
    //     assert_eq!(unwind_info.code_bytes, expected);
2247
    // }
2248
2249
    // #[test]
2250
    // fn test_get_unwind_info_redirect() {
2251
    //     let pe = PE::parse(&PE_DATA).expect("parse PE");
2252
    //     let exception_data = pe.exception_data.expect("get exception data");
2253
2254
    //     // runtime function #4 has a redirect (unwind_info_address & 1).
2255
    //     let rt_function = RuntimeFunction {
2256
    //         begin_address: 0x1355,
2257
    //         end_address: 0x1420,
2258
    //         unwind_info_address: 0x4019,
2259
    //     };
2260
2261
    //     let unwind_info = exception_data
2262
    //         .get_unwind_info(rt_function, &pe.sections)
2263
    //         .expect("get unwind info");
2264
2265
    //     // Unwind codes just used to assert that the right unwind info was resolved
2266
    //     let expected = &[
2267
    //         28, 100, 15, 0, 28, 84, 14, 0, 28, 52, 12, 0, 28, 82, 24, 240, 22, 224, 20, 208, 18,
2268
    //         192, 16, 112,
2269
    //     ];
2270
2271
    //     assert_eq!(unwind_info.code_bytes, expected);
2272
    // }
2273
2274
    #[test]
2275
    fn test_unwind_codes_one_epilog() {
2276
        // ; Prologue
2277
        // .text:00000001400884E0 000 57        push    rdi // Save RDI
2278
        //
2279
        // .text:00000001400884E1 008 8B C2     mov     eax, edx
2280
        // .text:00000001400884E3 008 48 8B F9  mov     rdi, rcx
2281
        // .text:00000001400884E6 008 49 8B C8  mov     rcx, r8
2282
        // .text:00000001400884E9 008 F3 AA     rep stosb
2283
        // .text:00000001400884EB 008 49 8B C1  mov     rax, r9
2284
        //
2285
        // ; Epilogue
2286
        // .text:00000001400884EE 008 5F        pop     rdi // Restore RDI
2287
        // .text:00000001400884EF 000 C3        retn
2288
2289
        const BYTES: &[u8] = &[
2290
            0x02, 0x01, 0x03, 0x00, // UNWIND_INFO_HDR <2, 0, 1, 3, 0, 0>
2291
            0x02, 0x16, // UNWIND_CODE <<2, 6, 1>> ; UWOP_EPILOG
2292
            0x00, 0x06, // UNWIND_CODE <<0, 6, 0>> ; UWOP_EPILOG
2293
            0x01, 0x70, // UNWIND_CODE <<1, 0, 7>> ; UWOP_PUSH_NONVOL
2294
        ];
2295
2296
        let unwind_info = UnwindInfo::parse(BYTES, 0).unwrap();
2297
        let unwind_codes: Vec<UnwindCode> = unwind_info
2298
            .unwind_codes()
2299
            .map(|result| result.expect("Unable to parse unwind code"))
2300
            .collect();
2301
        assert_eq!(unwind_codes.len(), 3);
2302
        assert_eq!(
2303
            unwind_codes,
2304
            [
2305
                UnwindCode {
2306
                    code_offset: 2,
2307
                    operation: UnwindOperation::Epilog {
2308
                        offset_low_or_size: 2,
2309
                        offset_high_or_flags: 1,
2310
                    },
2311
                },
2312
                UnwindCode {
2313
                    code_offset: 0,
2314
                    operation: UnwindOperation::Epilog {
2315
                        offset_low_or_size: 0,
2316
                        offset_high_or_flags: 0,
2317
                    },
2318
                },
2319
                UnwindCode {
2320
                    code_offset: 1,
2321
                    operation: UnwindOperation::PushNonVolatile(Register(7)), // RDI
2322
                },
2323
            ]
2324
        );
2325
    }
2326
2327
    #[test]
2328
    fn test_unwind_codes_two_epilog() {
2329
        // ; Prologue
2330
        // .text:00000001400889A0 000 57        push    rdi // Save RDI
2331
        // .text:00000001400889A1 008 56        push    rsi // Save RSI
2332
        //
2333
        // .text:00000001400889A2 010 48 8B F9  mov     rdi, rcx
2334
        // .text:00000001400889A5 010 48 8B F2  mov     rsi, rdx
2335
        // .text:00000001400889A8 010 49 8B C8  mov     rcx, r8
2336
        // .text:00000001400889AB 010 F3 A4     rep movsb
2337
        //
2338
        // ; Epilogue
2339
        // .text:00000001400889AD 010 5E        pop     rsi // Restore RSI
2340
        // .text:00000001400889AE 008 5F        pop     rdi // Restore RDI
2341
        // .text:00000001400889AF 000 C3        retn
2342
2343
        const BYTES: &[u8] = &[
2344
            0x02, 0x02, 0x04, 0x00, // $xdatasym_5     UNWIND_INFO_HDR <2, 0, 2, 4, 0, 0>
2345
            0x03, 0x16, // UNWIND_CODE <<3, 6, 1>> ; UWOP_EPILOG
2346
            0x00, 0x06, // UNWIND_CODE <<0, 6, 0>> ; UWOP_EPILOG
2347
            0x02, 0x60, // UNWIND_CODE <<2, 0, 6>> ; UWOP_PUSH_NONVOL
2348
            0x01, 0x70, // UNWIND_CODE <<1, 0, 7>> ; UWOP_PUSH_NONVOL
2349
        ];
2350
2351
        let unwind_info = UnwindInfo::parse(BYTES, 0).unwrap();
2352
        let unwind_codes: Vec<UnwindCode> = unwind_info
2353
            .unwind_codes()
2354
            .map(|result| result.expect("Unable to parse unwind code"))
2355
            .collect();
2356
        assert_eq!(unwind_codes.len(), 4);
2357
        assert_eq!(
2358
            unwind_codes,
2359
            [
2360
                UnwindCode {
2361
                    code_offset: 3,
2362
                    operation: UnwindOperation::Epilog {
2363
                        offset_low_or_size: 3,
2364
                        offset_high_or_flags: 1,
2365
                    },
2366
                },
2367
                UnwindCode {
2368
                    code_offset: 0,
2369
                    operation: UnwindOperation::Epilog {
2370
                        offset_low_or_size: 0,
2371
                        offset_high_or_flags: 0,
2372
                    },
2373
                },
2374
                UnwindCode {
2375
                    code_offset: 2,
2376
                    operation: UnwindOperation::PushNonVolatile(Register(6)), // RSI
2377
                },
2378
                UnwindCode {
2379
                    code_offset: 1,
2380
                    operation: UnwindOperation::PushNonVolatile(Register(7)), // RDI
2381
                },
2382
            ]
2383
        );
2384
    }
2385
2386
    #[test]
2387
    fn test_iter_unwind_codes() {
2388
        let unwind_info = UnwindInfo {
2389
            version: 1,
2390
            size_of_prolog: 4,
2391
            frame_register: Register(0),
2392
            frame_register_offset: 0,
2393
            chained_info: None,
2394
            handler: None,
2395
            code_bytes: &[4, 98],
2396
        };
2397
2398
        let unwind_codes: Vec<UnwindCode> = unwind_info
2399
            .unwind_codes()
2400
            .map(|result| result.expect("parse unwind code"))
2401
            .collect();
2402
2403
        assert_eq!(unwind_codes.len(), 1);
2404
2405
        let expected = UnwindCode {
2406
            code_offset: 4,
2407
            operation: UnwindOperation::Alloc(56),
2408
        };
2409
2410
        assert_eq!(unwind_codes[0], expected);
2411
    }
2412
2413
    #[rustfmt::skip]
2414
    const UNWIND_INFO_C_SCOPE_TABLE: &[u8] = &[
2415
        // UNWIND_INFO_HDR
2416
        0x09, 0x0F, 0x06, 0x00,
2417
2418
        // UNWIND_CODEs
2419
        0x0F, 0x64,             // UWOP_SAVE_NONVOL (Offset=6, Reg=0x0F)
2420
        0x09, 0x00,
2421
        0x0F, 0x34,             // UWOP_SAVE_NONVOL (Offset=3, Reg=0x0F)
2422
        0x08, 0x00,
2423
        0x0F, 0x52,             // UWOP_ALLOC_SMALL (Size = (2 * 8) + 8 = 24 bytes)
2424
        0x0B, 0x70,             // UWOP_PUSH_NONVOL (Reg=0x0B)
2425
2426
        // Exception handler RVA
2427
        0xC0, 0x1F, 0x00, 0x00, // __C_specific_handler
2428
2429
        // Scope count
2430
        0x02, 0x00, 0x00, 0x00, // Scope table count = 2
2431
2432
        // First C_SCOPE_TABLE entry
2433
        0x01, 0x15, 0x00, 0x00, // BeginAddress   = 0x00001501
2434
        0x06, 0x16, 0x00, 0x00, // EndAddress     = 0x00001606
2435
        0x76, 0x1F, 0x00, 0x00, // HandlerAddress = 0x00001F76
2436
        0x06, 0x16, 0x00, 0x00, // JumpTarget     = 0x00001606
2437
2438
        // Second C_SCOPE_TABLE entry
2439
        0x3A, 0x16, 0x00, 0x00, // BeginAddress   = 0x0000163A
2440
        0x4C, 0x16, 0x00, 0x00, // EndAddress     = 0x0000164C
2441
        0x76, 0x1F, 0x00, 0x00, // HandlerAddress = 0x00001F76
2442
        0x06, 0x16, 0x00, 0x00, // JumpTarget     = 0x00001606
2443
    ];
2444
2445
    #[rustfmt::skip]
2446
    const UNWIND_INFO_C_SCOPE_TABLE_INVALID: &[u8] = &[
2447
        // UNWIND_INFO_HDR
2448
        0x09, 0x0F, 0x06, 0x00,
2449
2450
        // UNWIND_CODEs
2451
        0x0F, 0x64,             // UWOP_SAVE_NONVOL (Offset=6, Reg=0x0F)
2452
        0x09, 0x00,
2453
        0x0F, 0x34,             // UWOP_SAVE_NONVOL (Offset=3, Reg=0x0F)
2454
        0x08, 0x00,
2455
        0x0F, 0x52,             // UWOP_ALLOC_SMALL (Size = (2 * 8) + 8 = 24 bytes)
2456
        0x0B, 0x70,             // UWOP_PUSH_NONVOL (Reg=0x0B)
2457
2458
        // Exception handler RVA
2459
        0xC0, 0x1F, 0x00, 0x00, // __C_specific_handler
2460
2461
        // Scope count
2462
        0x02, 0x00, 0x00, 0x00, // Scope table count = 2
2463
2464
        // First C_SCOPE_TABLE entry
2465
        0x01, 0x15, 0x00, 0x00, // BeginAddress   = 0x00001501
2466
        0x06, 0x16, 0x00, 0x00, // EndAddress     = 0x00001606
2467
        0x76, 0x1F, 0x00, 0x00, // HandlerAddress = 0x00001F76
2468
        0x06, 0x16, 0x00, 0x00, // JumpTarget     = 0x00001606
2469
2470
        // Second C_SCOPE_TABLE entry
2471
        0x3A, 0x16, 0x00, 0x00, // BeginAddress   = 0x0000163A
2472
        0x4C, 0x16, 0x00, 0x00, // EndAddress     = 0x0000164C
2473
        0x76, 0x1F, 0x00, 0x00, // HandlerAddress = 0x00001F76
2474
        0x06,                   // JumpTarget     = 0x??????06
2475
    ];
2476
2477
    #[test]
2478
    fn parse_c_scope_table() {
2479
        let unwind_info = UnwindInfo::parse(UNWIND_INFO_C_SCOPE_TABLE, 0)
2480
            .expect("Failed to parse unwind info with C scope table");
2481
        let entries = unwind_info
2482
            .c_scope_table_entries()
2483
            .expect("C scope table should present");
2484
        let entries = entries.collect::<Vec<_>>();
2485
        assert_eq!(entries.len(), 2);
2486
        assert_eq!(
2487
            entries[0],
2488
            ScopeTableEntry {
2489
                begin: 0x00001501,
2490
                end: 0x00001606,
2491
                handler: 0x00001F76,
2492
                target: 0x00001606,
2493
            }
2494
        );
2495
        assert_eq!(
2496
            entries[1],
2497
            ScopeTableEntry {
2498
                begin: 0x0000163A,
2499
                end: 0x0000164C,
2500
                handler: 0x00001F76,
2501
                target: 0x00001606,
2502
            }
2503
        );
2504
    }
2505
2506
    #[test]
2507
    #[should_panic(expected = "C scope table should present")]
2508
    fn malformed_scope_table_is_not_allowed() {
2509
        let unwind_info = UnwindInfo::parse(UNWIND_INFO_C_SCOPE_TABLE_INVALID, 0)
2510
            .expect("Failed to parse unwind info with C scope table");
2511
        unwind_info
2512
            .c_scope_table_entries()
2513
            .expect("C scope table should present");
2514
    }
2515
2516
    #[test]
2517
    #[should_panic(expected = "Scope table is not aligned")]
2518
    fn unaligned_scope_table_is_not_allowed() {
2519
        let it = ScopeTableIterator {
2520
            data: &[0x00, 0x00, 0x00, 0x00, 0x00],
2521
            offset: 0,
2522
        };
2523
        let _ = it.collect::<Vec<_>>();
2524
    }
2525
2526
    mod arm64 {
2527
        use crate::pe::exception::*;
2528
2529
        #[test]
2530
        fn parse_unwind_info_packed() {
2531
            // unwind_data = 0x80600021
2532
            // bits [1:0]   = 01       -> Flag = 1 (packed function)
2533
            // bits [12:2]  = 8        -> FunctionLength = 8 * 4 = 32
2534
            // bits [15:13] = 000      -> RegF = 0
2535
            // bits [19:16] = 0000     -> RegI = 0
2536
            // bit  [20]    = 0        -> H = false
2537
            // bits [22:21] = 11       -> CR = 3 (chained)
2538
            // bits [31:23] = 0x100    -> FrameSize = 256 * 16 = 0x1000
2539
            const DATA: &[u8] = &[
2540
                0x78, 0x11, 0x00, 0x00, // begin_address
2541
                0x21, 0x00, 0x60, 0x80, // unwind_data
2542
            ];
2543
2544
            let func = DATA.pread::<Arm64RuntimeFunction>(0).unwrap();
2545
            assert_eq!(func.begin_address, 0x1178);
2546
            assert_eq!(func.is_packed(), true);
2547
            assert_eq!(func.flag(), ARM64_PDATA_PACKED_UNWIND_FUNCTION);
2548
            assert_eq!(func.function_length(), 32);
2549
            assert_eq!(func.frame_size(), 0x1000);
2550
            assert_eq!(func.cr(), ARM64_PDATA_CR_CHAINED);
2551
            assert_eq!(func.h(), false);
2552
            assert_eq!(func.reg_f(), 0);
2553
            assert_eq!(func.reg_i(), 0);
2554
        }
2555
2556
        #[test]
2557
        fn parse_packed_all_fields_nonzero() {
2558
            // Construct a value where every packed field is non-zero to verify
2559
            // that each accessor extracts from the correct bit position.
2560
            //
2561
            // unwind_data = 0x04556041
2562
            // bits [1:0]   = 01       -> Flag = 1 (packed function)
2563
            // bits [12:2]  = 0x10     -> FunctionLength = 16 * 4 = 64
2564
            // bits [15:13] = 011      -> RegF = 3
2565
            // bits [19:16] = 0101     -> RegI = 5
2566
            // bit  [20]    = 1        -> H = true
2567
            // bits [22:21] = 10       -> CR = 2 (chained with PAC)
2568
            // bits [31:23] = 0x08     -> FrameSize = 8 * 16 = 128
2569
            const DATA: &[u8] = &[
2570
                0x00, 0x10, 0x00, 0x00, // begin_address
2571
                0x41, 0x60, 0x55, 0x04, // unwind_data = 0x04556041
2572
            ];
2573
2574
            let func = DATA.pread::<Arm64RuntimeFunction>(0).unwrap();
2575
            assert_eq!(func.flag(), ARM64_PDATA_PACKED_UNWIND_FUNCTION);
2576
            assert!(func.is_packed());
2577
            assert_eq!(func.function_length(), 64);
2578
            assert_eq!(func.frame_size(), 128);
2579
            assert_eq!(func.cr(), ARM64_PDATA_CR_CHAINED_WITH_PAC);
2580
            assert_eq!(func.h(), true);
2581
            assert_eq!(func.reg_i(), 5);
2582
            assert_eq!(func.reg_f(), 3);
2583
        }
2584
2585
        #[test]
2586
        fn parse_packed_bit_positions() {
2587
            // unwind_data = 0x02424281
2588
            // bits [1:0]   = 01       -> Flag = 1 (packed function)
2589
            // bits [12:2]  = 0xA0     -> FunctionLength = 160 * 4 = 640
2590
            // bits [15:13] = 010      -> RegF = 2
2591
            // bits [19:16] = 0010     -> RegI = 2
2592
            // bit  [20]    = 0        -> H = false
2593
            // bits [22:21] = 10       -> CR = 2 (chained with PAC)
2594
            // bits [31:23] = 0x04     -> FrameSize = 4 * 16 = 64
2595
            let func = Arm64RuntimeFunction {
2596
                begin_address: 0,
2597
                unwind_data: 0x02424281,
2598
            };
2599
            assert_eq!(func.flag(), ARM64_PDATA_PACKED_UNWIND_FUNCTION);
2600
            assert_eq!(func.function_length(), 640);
2601
            assert_eq!(func.reg_f(), 2);
2602
            assert_eq!(func.reg_i(), 2);
2603
            assert_eq!(func.h(), false);
2604
            assert_eq!(func.cr(), ARM64_PDATA_CR_CHAINED_WITH_PAC);
2605
            assert_eq!(func.frame_size(), 64);
2606
        }
2607
2608
        #[test]
2609
        fn parse_packed_max_field_values() {
2610
            // unwind_data = 0xFFFFFFFF
2611
            // bits [1:0]   = 11       -> Flag = 3
2612
            // bits [12:2]  = 0x7FF    -> FunctionLength = 0x7FF * 4
2613
            // bits [15:13] = 111      -> RegF = 7
2614
            // bits [19:16] = 1111     -> RegI = 15
2615
            // bit  [20]    = 1        -> H = true
2616
            // bits [22:21] = 11       -> CR = 3
2617
            // bits [31:23] = 0x1FF    -> FrameSize = 0x1FF * 16
2618
            let func = Arm64RuntimeFunction {
2619
                begin_address: 0,
2620
                unwind_data: 0xFFFFFFFF,
2621
            };
2622
            assert_eq!(func.flag(), 3);
2623
            assert_eq!(func.function_length(), 0x7FF * 4);
2624
            assert_eq!(func.reg_f(), 0x7);
2625
            assert_eq!(func.reg_i(), 0xF);
2626
            assert_eq!(func.h(), true);
2627
            assert_eq!(func.cr(), 0x3);
2628
            assert_eq!(func.frame_size(), 0x1FF * 16);
2629
        }
2630
2631
        #[test]
2632
        fn parse_packed_field_isolation() {
2633
            // bits [31:23] = 0x001    -> FrameSize = 1 * 16 = 16
2634
            let only_frame = Arm64RuntimeFunction {
2635
                begin_address: 0,
2636
                unwind_data: 0x1 << 23,
2637
            };
2638
            assert_eq!(only_frame.frame_size(), 16);
2639
            assert_eq!(only_frame.cr(), 0);
2640
            assert_eq!(only_frame.h(), false);
2641
            assert_eq!(only_frame.reg_i(), 0);
2642
            assert_eq!(only_frame.reg_f(), 0);
2643
2644
            // bits [22:21] = 11       -> CR = 3
2645
            let only_cr = Arm64RuntimeFunction {
2646
                begin_address: 0,
2647
                unwind_data: 0x3 << 21,
2648
            };
2649
            assert_eq!(only_cr.cr(), 3);
2650
            assert_eq!(only_cr.frame_size(), 0);
2651
            assert_eq!(only_cr.h(), false);
2652
            assert_eq!(only_cr.reg_i(), 0);
2653
            assert_eq!(only_cr.reg_f(), 0);
2654
2655
            // bit  [20]    = 1        -> H = true
2656
            let only_h = Arm64RuntimeFunction {
2657
                begin_address: 0,
2658
                unwind_data: 0x1 << 20,
2659
            };
2660
            assert_eq!(only_h.h(), true);
2661
            assert_eq!(only_h.cr(), 0);
2662
            assert_eq!(only_h.frame_size(), 0);
2663
            assert_eq!(only_h.reg_i(), 0);
2664
            assert_eq!(only_h.reg_f(), 0);
2665
2666
            // bits [19:16] = 1111     -> RegI = 15
2667
            let only_reg_i = Arm64RuntimeFunction {
2668
                begin_address: 0,
2669
                unwind_data: 0xF << 16,
2670
            };
2671
            assert_eq!(only_reg_i.reg_i(), 15);
2672
            assert_eq!(only_reg_i.h(), false);
2673
            assert_eq!(only_reg_i.cr(), 0);
2674
            assert_eq!(only_reg_i.frame_size(), 0);
2675
            assert_eq!(only_reg_i.reg_f(), 0);
2676
2677
            // bits [15:13] = 111      -> RegF = 7
2678
            let only_reg_f = Arm64RuntimeFunction {
2679
                begin_address: 0,
2680
                unwind_data: 0x7 << 13,
2681
            };
2682
            assert_eq!(only_reg_f.reg_f(), 7);
2683
            assert_eq!(only_reg_f.reg_i(), 0);
2684
            assert_eq!(only_reg_f.h(), false);
2685
            assert_eq!(only_reg_f.cr(), 0);
2686
            assert_eq!(only_reg_f.frame_size(), 0);
2687
        }
2688
2689
        #[test]
2690
        fn parse_unpacked_xdata_rva() {
2691
            // Flag = 0 -> unwind_data is an RVA (low 2 bits cleared)
2692
            // unwind_data = 0x00001234, with low 2 bits = 00
2693
            const DATA: &[u8] = &[
2694
                0x00, 0x10, 0x00, 0x00, // begin_address
2695
                0x34, 0x12, 0x00, 0x00, // unwind_data = 0x00001234
2696
            ];
2697
2698
            let func = DATA.pread::<Arm64RuntimeFunction>(0).unwrap();
2699
            assert_eq!(func.flag(), ARM64_PDATA_REF_TO_FULL_XDATA);
2700
            assert!(!func.is_packed());
2701
            assert_eq!(func.unwind_data_rva(), 0x00001234);
2702
        }
2703
2704
        #[test]
2705
        fn arm64_runtime_function_iterator() {
2706
            // 2 ARM64 entries (8 bytes each = 16 bytes total)
2707
            #[rustfmt::skip]
2708
            const DATA: &[u8] = &[
2709
                0x00, 0x10, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
2710
                0x00, 0x20, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
2711
            ];
2712
2713
            let iter = Arm64RuntimeFunctionIterator { data: DATA };
2714
            let funcs: Vec<_> = iter.map(|r| r.unwrap()).collect();
2715
            assert_eq!(funcs.len(), 2);
2716
            assert_eq!(funcs[0].begin_address, 0x1000);
2717
            assert_eq!(funcs[0].unwind_data, 0x00001234);
2718
            assert_eq!(funcs[1].begin_address, 0x2000);
2719
            assert_eq!(funcs[1].unwind_data, 0x00005678);
2720
        }
2721
2722
        #[test]
2723
        fn xdata_header_with_extension() {
2724
            // Header word with epilog_count=0 and code_words=0 -> extension present
2725
            #[rustfmt::skip]
2726
            const XDATA: &[u8] = &[
2727
                0x04, 0x00, 0x00, 0x00, // header: func_len=1, ver=0, X=0, E=0, epilog_count=0, code_words=0
2728
                0x01, 0x00, 0x01, 0x00, // extension: epilog_count=1, code_words=1
2729
2730
                // Epilog scope
2731
                0x0D, 0x00, 0x00, 0x00,
2732
2733
                // Unwind codes (1 word = 4 bytes)
2734
                0x81, 0xE4, 0xE3, 0xE3,
2735
            ];
2736
2737
            let info = Arm64UnwindInfo::parse(XDATA, 0).unwrap();
2738
            assert!(info.extension.is_some());
2739
            let ext = info.extension.unwrap();
2740
            assert_eq!(ext.epilog_count(), 1);
2741
            assert_eq!(ext.code_words(), 1);
2742
            assert_eq!(info.epilog_scope_bytes.len(), 4);
2743
            assert_eq!(info.unwind_code_bytes.len(), 4);
2744
        }
2745
2746
        #[test]
2747
        fn epilog_scope_field_extraction() {
2748
            // Construct: start_offset=0x3FFFF (18 bits max), condition=0xF (reserved for arm64), start_index=0x3FF (10 bits max)
2749
            // But let's use smaller values to be clear:
2750
            // start_offset_words = 52 (0x34 / 4 = 0x0D -> bits [17:0] = 0x0D, *4 = 52)
2751
            // condition = 0x0E (bits [21:18])
2752
            // start_index = 2 (bits [31:22])
2753
            //
2754
            // value = (2 << 22) | (0x0E << 18) | 0x0D = 0x00B8000D
2755
            let scope = Arm64EpilogScope(0x00B8000D);
2756
            assert_eq!(scope.start_offset_words(), 52);
2757
            assert_eq!(scope.condition(), 0x0E);
2758
            assert_eq!(scope.start_index(), 2);
2759
        }
2760
2761
        #[test]
2762
        fn parse_unwind_info_0() {
2763
            #[rustfmt::skip]
2764
            const XDATA: &[u8] = &[
2765
                0x96, 0x00, 0x70, 0x10,
2766
2767
                // Unwind codes
2768
                0xE1,                   // set_fp
2769
                0x87,                   // save_fplr_x
2770
                0xD1, 0x04,             // save_reg
2771
                0xC8, 0x82,             // save_regp
2772
                0x26,                   // save_r19r20_x
2773
                0xE4,                   // end
2774
2775
                // Exception handler and its exception data
2776
                0x8C, 0x1E, 0x00, 0x00, // imagerel __CxxFrameHandler3_0
2777
                0xA0, 0x32, 0x00, 0x00, // exception data
2778
            ];
2779
2780
            let info = Arm64UnwindInfo::parse(XDATA, 0).unwrap();
2781
2782
            // do not count handler-specific dta.
2783
            assert_eq!(
2784
                Arm64UnwindInfo::size(XDATA).unwrap(),
2785
                XDATA.len() - size_of_val(&0u32)
2786
            );
2787
2788
            assert_eq!(info.header.0, 0x10700096);
2789
            assert_eq!(info.header.function_length(), 600);
2790
            assert_eq!(info.header.version(), 0);
2791
            assert_eq!(info.header.exception_data_present(), true);
2792
            assert_eq!(info.header.epilog_in_header(), true);
2793
            assert_eq!(info.header.epilog_count(), 1);
2794
            assert_eq!(info.header.code_words(), 2);
2795
            assert_eq!(info.header.has_extension(), false);
2796
2797
            assert_eq!(info.unwind_code_bytes, &XDATA[4..12]);
2798
2799
            let codes = info.unwind_codes(0).unwrap();
2800
            let codes = codes.collect::<Vec<_>>();
2801
            assert_eq!(codes.len(), 6);
2802
            assert_eq!(
2803
                codes[0].as_ref().unwrap(),
2804
                &Arm64UnwindCodeData {
2805
                    offset: 0,
2806
                    code: Arm64UnwindCode::SetFp,
2807
                    len: 1,
2808
                }
2809
            );
2810
            assert_eq!(
2811
                codes[1].as_ref().unwrap(),
2812
                &Arm64UnwindCodeData {
2813
                    offset: 1,
2814
                    code: Arm64UnwindCode::SaveFpLrPreindexed { offset_bytes: 64 },
2815
                    len: 1,
2816
                }
2817
            );
2818
            assert_eq!(
2819
                codes[2].as_ref().unwrap(),
2820
                &Arm64UnwindCodeData {
2821
                    offset: 2,
2822
                    code: Arm64UnwindCode::SaveReg {
2823
                        reg: 23,
2824
                        offset_bytes: 32,
2825
                    },
2826
                    len: 2,
2827
                }
2828
            );
2829
            assert_eq!(
2830
                codes[3].as_ref().unwrap(),
2831
                &Arm64UnwindCodeData {
2832
                    offset: 4,
2833
                    code: Arm64UnwindCode::SaveRegPair {
2834
                        first_reg: 21,
2835
                        offset_bytes: 16,
2836
                    },
2837
                    len: 2,
2838
                }
2839
            );
2840
            assert_eq!(
2841
                codes[4].as_ref().unwrap(),
2842
                &Arm64UnwindCodeData {
2843
                    offset: 6,
2844
                    code: Arm64UnwindCode::SaveR19R20Preindexed { offset_bytes: 48 },
2845
                    len: 1,
2846
                }
2847
            );
2848
            assert_eq!(
2849
                codes[5].as_ref().unwrap(),
2850
                &Arm64UnwindCodeData {
2851
                    offset: 7,
2852
                    code: Arm64UnwindCode::End,
2853
                    len: 1,
2854
                }
2855
            );
2856
        }
2857
2858
        #[test]
2859
        fn parse_unwind_info_1() {
2860
            #[rustfmt::skip]
2861
            const XDATA: &[u8] = &[
2862
                0x10, 0x00, 0x50, 0x08,
2863
2864
                // Epilog scopes
2865
                0x0D, 0x00, 0x00, 0x00,
2866
2867
                // Unwind codes
2868
                0x81, // save_fplr_x
2869
                0xE4, // end
2870
2871
                0xE3, 0xE3, // align to 4
2872
2873
                // Exception handler and its exception data
2874
                0x8C, 0x1E, 0x00, 0x00, // imagerel __CxxFrameHandler3_0
2875
                0xA0, 0x32, 0x00, 0x00, // exception data
2876
            ];
2877
2878
            let info = Arm64UnwindInfo::parse(XDATA, 0).unwrap();
2879
2880
            // do not count handler-specific dta.
2881
            assert_eq!(
2882
                Arm64UnwindInfo::size(XDATA).unwrap(),
2883
                XDATA.len() - size_of_val(&0u32)
2884
            );
2885
2886
            assert_eq!(info.header.0, 0x08500010);
2887
            assert_eq!(info.header.version(), 0);
2888
            assert_eq!(info.header.exception_data_present(), true);
2889
            assert_eq!(info.header.epilog_in_header(), false);
2890
2891
            assert_eq!(info.unwind_code_bytes, &XDATA[8..12]);
2892
2893
            let scopes = info.epilog_scopes().unwrap();
2894
            let scopes = scopes.map(Result::unwrap).collect::<Vec<_>>();
2895
            assert_eq!(scopes.len(), 1);
2896
            assert_eq!(scopes[0], Arm64EpilogScope(0x0000000D));
2897
2898
            let codes = info.unwind_codes(0).unwrap();
2899
            let codes = codes.collect::<Vec<_>>();
2900
            assert_eq!(codes.len(), 4);
2901
2902
            assert_eq!(
2903
                codes[0].as_ref().unwrap(),
2904
                &Arm64UnwindCodeData {
2905
                    offset: 0,
2906
                    code: Arm64UnwindCode::SaveFpLrPreindexed { offset_bytes: 16 },
2907
                    len: 1,
2908
                }
2909
            );
2910
            assert_eq!(
2911
                codes[1].as_ref().unwrap(),
2912
                &Arm64UnwindCodeData {
2913
                    offset: 1,
2914
                    code: Arm64UnwindCode::End,
2915
                    len: 1,
2916
                }
2917
            );
2918
            assert_eq!(
2919
                // padding
2920
                codes[2].as_ref().unwrap(),
2921
                &Arm64UnwindCodeData {
2922
                    offset: 2,
2923
                    code: Arm64UnwindCode::Nop,
2924
                    len: 1,
2925
                }
2926
            );
2927
            assert_eq!(
2928
                // padding
2929
                codes[3].as_ref().unwrap(),
2930
                &Arm64UnwindCodeData {
2931
                    offset: 3,
2932
                    code: Arm64UnwindCode::Nop,
2933
                    len: 1,
2934
                }
2935
            );
2936
2937
            assert_eq!(
2938
                info.exception_handler,
2939
                Some(Arm64ExceptionHandler {
2940
                    rva: 0x00001E8C,
2941
                    data: &0x000032A0u32.to_le_bytes()
2942
                })
2943
            );
2944
        }
2945
2946
        #[test]
2947
        fn unwind_code_alloc_large_and_custom_stack() {
2948
            // Test alloc_l (0xE0), pac_sign_lr (0xFC), custom stack opcodes, and end
2949
            #[rustfmt::skip]
2950
            const CODES: &[u8] = &[
2951
                0xE0, 0x00, 0x01, 0x00, // alloc_l: imm = 0x000100, size = 256 * 16 = 4096
2952
                0xFC,                    // pac_sign_lr
2953
                0xE8,                    // MSFT_OP_TRAP_FRAME
2954
                0xE4,                    // end
2955
                0xE3,                    // nop (padding)
2956
            ];
2957
2958
            let iter = Arm64UnwindCodeIterator::new(CODES, 0).unwrap();
2959
            let codes: Vec<_> = iter.map(|r| r.unwrap()).collect();
2960
            assert_eq!(codes.len(), 5);
2961
            assert_eq!(
2962
                codes[0].code,
2963
                Arm64UnwindCode::AllocLarge { size_bytes: 4096 }
2964
            );
2965
            assert_eq!(codes[0].len, 4);
2966
            assert_eq!(codes[1].code, Arm64UnwindCode::PacSignLr);
2967
            assert_eq!(
2968
                codes[2].code,
2969
                Arm64UnwindCode::CustomStack(Arm64CustomStackCase::TrapFrame)
2970
            );
2971
            assert_eq!(codes[3].code, Arm64UnwindCode::End);
2972
            assert_eq!(codes[4].code, Arm64UnwindCode::Nop);
2973
        }
2974
2975
        #[test]
2976
        fn unwind_code_reserved_opcodes() {
2977
            // Test reserved multi-byte opcodes
2978
            #[rustfmt::skip]
2979
            const CODES: &[u8] = &[
2980
                0xF8, 0xAB,             // reserved 2-byte
2981
                0xF9, 0x12, 0x34,       // reserved 3-byte
2982
                0xE4,                    // end
2983
                0xE3, 0xE3,             // padding
2984
            ];
2985
2986
            let iter = Arm64UnwindCodeIterator::new(CODES, 0).unwrap();
2987
            let codes: Vec<_> = iter.map(|r| r.unwrap()).collect();
2988
            assert_eq!(codes.len(), 5);
2989
            assert_eq!(
2990
                codes[0].code,
2991
                Arm64UnwindCode::Reserved {
2992
                    opcode: 0xF8,
2993
                    data: 0xAB,
2994
                    data_len: 1
2995
                }
2996
            );
2997
            assert_eq!(codes[0].len, 2);
2998
            assert_eq!(
2999
                codes[1].code,
3000
                Arm64UnwindCode::Reserved {
3001
                    opcode: 0xF9,
3002
                    data: 0x1234,
3003
                    data_len: 2
3004
                }
3005
            );
3006
            assert_eq!(codes[1].len, 3);
3007
            assert_eq!(codes[2].code, Arm64UnwindCode::End);
3008
        }
3009
3010
        #[test]
3011
        fn unwind_code_freg_operations() {
3012
            // save_fregp (0xD8): b0=0xD8, b1=0x42
3013
            //   x = ((0xD8 & 0x01) << 2) | ((0x42 >> 6) & 0x03) = (0 << 2) | 1 = 1
3014
            //   z = 0x42 & 0x3f = 2
3015
            //   first_reg = 8 + 1 = 9, offset = 2 * 8 = 16
3016
            //
3017
            // save_freg_x (0xDE): b1=0x65
3018
            //   x = (0x65 >> 5) & 0x07 = 3
3019
            //   z = 0x65 & 0x1f = 5
3020
            //   reg = 8 + 3 = 11, offset = (5+1) * 8 = 48
3021
            #[rustfmt::skip]
3022
            const CODES: &[u8] = &[
3023
                0xD8, 0x42,             // save_fregp
3024
                0xDE, 0x65,             // save_freg_x
3025
            ];
3026
3027
            let iter = Arm64UnwindCodeIterator::new(CODES, 0).unwrap();
3028
            let codes: Vec<_> = iter.map(|r| r.unwrap()).collect();
3029
            assert_eq!(codes.len(), 2);
3030
            assert_eq!(
3031
                codes[0].code,
3032
                Arm64UnwindCode::SaveFRegPair {
3033
                    first_reg: 9,
3034
                    offset_bytes: 16
3035
                }
3036
            );
3037
            assert_eq!(
3038
                codes[1].code,
3039
                Arm64UnwindCode::SaveFRegPreindexed {
3040
                    reg: 11,
3041
                    offset_bytes: 48
3042
                }
3043
            );
3044
        }
3045
3046
        #[test]
3047
        fn unwind_code_iterator_start_index() {
3048
            // Verify that start_index correctly skips into the byte stream
3049
            #[rustfmt::skip]
3050
            const CODES: &[u8] = &[
3051
                0xE1,                   // set_fp (offset 0)
3052
                0x87,                   // save_fplr_x (offset 1)
3053
                0xE4,                   // end (offset 2)
3054
                0xE3,                   // nop padding (offset 3)
3055
            ];
3056
3057
            // Start at index 2 -> should see end + nop
3058
            let iter = Arm64UnwindCodeIterator::new(CODES, 2).unwrap();
3059
            let codes: Vec<_> = iter.map(|r| r.unwrap()).collect();
3060
            assert_eq!(codes.len(), 2);
3061
            assert_eq!(codes[0].code, Arm64UnwindCode::End);
3062
            assert_eq!(codes[0].offset, 2);
3063
            assert_eq!(codes[1].code, Arm64UnwindCode::Nop);
3064
        }
3065
3066
        #[test]
3067
        fn unwind_code_iterator_bad_length() {
3068
            // Length not a multiple of 4
3069
            let result = Arm64UnwindCodeIterator::new(&[0xE4, 0xE3, 0xE3], 0);
3070
            assert!(result.is_err());
3071
        }
3072
3073
        #[test]
3074
        fn xdata_no_exception_handler() {
3075
            // Header: func_len=4, ver=0, X=0, E=1, epilog_count=0, code_words=1
3076
            // X=0 -> no exception data
3077
            // E=1 -> epilog in header
3078
            #[rustfmt::skip]
3079
            const XDATA: &[u8] = &[
3080
                0x10, 0x00, 0x20, 0x08, // header
3081
                0x81, 0xE4, 0xE3, 0xE3, // unwind codes
3082
            ];
3083
3084
            let info = Arm64UnwindInfo::parse(XDATA, 0).unwrap();
3085
            assert!(!info.header.exception_data_present());
3086
            assert!(info.header.epilog_in_header());
3087
            assert!(info.exception_handler.is_none());
3088
            assert!(info.epilog_scopes().is_none());
3089
        }
3090
3091
        #[test]
3092
        fn xdata_size_without_handler() {
3093
            // Same as above — size should be header (4) + code_words (1*4) = 8
3094
            #[rustfmt::skip]
3095
            const XDATA: &[u8] = &[
3096
                0x10, 0x00, 0x20, 0x08, // header
3097
                0x81, 0xE4, 0xE3, 0xE3, // unwind codes
3098
            ];
3099
3100
            assert_eq!(Arm64UnwindInfo::size(XDATA).unwrap(), 8);
3101
        }
3102
3103
        #[test]
3104
        fn arm64_len_vs_x64_len() {
3105
            // Verify len_arm64 gives different (correct) results from len
3106
            let ed = ExceptionData {
3107
                bytes: &[0u8; 64],
3108
                offset: 0,
3109
                size: 32,
3110
                file_alignment: 4,
3111
            };
3112
            // 32 / 12 = 2 (x64)
3113
            assert_eq!(ed.len(), 2);
3114
            // 32 / 8 = 4 (arm64)
3115
            assert_eq!(ed.len_arm64(), 4);
3116
        }
3117
    }
3118
}