202 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
| //===----------------------------- unwind-pe.h ----------------------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is dual licensed under the MIT and the University of Illinois Open
 | |
| // Source Licenses. See LICENSE.TXT for details.
 | |
| //
 | |
| // Pointer-Encoding decoder. Derived from:
 | |
| //   - libcxxabi/src/Unwind/dwarf2.h
 | |
| //   - libcxxabi/src/Unwind/AddressSpace.h
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #ifndef UNWIND_PE_H
 | |
| #define UNWIND_PE_H
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdint.h>
 | |
| #include <string.h>
 | |
| 
 | |
| // FSF exception handling Pointer-Encoding constants
 | |
| // Used in CFI augmentation by GCC
 | |
| enum {
 | |
|   DW_EH_PE_ptr       = 0x00,
 | |
|   DW_EH_PE_uleb128   = 0x01,
 | |
|   DW_EH_PE_udata2    = 0x02,
 | |
|   DW_EH_PE_udata4    = 0x03,
 | |
|   DW_EH_PE_udata8    = 0x04,
 | |
|   DW_EH_PE_signed    = 0x08,
 | |
|   DW_EH_PE_sleb128   = 0x09,
 | |
|   DW_EH_PE_sdata2    = 0x0A,
 | |
|   DW_EH_PE_sdata4    = 0x0B,
 | |
|   DW_EH_PE_sdata8    = 0x0C,
 | |
|   DW_EH_PE_absptr    = 0x00,
 | |
|   DW_EH_PE_pcrel     = 0x10,
 | |
|   DW_EH_PE_textrel   = 0x20,
 | |
|   DW_EH_PE_datarel   = 0x30,
 | |
|   DW_EH_PE_funcrel   = 0x40,
 | |
|   DW_EH_PE_aligned   = 0x50,
 | |
|   DW_EH_PE_indirect  = 0x80,
 | |
|   DW_EH_PE_omit      = 0xFF
 | |
| };
 | |
| 
 | |
| /// Read a ULEB128 into a 64-bit word.
 | |
