unwind-pe.h
4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//===----------------------------- unwind-pe.h ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// 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