etc/services - sync with NetBSD-8
[minix.git] / sys / lib / libunwind / AddressSpace.hpp
blob327c0d10895fe1371468f3445adc2d182672f6f8
1 //===------------------------- AddressSpace.hpp ---------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //
9 // Abstracts accessing local vs remote address spaces.
11 //===----------------------------------------------------------------------===//
13 #ifndef __ADDRESSSPACE_HPP__
14 #define __ADDRESSSPACE_HPP__
16 #include <sys/rbtree.h>
17 #include <cassert>
18 #include <cstddef>
19 #include <cstdint>
20 #include <cstdlib>
21 #include <cstring>
22 #include <dlfcn.h>
23 #include <elf.h>
24 #include <link.h>
25 #if !defined(__minix)
26 #include <pthread.h>
27 #else
28 #define pthread_rwlock_init(l, d) /* nothing */
29 #define pthread_rwlock_rdlock(l) /* nothing */
30 #define pthread_rwlock_wrlock(l) /* nothing */
31 #define pthread_rwlock_unlock(l) /* nothing */
32 #endif /* !defined(__minix) */
34 #include "dwarf2.h"
36 namespace _Unwind {
38 static int rangeCmp(void *, const void *, const void *);
39 static int rangeCmpKey(void *, const void *, const void *);
40 static int dsoTableCmp(void *, const void *, const void *);
41 static int dsoTableCmpKey(void *, const void *, const void *);
42 static int phdr_callback(struct dl_phdr_info *, size_t, void *);
44 struct unw_proc_info_t {
45 uintptr_t data_base; // Base address for data-relative relocations
46 uintptr_t start_ip; // Start address of function
47 uintptr_t end_ip; // First address after end of function
48 uintptr_t lsda; // Address of Language Specific Data Area
49 uintptr_t handler; // Personality routine
50 uintptr_t extra_args; // Extra stack space for frameless routines
51 uintptr_t unwind_info; // Address of DWARF unwind info
54 /// LocalAddressSpace is used as a template parameter to UnwindCursor when
55 /// unwinding a thread in the same process. The wrappers compile away,
56 /// making local unwinds fast.
57 class LocalAddressSpace {
58 public:
59 typedef uintptr_t pint_t;
60 typedef intptr_t sint_t;
62 typedef void (*findPCRange_t)(LocalAddressSpace &, pint_t, pint_t &pcStart,
63 pint_t &pcEnd);
65 LocalAddressSpace(findPCRange_t findPCRange_)
66 : findPCRange(findPCRange_), needsReload(true) {
67 static const rb_tree_ops_t segmentTreeOps = {
68 rangeCmp, rangeCmpKey, offsetof(Range, range_link), NULL
70 static const rb_tree_ops_t dsoTreeOps = {
71 dsoTableCmp, dsoTableCmpKey, offsetof(Range, dso_link), NULL
73 rb_tree_init(&segmentTree, &segmentTreeOps);
74 rb_tree_init(&dsoTree, &dsoTreeOps);
75 pthread_rwlock_init(&fdeTreeLock, NULL);
78 uint8_t get8(pint_t addr) {
79 uint8_t val;
80 memcpy(&val, (void *)addr, sizeof(val));
81 return val;
84 uint16_t get16(pint_t addr) {
85 uint16_t val;
86 memcpy(&val, (void *)addr, sizeof(val));
87 return val;
90 uint32_t get32(pint_t addr) {
91 uint32_t val;
92 memcpy(&val, (void *)addr, sizeof(val));
93 return val;
96 uint64_t get64(pint_t addr) {
97 uint64_t val;
98 memcpy(&val, (void *)addr, sizeof(val));
99 return val;
102 uintptr_t getP(pint_t addr) {
103 if (sizeof(uintptr_t) == sizeof(uint32_t))
104 return get32(addr);
105 else
106 return get64(addr);
109 uint64_t getULEB128(pint_t &addr, pint_t end) {
110 uint64_t result = 0;
111 uint8_t byte;
112 int bit = 0;
113 do {
114 uint64_t b;
116 assert(addr != end);
118 byte = get8(addr++);
119 b = byte & 0x7f;
121 assert(bit < 64);
122 assert(b << bit >> bit == b);
124 result |= b << bit;
125 bit += 7;
126 } while (byte >= 0x80);
127 return result;
130 int64_t getSLEB128(pint_t &addr, pint_t end) {
131 uint64_t result = 0;
132 uint8_t byte;
133 int bit = 0;
134 do {
135 uint64_t b;
137 assert(addr != end);
139 byte = get8(addr++);
140 b = byte & 0x7f;
142 assert(bit < 64);
143 assert(b << bit >> bit == b);
145 result |= b << bit;
146 bit += 7;
147 } while (byte >= 0x80);
148 // sign extend negative numbers
149 if ((byte & 0x40) != 0)
150 result |= (-1LL) << bit;
151 return result;
154 pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
155 const unw_proc_info_t *ctx) {
156 pint_t startAddr = addr;
157 const uint8_t *p = (uint8_t *)addr;
158 pint_t result;
160 if (encoding == DW_EH_PE_omit)
161 return 0;
162 if (encoding == DW_EH_PE_aligned) {
163 addr = (addr + sizeof(pint_t) - 1) & sizeof(pint_t);
164 return getP(addr);
167 // first get value
168 switch (encoding & 0x0F) {
169 case DW_EH_PE_ptr:
170 result = getP(addr);
171 p += sizeof(pint_t);
172 addr = (pint_t)p;
173 break;
174 case DW_EH_PE_uleb128:
175 result = getULEB128(addr, end);
176 break;
177 case DW_EH_PE_udata2:
178 result = get16(addr);
179 p += 2;
180 addr = (pint_t)p;
181 break;
182 case DW_EH_PE_udata4:
183 result = get32(addr);
184 p += 4;
185 addr = (pint_t)p;
186 break;
187 case DW_EH_PE_udata8:
188 result = get64(addr);
189 p += 8;
190 addr = (pint_t)p;
191 break;
192 case DW_EH_PE_sleb128:
193 result = getSLEB128(addr, end);
194 break;
195 case DW_EH_PE_sdata2:
196 result = (int16_t)get16(addr);
197 p += 2;
198 addr = (pint_t)p;
199 break;
200 case DW_EH_PE_sdata4:
201 result = (int32_t)get32(addr);
202 p += 4;
203 addr = (pint_t)p;
204 break;
205 case DW_EH_PE_sdata8:
206 result = get64(addr);
207 p += 8;
208 addr = (pint_t)p;
209 break;
210 case DW_EH_PE_omit:
211 result = 0;
212 break;
213 default:
214 assert(0 && "unknown pointer encoding");
217 // then add relative offset
218 switch (encoding & 0x70) {
219 case DW_EH_PE_absptr:
220 // do nothing
221 break;
222 case DW_EH_PE_pcrel:
223 result += startAddr;
224 break;
225 case DW_EH_PE_textrel:
226 assert(0 && "DW_EH_PE_textrel pointer encoding not supported");
227 break;
228 case DW_EH_PE_datarel:
229 assert(ctx != NULL && "DW_EH_PE_datarel without context");
230 if (ctx)
231 result += ctx->data_base;
232 break;
233 case DW_EH_PE_funcrel:
234 assert(ctx != NULL && "DW_EH_PE_funcrel without context");
235 if (ctx)
236 result += ctx->start_ip;
237 break;
238 case DW_EH_PE_aligned:
239 __builtin_unreachable();
240 default:
241 assert(0 && "unknown pointer encoding");
242 break;
245 if (encoding & DW_EH_PE_indirect)
246 result = getP(result);
248 return result;
251 bool findFDE(pint_t pc, pint_t &fdeStart, pint_t &data_base) {
252 Range *n;
253 for (;;) {
254 pthread_rwlock_rdlock(&fdeTreeLock);
255 n = (Range *)rb_tree_find_node(&segmentTree, &pc);
256 pthread_rwlock_unlock(&fdeTreeLock);
257 if (n != NULL)
258 break;
259 if (!needsReload)
260 break;
261 lazyReload();
263 if (n == NULL)
264 return false;
265 if (n->hdr_start == 0) {
266 fdeStart = n->hdr_base;
267 data_base = n->data_base;
268 return true;
271 pint_t base = n->hdr_base;
272 pint_t first = n->hdr_start;
273 for (pint_t len = n->hdr_entries; len > 1; ) {
274 pint_t next = first + (len / 2) * 8;
275 pint_t nextPC = base + (int32_t)get32(next);
276 if (nextPC == pc) {
277 first = next;
278 break;
280 if (nextPC < pc) {
281 first = next;
282 len -= (len / 2);
283 } else {
284 len /= 2;
287 fdeStart = base + (int32_t)get32(first + 4);
288 data_base = n->data_base;
289 return true;
292 bool addFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) {
293 pthread_rwlock_wrlock(&fdeTreeLock);
294 Range *n = (Range *)malloc(sizeof(*n));
295 n->hdr_base = fde;
296 n->hdr_start = 0;
297 n->hdr_entries = 0;
298 n->first_pc = pcStart;
299 n->last_pc = pcEnd;
300 n->data_base = 0;
301 n->ehframe_base = 0;
302 if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) == n) {
303 pthread_rwlock_unlock(&fdeTreeLock);
304 return true;
306 free(n);
307 pthread_rwlock_unlock(&fdeTreeLock);
308 return false;
311 bool removeFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) {
312 pthread_rwlock_wrlock(&fdeTreeLock);
313 Range *n = static_cast<Range *>(rb_tree_find_node(&segmentTree, &pcStart));
314 if (n == NULL) {
315 pthread_rwlock_unlock(&fdeTreeLock);
316 return false;
318 assert(n->first_pc == pcStart);
319 assert(n->last_pc == pcEnd);
320 assert(n->hdr_base == fde);
321 assert(n->hdr_start == 0);
322 assert(n->hdr_entries == 0);
323 assert(n->data_base == 0);
324 assert(n->ehframe_base == 0);
325 rb_tree_remove_node(&segmentTree, n);
326 free(n);
327 pthread_rwlock_unlock(&fdeTreeLock);
328 return true;
331 void removeDSO(pint_t ehFrameBase) {
332 pthread_rwlock_wrlock(&fdeTreeLock);
333 Range *n;
334 n = (Range *)rb_tree_find_node(&dsoTree, &ehFrameBase);
335 if (n == NULL) {
336 pthread_rwlock_unlock(&fdeTreeLock);
337 return;
339 rb_tree_remove_node(&dsoTree, n);
340 rb_tree_remove_node(&segmentTree, n);
341 free(n);
342 pthread_rwlock_unlock(&fdeTreeLock);
345 void setLazyReload() {
346 pthread_rwlock_wrlock(&fdeTreeLock);
347 needsReload = true;
348 pthread_rwlock_unlock(&fdeTreeLock);
351 private:
352 findPCRange_t findPCRange;
353 bool needsReload;
354 #if !defined(__minix)
355 pthread_rwlock_t fdeTreeLock;
356 #endif /* !defined(__minix) */
357 rb_tree_t segmentTree;
358 rb_tree_t dsoTree;
360 friend int phdr_callback(struct dl_phdr_info *, size_t, void *);
361 friend int rangeCmp(void *, const void *, const void *);
362 friend int rangeCmpKey(void *, const void *, const void *);
363 friend int dsoTableCmp(void *, const void *, const void *);
364 friend int dsoTableCmpKey(void *, const void *, const void *);
366 void updateRange();
368 struct Range {
369 rb_node_t range_link;
370 rb_node_t dso_link;
371 pint_t hdr_base; // Pointer to FDE if hdr_start == 0
372 pint_t hdr_start;
373 pint_t hdr_entries;
374 pint_t first_pc;
375 pint_t last_pc;
376 pint_t data_base;
377 pint_t ehframe_base;
380 void lazyReload() {
381 pthread_rwlock_wrlock(&fdeTreeLock);
382 dl_iterate_phdr(phdr_callback, this);
383 needsReload = false;
384 pthread_rwlock_unlock(&fdeTreeLock);
387 void addDSO(pint_t header, pint_t data_base) {
388 if (header == 0)
389 return;
390 if (get8(header) != 1)
391 return;
392 if (get8(header + 3) != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
393 return;
394 pint_t end = header + 4;
395 pint_t ehframe_base = getEncodedP(end, 0, get8(header + 1), NULL);
396 pint_t entries = getEncodedP(end, 0, get8(header + 2), NULL);
397 pint_t start = (end + 3) & ~pint_t(3);
398 if (entries == 0)
399 return;
400 Range *n = (Range *)malloc(sizeof(*n));
401 n->hdr_base = header;
402 n->hdr_start = start;
403 n->hdr_entries = entries;
404 n->first_pc = header + (int32_t)get32(n->hdr_start);
405 pint_t tmp;
406 (*findPCRange)(
407 *this, header + (int32_t)get32(n->hdr_start + (entries - 1) * 8 + 4),
408 tmp, n->last_pc);
409 n->data_base = data_base;
410 n->ehframe_base = ehframe_base;
412 if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) != n) {
413 free(n);
414 return;
416 rb_tree_insert_node(&dsoTree, n);
420 static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data_) {
421 LocalAddressSpace *data = (LocalAddressSpace *)data_;
422 size_t eh_frame = 0, data_base = 0;
423 const Elf_Phdr *hdr = info->dlpi_phdr;
424 const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum;
425 const Elf_Dyn *dyn;
427 for (; hdr != last_hdr; ++hdr) {
428 switch (hdr->p_type) {
429 case PT_GNU_EH_FRAME:
430 eh_frame = info->dlpi_addr + hdr->p_vaddr;
431 break;
432 case PT_DYNAMIC:
433 dyn = (const Elf_Dyn *)(info->dlpi_addr + hdr->p_vaddr);
434 while (dyn->d_tag != DT_NULL) {
435 if (dyn->d_tag == DT_PLTGOT) {
436 data_base = info->dlpi_addr + dyn->d_un.d_ptr;
437 break;
439 ++dyn;
444 if (eh_frame)
445 data->addDSO(eh_frame, data_base);
447 return 0;
450 static int rangeCmp(void *context, const void *n1_, const void *n2_) {
451 const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_;
452 const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_;
454 if (n1->first_pc < n2->first_pc)
455 return -1;
456 if (n1->first_pc > n2->first_pc)
457 return 1;
458 assert(n1->last_pc == n2->last_pc);
459 return 0;
462 static int rangeCmpKey(void *context, const void *n_, const void *pc_) {
463 const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_;
464 const LocalAddressSpace::pint_t *pc = (const LocalAddressSpace::pint_t *)pc_;
465 if (n->last_pc < *pc)
466 return -1;
467 if (n->first_pc > *pc)
468 return 1;
469 return 0;
472 static int dsoTableCmp(void *context, const void *n1_, const void *n2_) {
473 const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_;
474 const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_;
476 if (n1->ehframe_base < n2->ehframe_base)
477 return -1;
478 if (n1->ehframe_base > n2->ehframe_base)
479 return 1;
480 return 0;
483 static int dsoTableCmpKey(void *context, const void *n_, const void *ptr_) {
484 const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_;
485 const LocalAddressSpace::pint_t *ptr = (const LocalAddressSpace::pint_t *)ptr_;
486 if (n->ehframe_base < *ptr)
487 return -1;
488 if (n->ehframe_base > *ptr)
489 return 1;
490 return 0;
493 } // namespace _Unwind
495 #endif // __ADDRESSSPACE_HPP__