1 //===------------------------- AddressSpace.hpp ---------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
9 // Abstracts accessing local vs remote address spaces.
11 //===----------------------------------------------------------------------===//
13 #ifndef __ADDRESSSPACE_HPP__
14 #define __ADDRESSSPACE_HPP__
16 #include <sys/rbtree.h>
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) */
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
{
59 typedef uintptr_t pint_t
;
60 typedef intptr_t sint_t
;
62 typedef void (*findPCRange_t
)(LocalAddressSpace
&, pint_t
, pint_t
&pcStart
,
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
) {
80 memcpy(&val
, (void *)addr
, sizeof(val
));
84 uint16_t get16(pint_t addr
) {
86 memcpy(&val
, (void *)addr
, sizeof(val
));
90 uint32_t get32(pint_t addr
) {
92 memcpy(&val
, (void *)addr
, sizeof(val
));
96 uint64_t get64(pint_t addr
) {
98 memcpy(&val
, (void *)addr
, sizeof(val
));
102 uintptr_t getP(pint_t addr
) {
103 if (sizeof(uintptr_t) == sizeof(uint32_t))
109 uint64_t getULEB128(pint_t
&addr
, pint_t end
) {
122 assert(b
<< bit
>> bit
== b
);
126 } while (byte
>= 0x80);
130 int64_t getSLEB128(pint_t
&addr
, pint_t end
) {
143 assert(b
<< bit
>> bit
== b
);
147 } while (byte
>= 0x80);
148 // sign extend negative numbers
149 if ((byte
& 0x40) != 0)
150 result
|= (-1LL) << bit
;
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
;
160 if (encoding
== DW_EH_PE_omit
)
162 if (encoding
== DW_EH_PE_aligned
) {
163 addr
= (addr
+ sizeof(pint_t
) - 1) & sizeof(pint_t
);
168 switch (encoding
& 0x0F) {
174 case DW_EH_PE_uleb128
:
175 result
= getULEB128(addr
, end
);
177 case DW_EH_PE_udata2
:
178 result
= get16(addr
);
182 case DW_EH_PE_udata4
:
183 result
= get32(addr
);
187 case DW_EH_PE_udata8
:
188 result
= get64(addr
);
192 case DW_EH_PE_sleb128
:
193 result
= getSLEB128(addr
, end
);
195 case DW_EH_PE_sdata2
:
196 result
= (int16_t)get16(addr
);
200 case DW_EH_PE_sdata4
:
201 result
= (int32_t)get32(addr
);
205 case DW_EH_PE_sdata8
:
206 result
= get64(addr
);
214 assert(0 && "unknown pointer encoding");
217 // then add relative offset
218 switch (encoding
& 0x70) {
219 case DW_EH_PE_absptr
:
225 case DW_EH_PE_textrel
:
226 assert(0 && "DW_EH_PE_textrel pointer encoding not supported");
228 case DW_EH_PE_datarel
:
229 assert(ctx
!= NULL
&& "DW_EH_PE_datarel without context");
231 result
+= ctx
->data_base
;
233 case DW_EH_PE_funcrel
:
234 assert(ctx
!= NULL
&& "DW_EH_PE_funcrel without context");
236 result
+= ctx
->start_ip
;
238 case DW_EH_PE_aligned
:
239 __builtin_unreachable();
241 assert(0 && "unknown pointer encoding");
245 if (encoding
& DW_EH_PE_indirect
)
246 result
= getP(result
);
251 bool findFDE(pint_t pc
, pint_t
&fdeStart
, pint_t
&data_base
) {
254 pthread_rwlock_rdlock(&fdeTreeLock
);
255 n
= (Range
*)rb_tree_find_node(&segmentTree
, &pc
);
256 pthread_rwlock_unlock(&fdeTreeLock
);
265 if (n
->hdr_start
== 0) {
266 fdeStart
= n
->hdr_base
;
267 data_base
= n
->data_base
;
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
);
287 fdeStart
= base
+ (int32_t)get32(first
+ 4);
288 data_base
= n
->data_base
;
292 bool addFDE(pint_t pcStart
, pint_t pcEnd
, pint_t fde
) {
293 pthread_rwlock_wrlock(&fdeTreeLock
);
294 Range
*n
= (Range
*)malloc(sizeof(*n
));
298 n
->first_pc
= pcStart
;
302 if (static_cast<Range
*>(rb_tree_insert_node(&segmentTree
, n
)) == n
) {
303 pthread_rwlock_unlock(&fdeTreeLock
);
307 pthread_rwlock_unlock(&fdeTreeLock
);
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
));
315 pthread_rwlock_unlock(&fdeTreeLock
);
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
);
327 pthread_rwlock_unlock(&fdeTreeLock
);
331 void removeDSO(pint_t ehFrameBase
) {
332 pthread_rwlock_wrlock(&fdeTreeLock
);
334 n
= (Range
*)rb_tree_find_node(&dsoTree
, &ehFrameBase
);
336 pthread_rwlock_unlock(&fdeTreeLock
);
339 rb_tree_remove_node(&dsoTree
, n
);
340 rb_tree_remove_node(&segmentTree
, n
);
342 pthread_rwlock_unlock(&fdeTreeLock
);
345 void setLazyReload() {
346 pthread_rwlock_wrlock(&fdeTreeLock
);
348 pthread_rwlock_unlock(&fdeTreeLock
);
352 findPCRange_t findPCRange
;
354 #if !defined(__minix)
355 pthread_rwlock_t fdeTreeLock
;
356 #endif /* !defined(__minix) */
357 rb_tree_t segmentTree
;
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 *);
369 rb_node_t range_link
;
371 pint_t hdr_base
; // Pointer to FDE if hdr_start == 0
381 pthread_rwlock_wrlock(&fdeTreeLock
);
382 dl_iterate_phdr(phdr_callback
, this);
384 pthread_rwlock_unlock(&fdeTreeLock
);
387 void addDSO(pint_t header
, pint_t data_base
) {
390 if (get8(header
) != 1)
392 if (get8(header
+ 3) != (DW_EH_PE_datarel
| DW_EH_PE_sdata4
))
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);
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
);
407 *this, header
+ (int32_t)get32(n
->hdr_start
+ (entries
- 1) * 8 + 4),
409 n
->data_base
= data_base
;
410 n
->ehframe_base
= ehframe_base
;
412 if (static_cast<Range
*>(rb_tree_insert_node(&segmentTree
, n
)) != n
) {
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
;
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
;
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
;
445 data
->addDSO(eh_frame
, data_base
);
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
)
456 if (n1
->first_pc
> n2
->first_pc
)
458 assert(n1
->last_pc
== n2
->last_pc
);
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
)
467 if (n
->first_pc
> *pc
)
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
)
478 if (n1
->ehframe_base
> n2
->ehframe_base
)
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
)
488 if (n
->ehframe_base
> *ptr
)
493 } // namespace _Unwind
495 #endif // __ADDRESSSPACE_HPP__