| static uint64_t unw_getULEB128(uintptr_t *addr) {
 | |
|   const uint8_t *p = (uint8_t *)*addr;
 | |
|   uint64_t result = 0;
 | |
|   int bit = 0;
 | |
|   do {
 | |
|     uint64_t b;
 | |
| 
 | |
|     b = *p & 0x7f;
 | |
| 
 | |
|     if (bit >= 64 || b << bit >> bit != b) {
 | |
|       assert(!"malformed uleb128 expression");
 | |
|     } else {
 | |
|       result |= b << bit;
 | |
|       bit += 7;
 | |
|     }
 | |
|   } while (*p++ >= 0x80);
 | |
|   *addr = (uintptr_t) p;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /// Read a SLEB128 into a 64-bit word.
 | |
| static int64_t unw_getSLEB128(uintptr_t *addr) {
 | |
|   const uint8_t *p = (uint8_t *)addr;
 | |
|   int64_t result = 0;
 | |
|   int bit = 0;
 | |
|   uint8_t byte;
 | |
|   do {
 | |
|     byte = *p++;
 | |
|     result |= ((byte & 0x7f) << bit);
 | |
|     bit += 7;
 | |
|   } while (byte & 0x80);
 | |
|   // sign extend negative numbers
 | |
|   if ((byte & 0x40) != 0)
 | |
|     result |= (-1LL) << bit;
 | |
|   *addr = (uintptr_t) p;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static uint16_t unw_get16(uintptr_t addr) {
 | |
|   uint16_t val;
 | |
|   memcpy(&val, (void *)addr, sizeof(val));
 | |
|   return val;
 | |
| }
 | |
| 
 | |
| static uint32_t unw_get32(uintptr_t addr) {
 | |
|   uint32_t val;
 | |
|   memcpy(&val, (void *)addr, sizeof(val));
 | |
|   return val;
 | |
| }
 | |
| 
 | |
| static uint64_t unw_get64(uintptr_t addr) {
 | |
|   uint64_t val;
 | |
|   memcpy(&val, (void *)addr, sizeof(val));
 | |
|   return val;
 | |
| }
 | |
| 
 | |
| static uintptr_t unw_getP(uintptr_t addr) {
 | |
|   if (sizeof(uintptr_t) == 8)
 | |
|     return unw_get64(addr);
 | |
|   else
 | |
|     return unw_get32(addr);
 | |
| }
 | |
| 
 | |
| static const unsigned char *read_uleb128(const unsigned char *p,
 | |
|                                          _uleb128_t *ret) {
 | |
|   uintptr_t addr = (uintptr_t)p;
 | |
|   *ret = unw_getULEB128(&addr);
 | |
|   return (unsigned char *)addr;
 | |
| }
 | |
| 
 | |
| static const unsigned char *read_encoded_value(struct _Unwind_Context *ctx,
 | |
|                                                unsigned char encoding,
 | |
|                                                const unsigned char *p,
 | |
|                                                _Unwind_Ptr *ret) {
 | |
|   uintptr_t addr = (uintptr_t)p;
 | |
|   uintptr_t startAddr = addr;
 | |
|   uintptr_t result;
 | |
| 
 | |
|   (void)ctx;
 | |
| 
 | |
|   // first get value
 | |
|   switch (encoding & 0x0F) {
 | |
|   case DW_EH_PE_ptr:
 | |
|     result = unw_getP(addr);
 | |
|     p += sizeof(uintptr_t);
 | |
|     break;
 | |
|   case DW_EH_PE_uleb128:
 | |
|     result = (uintptr_t)unw_getULEB128(&addr);
 | |
|     p = (const unsigned char *)addr;
 | |
|     break;
 | |
|   case DW_EH_PE_udata2:
 | |
|     result = unw_get16(addr);
 | |
|     p += 2;
 | |
|     break;
 | |
|   case DW_EH_PE_udata4:
 | |
|     result = unw_get32(addr);
 | |
|     p += 4;
 | |
|     break;
 | |
|   case DW_EH_PE_udata8:
 | |
|     result = (uintptr_t)unw_get64(addr);
 | |
|     p += 8;
 | |
|     break;
 | |
|   case DW_EH_PE_sleb128:
 | |
|     result = (uintptr_t)unw_getSLEB128(&addr);
 | |
|     p = (const unsigned char *)addr;
 | |
|     break;
 | |
|   case DW_EH_PE_sdata2:
 | |
|     // Sign extend from signed 16-bit value.
 | |
|     result = (uintptr_t)(int16_t)unw_get16(addr);
 | |
|     p += 2;
 | |
|     break;
 | |
|   case DW_EH_PE_sdata4:
 | |
|     // Sign extend from signed 32-bit value.
 | |
|     result = (uintptr_t)(int32_t)unw_get32(addr);
 | |
|     p += 4;
 | |
|     break;
 | |
|   case DW_EH_PE_sdata8:
 | |
|     result = (uintptr_t)unw_get64(addr);
 | |
|     p += 8;
 | |
|     break;
 | |
|   default:
 | |
|     assert(!"unknown pointer encoding");
 | |
|   }
 | |
| 
 | |
|   // then add relative offset
 | |
|   switch (encoding & 0x70) {
 | |
|   case DW_EH_PE_absptr:
 | |
|     // do nothing
 | |
|     break;
 | |
|   case DW_EH_PE_pcrel:
 | |
|     result += startAddr;
 | |
|     break;
 | |
|   case DW_EH_PE_textrel:
 | |
|     assert(!"DW_EH_PE_textrel pointer encoding not supported");
 | |
|     break;
 | |
|   case DW_EH_PE_datarel:
 | |
|     assert(!"DW_EH_PE_datarel pointer encoding not supported");
 | |
|     break;
 | |
|   case DW_EH_PE_funcrel:
 | |
|     assert(!"DW_EH_PE_funcrel pointer encoding not supported");
 | |
|     break;
 | |
|   case DW_EH_PE_aligned:
 | |
|     assert(!"DW_EH_PE_aligned pointer encoding not supported");
 | |
|     break;
 | |
|   default:
 | |
|     assert(!"unknown pointer encoding");
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   if (encoding & DW_EH_PE_indirect)
 | |
|     result = unw_getP(result);
 | |
| 
 | |
|   *ret = result;
 | |
|   return p;
 | |
| }
 | |
| 
 | |
| #endif  // UNWIND_PE_H
 |