github.com/saferwall/pe@v1.5.2/exception.go (about)

     1  // Copyright 2018 Saferwall. All rights reserved.
     2  // Use of this source code is governed by Apache v2 license
     3  // license that can be found in the LICENSE file.
     4  
     5  package pe
     6  
     7  import (
     8  	"encoding/binary"
     9  	"strconv"
    10  )
    11  
    12  const (
    13  	// Unwind information flags.
    14  
    15  	// UnwFlagNHandler - The function has no handler.
    16  	UnwFlagNHandler = uint8(0x0)
    17  
    18  	// UnwFlagEHandler - The function has an exception handler that should
    19  	// be called when looking for functions that need to examine exceptions.
    20  	UnwFlagEHandler = uint8(0x1)
    21  
    22  	// UnwFlagUHandler - The function has a termination handler that should
    23  	// be called when unwinding an exception.
    24  	UnwFlagUHandler = uint8(0x2)
    25  
    26  	// UnwFlagChainInfo - This unwind info structure is not the primary one
    27  	// for the procedure. Instead, the chained unwind info entry is the contents
    28  	// of a previous RUNTIME_FUNCTION entry. For information, see Chained unwind
    29  	// info structures. If this flag is set, then the UNW_FLAG_EHANDLER and
    30  	// UNW_FLAG_UHANDLER flags must be cleared. Also, the frame register and
    31  	// fixed-stack allocation field must have the same values as in the primary
    32  	// unwind info.
    33  	UnwFlagChainInfo = uint8(0x4)
    34  )
    35  
    36  // The meaning of the operation info bits depends upon the operation code.
    37  // To encode a general-purpose (integer) register, this mapping is used:
    38  const (
    39  	rax = iota
    40  	rcx
    41  	rdx
    42  	rbx
    43  	rsp
    44  	rbp
    45  	rsi
    46  	rdi
    47  	r8
    48  	r9
    49  	r10
    50  	r11
    51  	r12
    52  	r13
    53  	r14
    54  	r15
    55  )
    56  
    57  // OpInfoRegisters maps registers to string.
    58  var OpInfoRegisters = map[uint8]string{
    59  	rax: "RAX",
    60  	rcx: "RCX",
    61  	rdx: "RDX",
    62  	rbx: "RBX",
    63  	rsp: "RSP",
    64  	rbp: "RBP",
    65  	rsi: "RSI",
    66  	rdi: "RDI",
    67  	r8:  "R8",
    68  	r9:  "R9",
    69  	r10: "R10",
    70  	r11: "R11",
    71  	r12: "R12",
    72  	r13: "R13",
    73  	r14: "R14",
    74  	r15: "R15",
    75  }
    76  
    77  // UnwindOpType represents the type of an unwind opcode.
    78  type UnwindOpType uint8
    79  
    80  // _UNWIND_OP_CODES
    81  const (
    82  	// Push a nonvolatile integer register, decrementing RSP by 8. The
    83  	// operation info is the number of the register. Because of the constraints
    84  	// on epilogs, UWOP_PUSH_NONVOL unwind codes must appear first in the
    85  	// prolog and correspondingly, last in the unwind code array. This relative
    86  	// ordering applies to all other unwind codes except UWOP_PUSH_MACHFRAME.
    87  	UwOpPushNonVol = UnwindOpType(0)
    88  
    89  	// Allocate a large-sized area on the stack. There are two forms. If the
    90  	// operation info equals 0, then the size of the allocation divided by 8 is
    91  	// recorded in the next slot, allowing an allocation up to 512K - 8. If the
    92  	// operation info equals 1, then the unscaled size of the allocation is
    93  	// recorded in the next two slots in little-endian format, allowing
    94  	// allocations up to 4GB - 8.
    95  	UwOpAllocLarge = UnwindOpType(1)
    96  
    97  	// Allocate a small-sized area on the stack. The size of the allocation is
    98  	// the operation info field * 8 + 8, allowing allocations from 8 to 128
    99  	// bytes.
   100  	UwOpAllocSmall = UnwindOpType(2)
   101  
   102  	// Establish the frame pointer register by setting the register to some
   103  	// offset of the current RSP. The offset is equal to the Frame Register
   104  	// offset (scaled) field in the UNWIND_INFO * 16, allowing offsets from 0
   105  	// to 240. The use of an offset permits establishing a frame pointer that
   106  	// points to the middle of the fixed stack allocation, helping code density
   107  	// by allowing more accesses to use short instruction forms. The operation
   108  	// info field is reserved and shouldn't be used.
   109  	UwOpSetFpReg = UnwindOpType(3)
   110  
   111  	// Save a nonvolatile integer register on the stack using a MOV instead of
   112  	// a PUSH. This code is primarily used for shrink-wrapping, where a
   113  	// nonvolatile register is saved to the stack in a position that was
   114  	// previously allocated. The operation info is the number of the register.
   115  	// The scaled-by-8 stack offset is recorded in the next unwind operation
   116  	// code slot, as described in the note above.
   117  	UwOpSaveNonVol = UnwindOpType(4)
   118  
   119  	// Save a nonvolatile integer register on the stack with a long offset,
   120  	// using a MOV instead of a PUSH. This code is primarily used for
   121  	// shrink-wrapping, where a nonvolatile register is saved to the stack in a
   122  	// position that was previously allocated. The operation info is the number
   123  	// of the register. The unscaled stack offset is recorded in the next two
   124  	// unwind operation code slots, as described in the note above.
   125  	UwOpSaveNonVolFar = UnwindOpType(5)
   126  
   127  	// For version 1 of the UNWIND_INFO structure, this code was called
   128  	// UWOP_SAVE_XMM and occupied 2 records, it retained the lower 64 bits of
   129  	// the XMM register, but was later removed and is now skipped. In practice,
   130  	// this code has never been used.
   131  	// For version 2 of the UNWIND_INFO structure, this code is called
   132  	// UWOP_EPILOG, takes 2 entries, and describes the function epilogue.
   133  	UwOpEpilog = UnwindOpType(6)
   134  
   135  	// For version 1 of the UNWIND_INFO structure, this code was called
   136  	// UWOP_SAVE_XMM_FAR and occupied 3 records, it saved the lower 64 bits of
   137  	// the XMM register, but was later removed and is now skipped. In practice,
   138  	// this code has never been used.
   139  	// For version 2 of the UNWIND_INFO structure, this code is called
   140  	// UWOP_SPARE_CODE, takes 3 entries, and makes no sense.
   141  	UwOpSpareCode = UnwindOpType(7)
   142  
   143  	// Save all 128 bits of a nonvolatile XMM register on the stack. The
   144  	// operation info is the number of the register. The scaled-by-16 stack
   145  	// offset is recorded in the next slot.
   146  	UwOpSaveXmm128 = UnwindOpType(8)
   147  
   148  	// Save all 128 bits of a nonvolatile XMM register on the stack with a long
   149  	// offset. The operation info is the number of the register. The unscaled
   150  	// stack offset is recorded in the next two slots.
   151  	UwOpSaveXmm128Far = UnwindOpType(9)
   152  
   153  	// Push a machine frame. This unwind code is used to record the effect of a
   154  	// hardware interrupt or exception.
   155  	UwOpPushMachFrame = UnwindOpType(10)
   156  
   157  	// UWOP_SET_FPREG_LARGE is a CLR Unix-only extension to the Windows AMD64
   158  	// unwind codes. It is not part of the standard Windows AMD64 unwind codes
   159  	// specification. UWOP_SET_FPREG allows for a maximum of a 240 byte offset
   160  	// between RSP and the frame pointer, when the frame pointer is
   161  	// established. UWOP_SET_FPREG_LARGE has a 32-bit range scaled by 16. When
   162  	// UWOP_SET_FPREG_LARGE is used, UNWIND_INFO.FrameRegister must be set to
   163  	// the frame pointer register, and UNWIND_INFO.FrameOffset must be set to
   164  	// 15 (its maximum value). UWOP_SET_FPREG_LARGE is followed by two
   165  	// UNWIND_CODEs that are combined to form a 32-bit offset (the same as
   166  	// UWOP_SAVE_NONVOL_FAR). This offset is then scaled by 16. The result must
   167  	// be less than 2^32 (that is, the top 4 bits of the unscaled 32-bit number
   168  	// must be zero). This result is used as the frame pointer register offset
   169  	// from RSP at the time the frame pointer is established. Either
   170  	// UWOP_SET_FPREG or UWOP_SET_FPREG_LARGE can be used, but not both.
   171  	UwOpSetFpRegLarge = UnwindOpType(11)
   172  )
   173  
   174  // ImageRuntimeFunctionEntry represents an entry in the function table on 64-bit
   175  // Windows (IMAGE_RUNTIME_FUNCTION_ENTRY). Table-based exception handling request
   176  // a table entry for all functions that allocate stack space or call another
   177  // function (for example, non-leaf functions).
   178  type ImageRuntimeFunctionEntry struct {
   179  	// The address of the start of the function.
   180  	BeginAddress uint32 `json:"begin_address"`
   181  
   182  	// The address of the end of the function.
   183  	EndAddress uint32 `json:"end_address"`
   184  
   185  	// The unwind data info structure is used to record the effects a function
   186  	// has on the stack pointer, and where the nonvolatile registers are saved
   187  	// on the stack.
   188  	UnwindInfoAddress uint32 `json:"unwind_info_address"`
   189  }
   190  
   191  // ImageARMRuntimeFunctionEntry represents the function table entry for the ARM
   192  // platform.
   193  type ImageARMRuntimeFunctionEntry struct {
   194  	// Function Start RVA is the 32-bit RVA of the start of the function. If
   195  	// the function contains thumb code, the low bit of this address must be set.
   196  	BeginAddress uint32 `bitfield:",functionstart" json:"begin_address"`
   197  
   198  	// Flag is a 2-bit field that indicates how to interpret the remaining
   199  	// 30 bits of the second .pdata word. If Flag is 0, then the remaining bits
   200  	// form an Exception Information RVA (with the low two bits implicitly 0).
   201  	// If Flag is non-zero, then the remaining bits form a Packed Unwind Data
   202  	// structure.
   203  	Flag uint8 `json:"flag"`
   204  
   205  	/* Exception Information RVA or Packed Unwind Data.
   206  
   207  	Exception Information RVA is the address of the variable-length exception
   208  	information structure, stored in the .xdata section.
   209  	This data must be 4-byte aligned.
   210  
   211  	Packed Unwind Data is a compressed description of the operations required
   212  	to unwind from a function, assuming a canonical form. In this case, no
   213  	.xdata record is required. */
   214  	ExceptionFlag uint32 `json:"exception_flag"`
   215  }
   216  
   217  // UnwindCode is used to record the sequence of operations in the prolog that
   218  // affect the nonvolatile registers and RSP. Each code item has this format:
   219  /* typedef union _UNWIND_CODE {
   220      struct {
   221          UCHAR CodeOffset;
   222          UCHAR UnwindOp : 4;
   223          UCHAR OpInfo : 4;
   224      } DUMMYUNIONNAME;
   225  
   226      struct {
   227          UCHAR OffsetLow;
   228          UCHAR UnwindOp : 4;
   229          UCHAR OffsetHigh : 4;
   230      } EpilogueCode;
   231  
   232      USHORT FrameOffset;
   233  } UNWIND_CODE, *PUNWIND_CODE;*/
   234  //
   235  // It provides information about the amount of stack space allocated, the location
   236  // of saved non-volatile registers, and whether or not a frame register is used
   237  // and what relation it has to the rest of the stack.
   238  type UnwindCode struct {
   239  	// Offset (from the beginning of the prolog) of the end of the instruction
   240  	// that performs is operation, plus 1 (that is, the offset of the start of
   241  	// the next instruction).
   242  	CodeOffset uint8 `json:"code_offset"`
   243  
   244  	// The unwind operation code.
   245  	UnwindOp UnwindOpType `json:"unwind_op"`
   246  
   247  	// Operation info.
   248  	OpInfo uint8 `json:"op_info"`
   249  
   250  	// Allocation size.
   251  	Operand     string `json:"operand"`
   252  	FrameOffset uint16 `json:"frame_offset"`
   253  }
   254  
   255  // UnwindInfo represents the _UNWIND_INFO structure. It is used to record the
   256  // effects a function has on the stack pointer, and where the nonvolatile
   257  // registers are saved on the stack.
   258  type UnwindInfo struct {
   259  	// (3 bits) Version number of the unwind data, currently 1 and 2.
   260  	Version uint8 `json:"version"`
   261  
   262  	// (5 bits) Three flags are currently defined above.
   263  	Flags uint8 `json:"flags"`
   264  
   265  	// Length of the function prolog in bytes.
   266  	SizeOfProlog uint8 `json:"size_of_prolog"`
   267  
   268  	// The number of slots in the unwind codes array. Some unwind codes,
   269  	// for example, UWOP_SAVE_NONVOL, require more than one slot in the array.
   270  	CountOfCodes uint8 `json:"count_of_codes"`
   271  
   272  	// If nonzero, then the function uses a frame pointer (FP), and this field
   273  	// is the number of the nonvolatile register used as the frame pointer,
   274  	// using the same encoding for the operation info field of UNWIND_CODE nodes.
   275  	FrameRegister uint8 `json:"frame_register"`
   276  
   277  	// If the frame register field is nonzero, this field is the scaled offset
   278  	// from RSP that is applied to the FP register when it's established. The
   279  	// actual FP register is set to RSP + 16 * this number, allowing offsets
   280  	// from 0 to 240. This offset permits pointing the FP register into the
   281  	// middle of the local stack allocation for dynamic stack frames, allowing
   282  	// better code density through shorter instructions. (That is, more
   283  	// instructions can use the 8-bit signed offset form.)
   284  	FrameOffset uint8 `json:"frame_offset"`
   285  
   286  	// An array of items that explains the effect of the prolog on the
   287  	// nonvolatile registers and RSP. See the section on UNWIND_CODE for the
   288  	// meanings of individual items. For alignment purposes, this array always
   289  	// has an even number of entries, and the final entry is potentially
   290  	// unused. In that case, the array is one longer than indicated by the
   291  	// count of unwind codes field.
   292  	UnwindCodes []UnwindCode `json:"unwind_codes"`
   293  
   294  	// Address of exception handler when UNW_FLAG_EHANDLER is set.
   295  	ExceptionHandler uint32 `json:"exception_handler"`
   296  
   297  	// If flag UNW_FLAG_CHAININFO is set, then the UNWIND_INFO structure ends
   298  	// with three UWORDs. These UWORDs represent the RUNTIME_FUNCTION
   299  	// information for the function of the chained unwind.
   300  	FunctionEntry ImageRuntimeFunctionEntry `json:"function_entry"`
   301  }
   302  
   303  //
   304  // The unwind codes are followed by an optional DWORD aligned field that
   305  // contains the exception handler address or the address of chained unwind
   306  // information. If an exception handler address is specified, then it is
   307  // followed by the language specified exception handler data.
   308  //
   309  //  union {
   310  //      ULONG ExceptionHandler;
   311  //      ULONG FunctionEntry;
   312  //  };
   313  //
   314  //  ULONG ExceptionData[];
   315  //
   316  
   317  type ScopeRecord struct {
   318  	// This value indicates the offset of the first instruction within a __try
   319  	// block located in the function.
   320  	BeginAddress uint32 `json:"begin_address"`
   321  
   322  	// This value indicates the offset to the instruction after the last
   323  	// instruction within the __try block (conceptually the __except statement).
   324  	EndAddress uint32 `json:"end_address"`
   325  
   326  	// This value indicates the offset to the function located within the
   327  	// parentheses of the __except() statement. In the documentation you'll
   328  	// find this routine called the "exception handler" or "exception filter".
   329  	HandlerAddress uint32 `json:"handler_address"`
   330  
   331  	// This value indicates the offset to the first instruction in the __except
   332  	// block associated with the __try block.
   333  	JumpTarget uint32 `json:"jump_target"`
   334  }
   335  
   336  // ScopeTable represents a variable length structure containing a count followed
   337  // by Count "scope records". While the RUNTIME_FUNCTION describes the entire range
   338  // of a function that contains SEH, the SCOPE_TABLE describes each of the individual
   339  // __try/__except blocks within the function.
   340  type ScopeTable struct {
   341  	// The count of scope records.
   342  	Count uint32 `json:"count"`
   343  
   344  	// A array of scope record.
   345  	ScopeRecords []ScopeRecord `json:"scope_records"`
   346  }
   347  
   348  //  typedef struct _SCOPE_TABLE {
   349  // 		ULONG Count;
   350  // 		struct
   351  // 		{
   352  // 			ULONG BeginAddress;
   353  // 			ULONG EndAddress;
   354  // 			ULONG HandlerAddress;
   355  // 			ULONG JumpTarget;
   356  // 		} ScopeRecord[1];
   357  //  } SCOPE_TABLE, *PSCOPE_TABLE;
   358  
   359  // Exception represent an entry in the function table.
   360  type Exception struct {
   361  	RuntimeFunction ImageRuntimeFunctionEntry `json:"runtime_function"`
   362  	UnwindInfo      UnwindInfo                `json:"unwind_info"`
   363  }
   364  
   365  func (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) {
   366  
   367  	unwindCode := UnwindCode{}
   368  	advanceBy := 0
   369  
   370  	// Read the unwind code at offset (2 bytes)
   371  	uc, err := pe.ReadUint16(offset)
   372  	if err != nil {
   373  		return unwindCode, advanceBy
   374  	}
   375  
   376  	unwindCode.CodeOffset = uint8(uc & 0xff)
   377  	unwindCode.UnwindOp = UnwindOpType(uc & 0xf00 >> 8)
   378  	unwindCode.OpInfo = uint8(uc & 0xf000 >> 12)
   379  
   380  	switch unwindCode.UnwindOp {
   381  	case UwOpAllocSmall:
   382  		size := int(unwindCode.OpInfo*8 + 8)
   383  		unwindCode.Operand = "Size=" + strconv.Itoa(size)
   384  		advanceBy++
   385  	case UwOpAllocLarge:
   386  		if unwindCode.OpInfo == 0 {
   387  			size := int(binary.LittleEndian.Uint16(pe.data[offset+2:]) * 8)
   388  			unwindCode.Operand = "Size=" + strconv.Itoa(size)
   389  			advanceBy += 2
   390  		} else {
   391  			size := int(binary.LittleEndian.Uint32(pe.data[offset+2:]) << 16)
   392  			unwindCode.Operand = "Size=" + strconv.Itoa(size)
   393  			advanceBy += 3
   394  		}
   395  	case UwOpSetFpReg:
   396  		unwindCode.Operand = "Register=" + OpInfoRegisters[unwindCode.OpInfo]
   397  		advanceBy++
   398  	case UwOpPushNonVol:
   399  		unwindCode.Operand = "Register=" + OpInfoRegisters[unwindCode.OpInfo]
   400  		advanceBy++
   401  	case UwOpSaveNonVol:
   402  		fo := binary.LittleEndian.Uint16(pe.data[offset+2:])
   403  		unwindCode.FrameOffset = fo * 8
   404  		unwindCode.Operand = "Register=" + OpInfoRegisters[unwindCode.OpInfo] +
   405  			", Offset=" + strconv.Itoa(int(unwindCode.FrameOffset))
   406  		advanceBy += 2
   407  	case UwOpSaveNonVolFar:
   408  		fo := binary.LittleEndian.Uint32(pe.data[offset+2:])
   409  		unwindCode.FrameOffset = uint16(fo * 8)
   410  		unwindCode.Operand = "Register=" + OpInfoRegisters[unwindCode.OpInfo] +
   411  			", Offset=" + strconv.Itoa(int(unwindCode.FrameOffset))
   412  		advanceBy += 3
   413  	case UwOpSaveXmm128:
   414  		fo := binary.LittleEndian.Uint16(pe.data[offset+2:])
   415  		unwindCode.FrameOffset = fo * 16
   416  		unwindCode.Operand = "Register=XMM" + strconv.Itoa(int(unwindCode.OpInfo)) +
   417  			", Offset=" + strconv.Itoa(int(unwindCode.FrameOffset))
   418  		advanceBy += 2
   419  	case UwOpSaveXmm128Far:
   420  		fo := binary.LittleEndian.Uint32(pe.data[offset+2:])
   421  		unwindCode.FrameOffset = uint16(fo)
   422  		unwindCode.Operand = "Register=XMM" + strconv.Itoa(int(unwindCode.OpInfo)) +
   423  			", Offset=" + strconv.Itoa(int(unwindCode.FrameOffset))
   424  		advanceBy += 3
   425  	case UwOpSetFpRegLarge:
   426  		unwindCode.Operand = "Register=" + OpInfoRegisters[unwindCode.OpInfo]
   427  		advanceBy += 2
   428  	case UwOpPushMachFrame:
   429  		advanceBy++
   430  	case UwOpEpilog:
   431  		if version == 2 {
   432  			unwindCode.Operand = "Flags=" + strconv.Itoa(int(unwindCode.OpInfo)) + ", Size=" + strconv.Itoa(int(unwindCode.CodeOffset))
   433  		}
   434  		advanceBy += 2
   435  	case UwOpSpareCode:
   436  		advanceBy += 3
   437  	default:
   438  		advanceBy++ // so we can get out of the loop
   439  		pe.logger.Warnf("Wrong unwind opcode %d", unwindCode.UnwindOp)
   440  	}
   441  
   442  	return unwindCode, advanceBy
   443  }
   444  
   445  func (pe *File) parseUnwindInfo(unwindInfo uint32) UnwindInfo {
   446  
   447  	ui := UnwindInfo{}
   448  
   449  	offset := pe.GetOffsetFromRva(unwindInfo)
   450  	v, err := pe.ReadUint32(offset)
   451  	if err != nil {
   452  		return ui
   453  	}
   454  
   455  	// The lowest 3 bits
   456  	ui.Version = uint8(v & 0x7)
   457  
   458  	// The next 5 bits.
   459  	ui.Flags = uint8(v & 0xf8 >> 3)
   460  
   461  	// The next byte
   462  	ui.SizeOfProlog = uint8(v & 0xff00 >> 8)
   463  
   464  	// The next byte
   465  	ui.CountOfCodes = uint8(v & 0xff0000 >> 16)
   466  
   467  	// The next 4 bits
   468  	ui.FrameRegister = uint8(v & 0xf00000 >> 24)
   469  
   470  	// The next 4 bits.
   471  	ui.FrameOffset = uint8(v&0xf0000000>>28) * 6
   472  
   473  	// Each unwind code struct is 2 bytes wide.
   474  	offset += 4
   475  	i := 0
   476  	for i < int(ui.CountOfCodes) {
   477  		ucOffset := offset + 2*uint32(i)
   478  		unwindCode, advanceBy := pe.parseUnwindCode(ucOffset, ui.Version)
   479  		if advanceBy == 0 {
   480  			return ui
   481  		}
   482  		ui.UnwindCodes = append(ui.UnwindCodes, unwindCode)
   483  		i += advanceBy
   484  	}
   485  
   486  	if ui.CountOfCodes&1 == 1 {
   487  		offset += 2
   488  	}
   489  
   490  	// An image-relative pointer to either the function's language-specific
   491  	// exception or termination handler, if flag UNW_FLAG_CHAININFO is clear
   492  	// and one of the flags UNW_FLAG_EHADLER or UNW_FLAG_UHANDLER is set.
   493  	if ui.Flags&UnwFlagEHandler != 0 || ui.Flags&UnwFlagUHandler != 0 {
   494  		if ui.Flags&UnwFlagChainInfo == 0 {
   495  			handlerOffset := offset + 2*uint32(i)
   496  			ui.ExceptionHandler = binary.LittleEndian.Uint32(pe.data[handlerOffset:])
   497  		}
   498  	}
   499  
   500  	// If the UNW_FLAG_CHAININFO flag is set, then an unwind info structure
   501  	// is a secondary one, and the shared exception-handler/chained-info
   502  	// address field contains the primary unwind information. This sample
   503  	// code retrieves the primary unwind information, assuming that unwindInfo
   504  	// is the structure that has the UNW_FLAG_CHAININFO flag set.
   505  	if ui.Flags&UnwFlagChainInfo != 0 {
   506  		chainOffset := offset + 2*uint32(i)
   507  		rf := ImageRuntimeFunctionEntry{}
   508  		size := uint32(binary.Size(ImageRuntimeFunctionEntry{}))
   509  		err := pe.structUnpack(&rf, chainOffset, size)
   510  		if err != nil {
   511  			return ui
   512  		}
   513  		ui.FunctionEntry = rf
   514  	}
   515  
   516  	return ui
   517  }
   518  
   519  // Exception directory contains an array of function table entries that are used
   520  // for exception handling.
   521  func (pe *File) parseExceptionDirectory(rva, size uint32) error {
   522  
   523  	// The target platform determines which format of the function table entry
   524  	// to use.
   525  	var exceptions []Exception
   526  	fileOffset := pe.GetOffsetFromRva(rva)
   527  
   528  	entrySize := uint32(binary.Size(ImageRuntimeFunctionEntry{}))
   529  	entriesCount := size / entrySize
   530  
   531  	for i := uint32(0); i < entriesCount; i++ {
   532  		functionEntry := ImageRuntimeFunctionEntry{}
   533  		offset := fileOffset + (entrySize * i)
   534  		err := pe.structUnpack(&functionEntry, offset, entrySize)
   535  		if err != nil {
   536  			return err
   537  		}
   538  
   539  		exception := Exception{RuntimeFunction: functionEntry}
   540  
   541  		if pe.Is64 {
   542  			exception.UnwindInfo = pe.parseUnwindInfo(functionEntry.UnwindInfoAddress)
   543  		}
   544  
   545  		exceptions = append(exceptions, exception)
   546  	}
   547  
   548  	pe.Exceptions = exceptions
   549  	if len(exceptions) > 0 {
   550  		pe.HasException = true
   551  	}
   552  	return nil
   553  }
   554  
   555  // PrettyUnwindInfoHandlerFlags returns the string representation of the
   556  // `flags` field of the unwind info structure.
   557  func PrettyUnwindInfoHandlerFlags(flags uint8) []string {
   558  	var values []string
   559  
   560  	unwFlagHandlerMap := map[uint8]string{
   561  		UnwFlagNHandler:  "No Handler",
   562  		UnwFlagEHandler:  "Exception",
   563  		UnwFlagUHandler:  "Termination",
   564  		UnwFlagChainInfo: "Chain",
   565  	}
   566  
   567  	for k, s := range unwFlagHandlerMap {
   568  		if k&flags != 0 {
   569  			values = append(values, s)
   570  		}
   571  	}
   572  	return values
   573  }
   574  
   575  // String returns the string representation of the an unwind opcode.
   576  func (uo UnwindOpType) String() string {
   577  
   578  	unOpToString := map[UnwindOpType]string{
   579  		UwOpPushNonVol:    "UWOP_PUSH_NONVOL",
   580  		UwOpAllocLarge:    "UWOP_ALLOC_LARE",
   581  		UwOpAllocSmall:    "UWOP_ALLOC_SMALL",
   582  		UwOpSetFpReg:      "UWOP_SET_FPREG",
   583  		UwOpSaveNonVol:    "UWOP_SAVE_NONVOL",
   584  		UwOpSaveNonVolFar: "UWOP_SAVE_NONVOL_FAR",
   585  		UwOpEpilog:        "UWOP_EPILOG",
   586  		UwOpSpareCode:     "UWOP_SPARE_CODE",
   587  		UwOpSaveXmm128:    "UWOP_SAVE_XMM128",
   588  		UwOpSaveXmm128Far: "UWOP_SAVE_XMM128_FAR",
   589  		UwOpPushMachFrame: "UWOP_PUSH_MACHFRAME",
   590  		UwOpSetFpRegLarge: "UWOP_SET_FPREG_LARGE",
   591  	}
   592  
   593  	if val, ok := unOpToString[uo]; ok {
   594  		return val
   595  	}
   596  
   597  	return "?"
   598  }