goblin_experimental/pe/exception.rs
1//! Exception handling and stack unwinding for x64.
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
42use core::cmp::Ordering;
43use core::fmt;
44use core::iter::FusedIterator;
45
46use scroll::ctx::TryFromCtx;
47use scroll::{self, Pread, Pwrite};
48
49use crate::error;
50
51use crate::pe::data_directories;
52use crate::pe::options;
53use crate::pe::section_table;
54use crate::pe::utils;
55
56/// The function has an exception handler that should be called when looking for functions that need
57/// to examine exceptions.
58const UNW_FLAG_EHANDLER: u8 = 0x01;
59/// The function has a termination handler that should be called when unwinding an exception.
60const UNW_FLAG_UHANDLER: u8 = 0x02;
61/// This unwind info structure is not the primary one for the procedure. Instead, the chained unwind
62/// info entry is the contents of a previous `RUNTIME_FUNCTION` entry. If this flag is set, then the
63/// `UNW_FLAG_EHANDLER` and `UNW_FLAG_UHANDLER` flags must be cleared. Also, the frame register and
64/// fixed-stack allocation fields must have the same values as in the primary unwind info.
65const UNW_FLAG_CHAININFO: u8 = 0x04;
66
67/// info == register number
68const UWOP_PUSH_NONVOL: u8 = 0;
69/// no info, alloc size in next 2 slots
70const UWOP_ALLOC_LARGE: u8 = 1;
71/// info == size of allocation / 8 - 1
72const UWOP_ALLOC_SMALL: u8 = 2;
73/// no info, FP = RSP + UNWIND_INFO.FPRegOffset*16
74const UWOP_SET_FPREG: u8 = 3;
75/// info == register number, offset in next slot
76const UWOP_SAVE_NONVOL: u8 = 4;
77/// info == register number, offset in next 2 slots
78const UWOP_SAVE_NONVOL_FAR: u8 = 5;
79/// changes the structure of unwind codes to `struct Epilogue`.
80/// (was UWOP_SAVE_XMM in version 1, but deprecated and removed)
81const UWOP_EPILOG: u8 = 6;
82/// reserved
83/// (was UWOP_SAVE_XMM_FAR in version 1, but deprecated and removed)
84const UWOP_SPARE_CODE: u8 = 7;
85/// info == XMM reg number, offset in next slot
86const UWOP_SAVE_XMM128: u8 = 8;
87/// info == XMM reg number, offset in next 2 slots
88const UWOP_SAVE_XMM128_FAR: u8 = 9;
89/// info == 0: no error-code, 1: error-code
90const UWOP_PUSH_MACHFRAME: u8 = 10;
91
92/// Size of `RuntimeFunction` entries.
93const RUNTIME_FUNCTION_SIZE: usize = 12;
94/// Size of unwind code slots. Codes take 1 - 3 slots.
95const UNWIND_CODE_SIZE: usize = 2;
96
97/// An unwind entry for a range of a function.
98///
99/// Unwind information for this function can be loaded with [`ExceptionData::get_unwind_info`].
100///
101/// [`ExceptionData::get_unwind_info`]: struct.ExceptionData.html#method.get_unwind_info
102#[repr(C)]
103#[derive(Copy, Clone, PartialEq, Default, Pread, Pwrite)]
104pub struct RuntimeFunction {
105 /// Function start address.
106 pub begin_address: u32,
107 /// Function end address.
108 pub end_address: u32,
109 /// Unwind info address.
110 pub unwind_info_address: u32,
111}
112
113impl fmt::Debug for RuntimeFunction {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 f.debug_struct("RuntimeFunction")
116 .field("begin_address", &format_args!("{:#x}", self.begin_address))
117 .field("end_address", &format_args!("{:#x}", self.end_address))
118 .field(
119 "unwind_info_address",
120 &format_args!("{:#x}", self.unwind_info_address),
121 )
122 .finish()
123 }
124}
125
126/// Iterator over runtime function entries in [`ExceptionData`](struct.ExceptionData.html).
127#[derive(Debug)]
128pub struct RuntimeFunctionIterator<'a> {
129 data: &'a [u8],
130}
131
132impl Iterator for RuntimeFunctionIterator<'_> {
133 type Item = error::Result<RuntimeFunction>;
134
135 fn next(&mut self) -> Option<Self::Item> {
136 if self.data.is_empty() {
137 return None;
138 }
139
140 Some(match self.data.pread_with(0, scroll::LE) {
141 Ok(func) => {
142 self.data = &self.data[RUNTIME_FUNCTION_SIZE..];
143 Ok(func)
144 }
145 Err(error) => {
146 self.data = &[];
147 Err(error.into())
148 }
149 })
150 }
151
152 fn size_hint(&self) -> (usize, Option<usize>) {
153 let len = self.data.len() / RUNTIME_FUNCTION_SIZE;
154 (len, Some(len))
155 }
156}
157
158impl FusedIterator for RuntimeFunctionIterator<'_> {}
159impl ExactSizeIterator for RuntimeFunctionIterator<'_> {}
160
161/// An x64 register used during unwinding.
162///
163/// - `0` - `15`: General purpose registers
164/// - `17` - `32`: XMM registers
165#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
166pub struct Register(pub u8);
167
168impl Register {
169 fn xmm(number: u8) -> Self {
170 Register(number + 17)
171 }
172
173 /// Returns the x64 register name.
174 pub fn name(self) -> &'static str {
175 match self.0 {
176 0 => "$rax",
177 1 => "$rcx",
178 2 => "$rdx",
179 3 => "$rbx",
180 4 => "$rsp",
181 5 => "$rbp",
182 6 => "$rsi",
183 7 => "$rdi",
184 8 => "$r8",
185 9 => "$r9",
186 10 => "$r10",
187 11 => "$r11",
188 12 => "$r12",
189 13 => "$r13",
190 14 => "$r14",
191 15 => "$r15",
192 16 => "$rip",
193 17 => "$xmm0",
194 18 => "$xmm1",
195 19 => "$xmm2",
196 20 => "$xmm3",
197 21 => "$xmm4",
198 22 => "$xmm5",
199 23 => "$xmm6",
200 24 => "$xmm7",
201 25 => "$xmm8",
202 26 => "$xmm9",
203 27 => "$xmm10",
204 28 => "$xmm11",
205 29 => "$xmm12",
206 30 => "$xmm13",
207 31 => "$xmm14",
208 32 => "$xmm15",
209 _ => "",
210 }
211 }
212}
213
214/// An unsigned offset to a value in the local stack frame.
215#[derive(Clone, Copy, Debug, Eq, PartialEq)]
216pub enum StackFrameOffset {
217 /// Offset from the current RSP, that is, the lowest address of the fixed stack allocation.
218 ///
219 /// To restore this register, read the value at the given offset from the RSP.
220 RSP(u32),
221
222 /// Offset from the value of the frame pointer register.
223 ///
224 /// To restore this register, read the value at the given offset from the FP register, reduced
225 /// by the `frame_register_offset` value specified in the `UnwindInfo` structure. By definition,
226 /// the frame pointer register is any register other than RAX (`0`).
227 FP(u32),
228}
229
230impl StackFrameOffset {
231 fn with_ctx(offset: u32, ctx: UnwindOpContext) -> Self {
232 match ctx.frame_register {
233 Register(0) => StackFrameOffset::RSP(offset),
234 Register(_) => StackFrameOffset::FP(offset),
235 }
236 }
237}
238
239impl fmt::Display for Register {
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 f.write_str(self.name())
242 }
243}
244
245/// An unwind operation corresponding to code in the function prolog.
246///
247/// Unwind operations can be used to reverse the effects of the function prolog and restore register
248/// values of parent stack frames that have been saved to the stack.
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
250pub enum UnwindOperation {
251 /// Push a nonvolatile integer register, decrementing `RSP` by 8.
252 PushNonVolatile(Register),
253
254 /// Allocate a fixed-size area on the stack.
255 Alloc(u32),
256
257 /// Establish the frame pointer register by setting the register to some offset of the current
258 /// RSP. The use of an offset permits establishing a frame pointer that points to the middle of
259 /// the fixed stack allocation, helping code density by allowing more accesses to use short
260 /// instruction forms.
261 SetFPRegister,
262
263 /// Save a nonvolatile integer register on the stack using a MOV instead of a PUSH. This code is
264 /// primarily used for shrink-wrapping, where a nonvolatile register is saved to the stack in a
265 /// position that was previously allocated.
266 SaveNonVolatile(Register, StackFrameOffset),
267
268 /// Save the lower 64 bits of a nonvolatile XMM register on the stack.
269 SaveXMM(Register, StackFrameOffset),
270
271 /// Describes the function epilog.
272 ///
273 /// This operation has been introduced with unwind info version 2 and is not implemented yet.
274 Epilog,
275
276 /// Save all 128 bits of a nonvolatile XMM register on the stack.
277 SaveXMM128(Register, StackFrameOffset),
278
279 /// Push a machine frame. This is used to record the effect of a hardware interrupt or
280 /// exception. Depending on the error flag, this frame has two different layouts.
281 ///
282 /// This unwind code always appears in a dummy prolog, which is never actually executed but
283 /// instead appears before the real entry point of an interrupt routine, and exists only to
284 /// provide a place to simulate the push of a machine frame. This operation records that
285 /// simulation, which indicates the machine has conceptually done this:
286 ///
287 /// 1. Pop RIP return address from top of stack into `temp`
288 /// 2. `$ss`, Push old `$rsp`, `$rflags`, `$cs`, `temp`
289 /// 3. If error flag is `true`, push the error code
290 ///
291 /// Without an error code, RSP was incremented by `40` and the following was frame pushed:
292 ///
293 /// Offset | Value
294 /// ---------|--------
295 /// RSP + 32 | `$ss`
296 /// RSP + 24 | old `$rsp`
297 /// RSP + 16 | `$rflags`
298 /// RSP + 8 | `$cs`
299 /// RSP + 0 | `$rip`
300 ///
301 /// With an error code, RSP was incremented by `48` and the following was frame pushed:
302 ///
303 /// Offset | Value
304 /// ---------|--------
305 /// RSP + 40 | `$ss`
306 /// RSP + 32 | old `$rsp`
307 /// RSP + 24 | `$rflags`
308 /// RSP + 16 | `$cs`
309 /// RSP + 8 | `$rip`
310 /// RSP + 0 | error code
311 PushMachineFrame(bool),
312
313 /// A reserved operation without effect.
314 Noop,
315}
316
317/// Context used to parse unwind operation.
318#[derive(Clone, Copy, Debug, PartialEq)]
319struct UnwindOpContext {
320 /// Version of the unwind info.
321 version: u8,
322
323 /// The nonvolatile register used as the frame pointer of this function.
324 ///
325 /// If this register is non-zero, all stack frame offsets used in unwind operations are of type
326 /// `StackFrameOffset::FP`. When loading these offsets, they have to be based off the value of
327 /// this frame register instead of the conventional RSP. This allows the RSP to be modified.
328 frame_register: Register,
329}
330
331/// An unwind operation that is executed at a particular place in the function prolog.
332#[derive(Clone, Copy, Debug, Eq, PartialEq)]
333pub struct UnwindCode {
334 /// Offset of the corresponding instruction in the function prolog.
335 ///
336 /// To be precise, this is the offset from the beginning of the prolog of the end of the
337 /// instruction that performs this operation, plus 1 (that is, the offset of the start of the
338 /// next instruction).
339 ///
340 /// Unwind codes are ordered by this offset in reverse order, suitable for unwinding.
341 pub code_offset: u8,
342
343 /// The operation that was performed by the code in the prolog.
344 pub operation: UnwindOperation,
345}
346
347impl<'a> TryFromCtx<'a, UnwindOpContext> for UnwindCode {
348 type Error = error::Error;
349 #[inline]
350 fn try_from_ctx(bytes: &'a [u8], ctx: UnwindOpContext) -> Result<(Self, usize), Self::Error> {
351 let mut read = 0;
352 let code_offset = bytes.gread_with::<u8>(&mut read, scroll::LE)?;
353 let operation = bytes.gread_with::<u8>(&mut read, scroll::LE)?;
354
355 let operation_code = operation & 0xf;
356 let operation_info = operation >> 4;
357
358 let operation = match operation_code {
359 self::UWOP_PUSH_NONVOL => {
360 let register = Register(operation_info);
361 UnwindOperation::PushNonVolatile(register)
362 }
363 self::UWOP_ALLOC_LARGE => {
364 let offset = match operation_info {
365 0 => u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 8,
366 1 => bytes.gread_with::<u32>(&mut read, scroll::LE)?,
367 i => {
368 let msg = format!("invalid op info ({}) for UWOP_ALLOC_LARGE", i);
369 return Err(error::Error::Malformed(msg));
370 }
371 };
372 UnwindOperation::Alloc(offset)
373 }
374 self::UWOP_ALLOC_SMALL => {
375 let offset = u32::from(operation_info) * 8 + 8;
376 UnwindOperation::Alloc(offset)
377 }
378 self::UWOP_SET_FPREG => UnwindOperation::SetFPRegister,
379 self::UWOP_SAVE_NONVOL => {
380 let register = Register(operation_info);
381 let offset = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 8;
382 UnwindOperation::SaveNonVolatile(register, StackFrameOffset::with_ctx(offset, ctx))
383 }
384 self::UWOP_SAVE_NONVOL_FAR => {
385 let register = Register(operation_info);
386 let offset = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
387 UnwindOperation::SaveNonVolatile(register, StackFrameOffset::with_ctx(offset, ctx))
388 }
389 self::UWOP_EPILOG => {
390 let data = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 16;
391 if ctx.version == 1 {
392 let register = Register::xmm(operation_info);
393 UnwindOperation::SaveXMM(register, StackFrameOffset::with_ctx(data, ctx))
394 } else {
395 // TODO: See https://weekly-geekly.github.io/articles/322956/index.html
396 UnwindOperation::Epilog
397 }
398 }
399 self::UWOP_SPARE_CODE => {
400 let data = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
401 if ctx.version == 1 {
402 let register = Register::xmm(operation_info);
403 UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(data, ctx))
404 } else {
405 UnwindOperation::Noop
406 }
407 }
408 self::UWOP_SAVE_XMM128 => {
409 let register = Register::xmm(operation_info);
410 let offset = u32::from(bytes.gread_with::<u16>(&mut read, scroll::LE)?) * 16;
411 UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(offset, ctx))
412 }
413 self::UWOP_SAVE_XMM128_FAR => {
414 let register = Register::xmm(operation_info);
415 let offset = bytes.gread_with::<u32>(&mut read, scroll::LE)?;
416 UnwindOperation::SaveXMM128(register, StackFrameOffset::with_ctx(offset, ctx))
417 }
418 self::UWOP_PUSH_MACHFRAME => {
419 let is_error = match operation_info {
420 0 => false,
421 1 => true,
422 i => {
423 let msg = format!("invalid op info ({}) for UWOP_PUSH_MACHFRAME", i);
424 return Err(error::Error::Malformed(msg));
425 }
426 };
427 UnwindOperation::PushMachineFrame(is_error)
428 }
429 op => {
430 let msg = format!("unknown unwind op code ({})", op);
431 return Err(error::Error::Malformed(msg));
432 }
433 };
434
435 let code = UnwindCode {
436 code_offset,
437 operation,
438 };
439
440 Ok((code, read))
441 }
442}
443
444/// An iterator over unwind codes for a function or part of a function, returned from
445/// [`UnwindInfo`].
446///
447/// [`UnwindInfo`]: struct.UnwindInfo.html
448#[derive(Clone, Debug)]
449pub struct UnwindCodeIterator<'a> {
450 bytes: &'a [u8],
451 offset: usize,
452 context: UnwindOpContext,
453}
454
455impl Iterator for UnwindCodeIterator<'_> {
456 type Item = error::Result<UnwindCode>;
457
458 fn next(&mut self) -> Option<Self::Item> {
459 if self.offset >= self.bytes.len() {
460 return None;
461 }
462
463 Some(self.bytes.gread_with(&mut self.offset, self.context))
464 }
465
466 fn size_hint(&self) -> (usize, Option<usize>) {
467 let upper = (self.bytes.len() - self.offset) / UNWIND_CODE_SIZE;
468 // the largest codes take up three slots
469 let lower = (upper + 3 - (upper % 3)) / 3;
470 (lower, Some(upper))
471 }
472}
473
474impl FusedIterator for UnwindCodeIterator<'_> {}
475
476/// A language-specific handler that is called as part of the search for an exception handler or as
477/// part of an unwind.
478#[derive(Copy, Clone, Debug, PartialEq)]
479pub enum UnwindHandler<'a> {
480 /// The image-relative address of an exception handler and its implementation-defined data.
481 ExceptionHandler(u32, &'a [u8]),
482 /// The image-relative address of a termination handler and its implementation-defined data.
483 TerminationHandler(u32, &'a [u8]),
484}
485
486/// Unwind information for a function or portion of a function.
487///
488/// The unwind info structure is used to record the effects a function has on the stack pointer and
489/// where the nonvolatile registers are saved on the stack. The unwind codes can be enumerated with
490/// [`unwind_codes`].
491///
492/// This unwind info might only be secondary information, and link to a [chained unwind handler].
493/// For unwinding, this link shall be followed until the root unwind info record has been resolved.
494///
495/// [`unwind_codes`]: struct.UnwindInfo.html#method.unwind_codes
496/// [chained unwind handler]: struct.UnwindInfo.html#structfield.chained_info
497#[derive(Clone)]
498pub struct UnwindInfo<'a> {
499 /// Version of this unwind info.
500 pub version: u8,
501
502 /// Length of the function prolog in bytes.
503 pub size_of_prolog: u8,
504
505 /// The nonvolatile register used as the frame pointer of this function.
506 ///
507 /// If this register is non-zero, all stack frame offsets used in unwind operations are of type
508 /// `StackFrameOffset::FP`. When loading these offsets, they have to be based off the value of
509 /// this frame register instead of the conventional RSP. This allows the RSP to be modified.
510 pub frame_register: Register,
511
512 /// Offset from RSP that is applied to the FP register when it is established.
513 ///
514 /// When loading offsets of type `StackFrameOffset::FP` from the stack, this offset has to be
515 /// subtracted before loading the value since the actual RSP was lower by that amount in the
516 /// prolog.
517 pub frame_register_offset: u32,
518
519 /// A record pointing to chained unwind information.
520 ///
521 /// If chained unwind info is present, then this unwind info is a secondary one and the linked
522 /// unwind info contains primary information. Chained info is useful in two situations. First,
523 /// it is used for noncontiguous code segments. Second, this mechanism is sometimes used to
524 /// group volatile register saves.
525 ///
526 /// The referenced unwind info can itself specify chained unwind information, until it arrives
527 /// at the root unwind info. Generally, the entire chain should be considered when unwinding.
528 pub chained_info: Option<RuntimeFunction>,
529
530 /// An exception or termination handler called as part of the unwind.
531 pub handler: Option<UnwindHandler<'a>>,
532
533 /// A list of unwind codes, sorted descending by code offset.
534 code_bytes: &'a [u8],
535}
536
537impl<'a> UnwindInfo<'a> {
538 /// Parses unwind information from the image at the given offset.
539 pub fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> {
540 // Read the version and flags fields, which are combined into a single byte.
541 let version_flags: u8 = bytes.gread_with(&mut offset, scroll::LE)?;
542 let version = version_flags & 0b111;
543 let flags = version_flags >> 3;
544
545 if version < 1 || version > 2 {
546 let msg = format!("unsupported unwind code version ({})", version);
547 return Err(error::Error::Malformed(msg));
548 }
549
550 let size_of_prolog = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
551 let count_of_codes = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
552
553 // Parse the frame register and frame register offset values, that are combined into a
554 // single byte.
555 let frame_info = bytes.gread_with::<u8>(&mut offset, scroll::LE)?;
556 // If nonzero, then the function uses a frame pointer (FP), and this field is the number
557 // of the nonvolatile register used as the frame pointer. The zero register value does
558 // not need special casing since it will not be referenced by the unwind operations.
559 let frame_register = Register(frame_info & 0xf);
560 // The the scaled offset from RSP that is applied to the FP register when it's
561 // established. The actual FP register is set to RSP + 16 * this number, allowing
562 // offsets from 0 to 240.
563 let frame_register_offset = u32::from((frame_info >> 4) * 16);
564
565 // An array of items that explains the effect of the prolog on the nonvolatile registers and
566 // RSP. Some unwind codes require more than one slot in the array.
567 let codes_size = count_of_codes as usize * UNWIND_CODE_SIZE;
568 let code_bytes = bytes.gread_with(&mut offset, codes_size)?;
569
570 // For alignment purposes, the codes array always has an even number of entries, and the
571 // final entry is potentially unused. In that case, the array is one longer than indicated
572 // by the count of unwind codes field.
573 if count_of_codes % 2 != 0 {
574 offset += 2;
575 }
576 debug_assert!(offset % 4 == 0);
577
578 let mut chained_info = None;
579 let mut handler = None;
580
581 // If flag UNW_FLAG_CHAININFO is set then the UNWIND_INFO structure ends with three UWORDs.
582 // These UWORDs represent the RUNTIME_FUNCTION information for the function of the chained
583 // unwind.
584 if flags & UNW_FLAG_CHAININFO != 0 {
585 chained_info = Some(bytes.gread_with(&mut offset, scroll::LE)?);
586
587 // The relative address of the language-specific handler is present in the UNWIND_INFO
588 // whenever flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER are set. The language-specific
589 // handler is called as part of the search for an exception handler or as part of an unwind.
590 } else if flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER) != 0 {
591 let address = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
592 let data = &bytes[offset..];
593
594 handler = Some(if flags & UNW_FLAG_EHANDLER != 0 {
595 UnwindHandler::ExceptionHandler(address, data)
596 } else {
597 UnwindHandler::TerminationHandler(address, data)
598 });
599 }
600
601 Ok(UnwindInfo {
602 version,
603 size_of_prolog,
604 frame_register,
605 frame_register_offset,
606 chained_info,
607 handler,
608 code_bytes,
609 })
610 }
611
612 /// Returns an iterator over unwind codes in this unwind info.
613 ///
614 /// Unwind codes are iterated in descending `code_offset` order suitable for unwinding. If the
615 /// optional [`chained_info`](Self::chained_info) is present, codes of that unwind info should be interpreted
616 /// immediately afterwards.
617 pub fn unwind_codes(&self) -> UnwindCodeIterator<'a> {
618 UnwindCodeIterator {
619 bytes: self.code_bytes,
620 offset: 0,
621 context: UnwindOpContext {
622 version: self.version,
623 frame_register: self.frame_register,
624 },
625 }
626 }
627}
628
629impl fmt::Debug for UnwindInfo<'_> {
630 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
631 let count_of_codes = self.code_bytes.len() / UNWIND_CODE_SIZE;
632
633 f.debug_struct("UnwindInfo")
634 .field("version", &self.version)
635 .field("size_of_prolog", &self.size_of_prolog)
636 .field("frame_register", &self.frame_register)
637 .field("frame_register_offset", &self.frame_register_offset)
638 .field("count_of_codes", &count_of_codes)
639 .field("chained_info", &self.chained_info)
640 .field("handler", &self.handler)
641 .finish()
642 }
643}
644
645impl<'a> IntoIterator for &'_ UnwindInfo<'a> {
646 type Item = error::Result<UnwindCode>;
647 type IntoIter = UnwindCodeIterator<'a>;
648
649 #[inline]
650 fn into_iter(self) -> Self::IntoIter {
651 self.unwind_codes()
652 }
653}
654
655/// Exception handling and stack unwind information for functions in the image.
656pub struct ExceptionData<'a> {
657 bytes: &'a [u8],
658 offset: usize,
659 size: usize,
660 file_alignment: u32,
661}
662
663impl<'a> ExceptionData<'a> {
664 /// Parses exception data from the image at the given offset.
665 pub fn parse(
666 bytes: &'a [u8],
667 directory: data_directories::DataDirectory,
668 sections: &[section_table::SectionTable],
669 file_alignment: u32,
670 ) -> error::Result<Self> {
671 Self::parse_with_opts(
672 bytes,
673 directory,
674 sections,
675 file_alignment,
676 &options::ParseOptions::default(),
677 )
678 }
679
680 /// Parses exception data from the image at the given offset.
681 pub fn parse_with_opts(
682 bytes: &'a [u8],
683 directory: data_directories::DataDirectory,
684 sections: &[section_table::SectionTable],
685 file_alignment: u32,
686 opts: &options::ParseOptions,
687 ) -> error::Result<Self> {
688 let size = directory.size as usize;
689
690 if size % RUNTIME_FUNCTION_SIZE != 0 {
691 return Err(error::Error::from(scroll::Error::BadInput {
692 size,
693 msg: "invalid exception directory table size",
694 }));
695 }
696
697 let rva = directory.virtual_address as usize;
698 let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
699 error::Error::Malformed(format!("cannot map exception_rva ({:#x}) into offset", rva))
700 })?;
701
702 if offset % 4 != 0 {
703 return Err(error::Error::from(scroll::Error::BadOffset(offset)));
704 }
705
706 Ok(ExceptionData {
707 bytes,
708 offset,
709 size,
710 file_alignment,
711 })
712 }
713
714 /// The number of function entries described by this exception data.
715 pub fn len(&self) -> usize {
716 self.size / RUNTIME_FUNCTION_SIZE
717 }
718
719 /// Indicating whether there are functions in this entry.
720 pub fn is_empty(&self) -> bool {
721 self.len() == 0
722 }
723
724 /// Iterates all function entries in order of their code offset.
725 ///
726 /// To search for a function by relative instruction address, use [`find_function`]. To resolve
727 /// unwind information, use [`get_unwind_info`].
728 ///
729 /// [`find_function`]: struct.ExceptionData.html#method.find_function
730 /// [`get_unwind_info`]: struct.ExceptionData.html#method.get_unwind_info
731 pub fn functions(&self) -> RuntimeFunctionIterator<'a> {
732 RuntimeFunctionIterator {
733 data: &self.bytes[self.offset..self.offset + self.size],
734 }
735 }
736
737 /// Returns the function at the given index.
738 pub fn get_function(&self, index: usize) -> error::Result<RuntimeFunction> {
739 self.get_function_by_offset(self.offset + index * RUNTIME_FUNCTION_SIZE)
740 }
741
742 /// Performs a binary search to find a function entry covering the given RVA relative to the
743 /// image.
744 pub fn find_function(&self, rva: u32) -> error::Result<Option<RuntimeFunction>> {
745 // NB: Binary search implementation copied from std::slice::binary_search_by and adapted.
746 // Theoretically, there should be nothing that causes parsing runtime functions to fail and
747 // all access to the bytes buffer is guaranteed to be in range. However, since all other
748 // functions also return Results, this is much more ergonomic here.
749
750 let mut size = self.len();
751 if size == 0 {
752 return Ok(None);
753 }
754
755 let mut base = 0;
756 while size > 1 {
757 let half = size / 2;
758 let mid = base + half;
759 let offset = self.offset + mid * RUNTIME_FUNCTION_SIZE;
760 let addr = self.bytes.pread_with::<u32>(offset, scroll::LE)?;
761 base = if addr > rva { base } else { mid };
762 size -= half;
763 }
764
765 let offset = self.offset + base * RUNTIME_FUNCTION_SIZE;
766 let addr = self.bytes.pread_with::<u32>(offset, scroll::LE)?;
767 let function = match addr.cmp(&rva) {
768 Ordering::Less | Ordering::Equal => self.get_function(base)?,
769 Ordering::Greater if base == 0 => return Ok(None),
770 Ordering::Greater => self.get_function(base - 1)?,
771 };
772
773 if function.end_address > rva {
774 Ok(Some(function))
775 } else {
776 Ok(None)
777 }
778 }
779
780 /// Resolves unwind information for the given function entry.
781 pub fn get_unwind_info(
782 &self,
783 function: RuntimeFunction,
784 sections: &[section_table::SectionTable],
785 ) -> error::Result<UnwindInfo<'a>> {
786 self.get_unwind_info_with_opts(function, sections, &options::ParseOptions::default())
787 }
788
789 /// Resolves unwind information for the given function entry.
790 pub fn get_unwind_info_with_opts(
791 &self,
792 mut function: RuntimeFunction,
793 sections: &[section_table::SectionTable],
794 opts: &options::ParseOptions,
795 ) -> error::Result<UnwindInfo<'a>> {
796 while function.unwind_info_address % 2 != 0 {
797 let rva = (function.unwind_info_address & !1) as usize;
798 function = self.get_function_by_rva_with_opts(rva, sections, opts)?;
799 }
800
801 let rva = function.unwind_info_address as usize;
802 let offset =
803 utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
804 error::Error::Malformed(format!("cannot map unwind rva ({:#x}) into offset", rva))
805 })?;
806
807 UnwindInfo::parse(self.bytes, offset)
808 }
809
810 #[allow(dead_code)]
811 fn get_function_by_rva(
812 &self,
813 rva: usize,
814 sections: &[section_table::SectionTable],
815 ) -> error::Result<RuntimeFunction> {
816 self.get_function_by_rva_with_opts(rva, sections, &options::ParseOptions::default())
817 }
818
819 fn get_function_by_rva_with_opts(
820 &self,
821 rva: usize,
822 sections: &[section_table::SectionTable],
823 opts: &options::ParseOptions,
824 ) -> error::Result<RuntimeFunction> {
825 let offset =
826 utils::find_offset(rva, sections, self.file_alignment, opts).ok_or_else(|| {
827 error::Error::Malformed(format!(
828 "cannot map exception rva ({:#x}) into offset",
829 rva
830 ))
831 })?;
832
833 self.get_function_by_offset(offset)
834 }
835
836 #[inline]
837 fn get_function_by_offset(&self, offset: usize) -> error::Result<RuntimeFunction> {
838 debug_assert!((offset - self.offset) % RUNTIME_FUNCTION_SIZE == 0);
839 debug_assert!(offset < self.offset + self.size);
840
841 Ok(self.bytes.pread_with(offset, scroll::LE)?)
842 }
843}
844
845impl fmt::Debug for ExceptionData<'_> {
846 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
847 f.debug_struct("ExceptionData")
848 .field("file_alignment", &self.file_alignment)
849 .field("offset", &format_args!("{:#x}", self.offset))
850 .field("size", &format_args!("{:#x}", self.size))
851 .field("len", &self.len())
852 .finish()
853 }
854}
855
856impl<'a> IntoIterator for &'_ ExceptionData<'a> {
857 type Item = error::Result<RuntimeFunction>;
858 type IntoIter = RuntimeFunctionIterator<'a>;
859
860 #[inline]
861 fn into_iter(self) -> Self::IntoIter {
862 self.functions()
863 }
864}
865
866#[cfg(test)]
867mod tests {
868 use super::*;
869
870 #[test]
871 fn test_size_of_runtime_function() {
872 assert_eq!(
873 std::mem::size_of::<RuntimeFunction>(),
874 RUNTIME_FUNCTION_SIZE
875 );
876 }
877
878 // Tests disabled until there is a solution for handling binary test data
879 // See https://github.com/m4b/goblin/issues/185
880
881 // macro_rules! microsoft_symbol {
882 // ($name:literal, $id:literal) => {{
883 // use std::fs::File;
884 // use std::path::Path;
885
886 // let path = Path::new(concat!("cache/", $name));
887 // if !path.exists() {
888 // let url = format!(
889 // "https://msdl.microsoft.com/download/symbols/{}/{}/{}",
890 // $name, $id, $name
891 // );
892
893 // let mut response = reqwest::get(&url).expect(concat!("get ", $name));
894 // let mut target = File::create(path).expect(concat!("create ", $name));
895 // response
896 // .copy_to(&mut target)
897 // .expect(concat!("download ", $name));
898 // }
899
900 // std::fs::read(path).expect(concat!("open ", $name))
901 // }};
902 // }
903
904 // lazy_static::lazy_static! {
905 // static ref PE_DATA: Vec<u8> = microsoft_symbol!("WSHTCPIP.DLL", "4a5be0b77000");
906 // }
907
908 // #[test]
909 // fn test_parse() {
910 // let pe = PE::parse(&PE_DATA).expect("parse PE");
911 // let exception_data = pe.exception_data.expect("get exception data");
912
913 // assert_eq!(exception_data.len(), 19);
914 // assert!(!exception_data.is_empty());
915 // }
916
917 // #[test]
918 // fn test_iter_functions() {
919 // let pe = PE::parse(&PE_DATA).expect("parse PE");
920 // let exception_data = pe.exception_data.expect("get exception data");
921
922 // let functions: Vec<RuntimeFunction> = exception_data
923 // .functions()
924 // .map(|result| result.expect("parse runtime function"))
925 // .collect();
926
927 // assert_eq!(functions.len(), 19);
928
929 // let expected = RuntimeFunction {
930 // begin_address: 0x1355,
931 // end_address: 0x1420,
932 // unwind_info_address: 0x4019,
933 // };
934
935 // assert_eq!(functions[4], expected);
936 // }
937
938 // #[test]
939 // fn test_get_function() {
940 // let pe = PE::parse(&PE_DATA).expect("parse PE");
941 // let exception_data = pe.exception_data.expect("get exception data");
942
943 // let expected = RuntimeFunction {
944 // begin_address: 0x1355,
945 // end_address: 0x1420,
946 // unwind_info_address: 0x4019,
947 // };
948
949 // assert_eq!(
950 // exception_data.get_function(4).expect("find function"),
951 // expected
952 // );
953 // }
954
955 // #[test]
956 // fn test_find_function() {
957 // let pe = PE::parse(&PE_DATA).expect("parse PE");
958 // let exception_data = pe.exception_data.expect("get exception data");
959
960 // let expected = RuntimeFunction {
961 // begin_address: 0x1355,
962 // end_address: 0x1420,
963 // unwind_info_address: 0x4019,
964 // };
965
966 // assert_eq!(
967 // exception_data.find_function(0x1400).expect("find function"),
968 // Some(expected)
969 // );
970 // }
971
972 // #[test]
973 // fn test_find_function_none() {
974 // let pe = PE::parse(&PE_DATA).expect("parse PE");
975 // let exception_data = pe.exception_data.expect("get exception data");
976
977 // // 0x1d00 is the end address of the last function.
978
979 // assert_eq!(
980 // exception_data.find_function(0x1d00).expect("find function"),
981 // None
982 // );
983 // }
984
985 // #[test]
986 // fn test_get_unwind_info() {
987 // let pe = PE::parse(&PE_DATA).expect("parse PE");
988 // let exception_data = pe.exception_data.expect("get exception data");
989
990 // // runtime function #0 directly refers to unwind info
991 // let rt_function = RuntimeFunction {
992 // begin_address: 0x1010,
993 // end_address: 0x1090,
994 // unwind_info_address: 0x25d8,
995 // };
996
997 // let unwind_info = exception_data
998 // .get_unwind_info(rt_function, &pe.sections)
999 // .expect("get unwind info");
1000
1001 // // Unwind codes just used to assert that the right unwind info was resolved
1002 // let expected = &[4, 98];
1003
1004 // assert_eq!(unwind_info.code_bytes, expected);
1005 // }
1006
1007 // #[test]
1008 // fn test_get_unwind_info_redirect() {
1009 // let pe = PE::parse(&PE_DATA).expect("parse PE");
1010 // let exception_data = pe.exception_data.expect("get exception data");
1011
1012 // // runtime function #4 has a redirect (unwind_info_address & 1).
1013 // let rt_function = RuntimeFunction {
1014 // begin_address: 0x1355,
1015 // end_address: 0x1420,
1016 // unwind_info_address: 0x4019,
1017 // };
1018
1019 // let unwind_info = exception_data
1020 // .get_unwind_info(rt_function, &pe.sections)
1021 // .expect("get unwind info");
1022
1023 // // Unwind codes just used to assert that the right unwind info was resolved
1024 // let expected = &[
1025 // 28, 100, 15, 0, 28, 84, 14, 0, 28, 52, 12, 0, 28, 82, 24, 240, 22, 224, 20, 208, 18,
1026 // 192, 16, 112,
1027 // ];
1028
1029 // assert_eq!(unwind_info.code_bytes, expected);
1030 // }
1031
1032 #[test]
1033 fn test_iter_unwind_codes() {
1034 let unwind_info = UnwindInfo {
1035 version: 1,
1036 size_of_prolog: 4,
1037 frame_register: Register(0),
1038 frame_register_offset: 0,
1039 chained_info: None,
1040 handler: None,
1041 code_bytes: &[4, 98],
1042 };
1043
1044 let unwind_codes: Vec<UnwindCode> = unwind_info
1045 .unwind_codes()
1046 .map(|result| result.expect("parse unwind code"))
1047 .collect();
1048
1049 assert_eq!(unwind_codes.len(), 1);
1050
1051 let expected = UnwindCode {
1052 code_offset: 4,
1053 operation: UnwindOperation::Alloc(56),
1054 };
1055
1056 assert_eq!(unwind_codes[0], expected);
1057 }
1058}