xref: /minix3/sys/lib/libunwind/AddressSpace.hpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1472758f3SLionel Sambuc //===------------------------- AddressSpace.hpp ---------------------------===//
2472758f3SLionel Sambuc //
3472758f3SLionel Sambuc //                     The LLVM Compiler Infrastructure
4472758f3SLionel Sambuc //
5472758f3SLionel Sambuc // This file is dual licensed under the MIT and the University of Illinois Open
6472758f3SLionel Sambuc // Source Licenses. See LICENSE.TXT for details.
7472758f3SLionel Sambuc //
8472758f3SLionel Sambuc //
9472758f3SLionel Sambuc // Abstracts accessing local vs remote address spaces.
10472758f3SLionel Sambuc //
11472758f3SLionel Sambuc //===----------------------------------------------------------------------===//
12472758f3SLionel Sambuc 
13472758f3SLionel Sambuc #ifndef __ADDRESSSPACE_HPP__
14472758f3SLionel Sambuc #define __ADDRESSSPACE_HPP__
15472758f3SLionel Sambuc 
16472758f3SLionel Sambuc #include <sys/rbtree.h>
17472758f3SLionel Sambuc #include <cassert>
18472758f3SLionel Sambuc #include <cstddef>
19472758f3SLionel Sambuc #include <cstdint>
20472758f3SLionel Sambuc #include <cstdlib>
21472758f3SLionel Sambuc #include <cstring>
22472758f3SLionel Sambuc #include <dlfcn.h>
23472758f3SLionel Sambuc #include <elf.h>
24472758f3SLionel Sambuc #include <link.h>
25472758f3SLionel Sambuc #if !defined(__minix)
26472758f3SLionel Sambuc #include <pthread.h>
27472758f3SLionel Sambuc #else
28472758f3SLionel Sambuc #define pthread_rwlock_init(l, d) /* nothing */
29472758f3SLionel Sambuc #define pthread_rwlock_rdlock(l) /* nothing */
30472758f3SLionel Sambuc #define pthread_rwlock_wrlock(l) /* nothing */
31472758f3SLionel Sambuc #define pthread_rwlock_unlock(l) /* nothing */
32472758f3SLionel Sambuc #endif /* !defined(__minix) */
33472758f3SLionel Sambuc 
34472758f3SLionel Sambuc #include "dwarf2.h"
35472758f3SLionel Sambuc 
36472758f3SLionel Sambuc namespace _Unwind {
37472758f3SLionel Sambuc 
38472758f3SLionel Sambuc static int rangeCmp(void *, const void *, const void *);
39472758f3SLionel Sambuc static int rangeCmpKey(void *, const void *, const void *);
40472758f3SLionel Sambuc static int dsoTableCmp(void *, const void *, const void *);
41472758f3SLionel Sambuc static int dsoTableCmpKey(void *, const void *, const void *);
42472758f3SLionel Sambuc static int phdr_callback(struct dl_phdr_info *, size_t, void *);
43472758f3SLionel Sambuc 
44472758f3SLionel Sambuc struct unw_proc_info_t {
45472758f3SLionel Sambuc   uintptr_t data_base;       // Base address for data-relative relocations
46472758f3SLionel Sambuc   uintptr_t start_ip;        // Start address of function
47472758f3SLionel Sambuc   uintptr_t end_ip;          // First address after end of function
48472758f3SLionel Sambuc   uintptr_t lsda;            // Address of Language Specific Data Area
49472758f3SLionel Sambuc   uintptr_t handler;         // Personality routine
50472758f3SLionel Sambuc   uintptr_t extra_args;      // Extra stack space for frameless routines
51472758f3SLionel Sambuc   uintptr_t unwind_info;     // Address of DWARF unwind info
52472758f3SLionel Sambuc };
53472758f3SLionel Sambuc 
54472758f3SLionel Sambuc /// LocalAddressSpace is used as a template parameter to UnwindCursor when
55472758f3SLionel Sambuc /// unwinding a thread in the same process.  The wrappers compile away,
56472758f3SLionel Sambuc /// making local unwinds fast.
57472758f3SLionel Sambuc class LocalAddressSpace {
58472758f3SLionel Sambuc public:
59472758f3SLionel Sambuc   typedef uintptr_t pint_t;
60472758f3SLionel Sambuc   typedef intptr_t sint_t;
61472758f3SLionel Sambuc 
62472758f3SLionel Sambuc   typedef void (*findPCRange_t)(LocalAddressSpace &, pint_t, pint_t &pcStart,
63472758f3SLionel Sambuc                                 pint_t &pcEnd);
64472758f3SLionel Sambuc 
LocalAddressSpace(findPCRange_t findPCRange_)65472758f3SLionel Sambuc   LocalAddressSpace(findPCRange_t findPCRange_)
66472758f3SLionel Sambuc       : findPCRange(findPCRange_), needsReload(true) {
67472758f3SLionel Sambuc     static const rb_tree_ops_t segmentTreeOps = {
68472758f3SLionel Sambuc       rangeCmp, rangeCmpKey, offsetof(Range, range_link), NULL
69472758f3SLionel Sambuc     };
70472758f3SLionel Sambuc     static const rb_tree_ops_t dsoTreeOps = {
71472758f3SLionel Sambuc       dsoTableCmp, dsoTableCmpKey, offsetof(Range, dso_link), NULL
72472758f3SLionel Sambuc     };
73472758f3SLionel Sambuc     rb_tree_init(&segmentTree, &segmentTreeOps);
74472758f3SLionel Sambuc     rb_tree_init(&dsoTree, &dsoTreeOps);
75472758f3SLionel Sambuc     pthread_rwlock_init(&fdeTreeLock, NULL);
76472758f3SLionel Sambuc   }
77472758f3SLionel Sambuc 
get8(pint_t addr)78b029fb59SBen Gras   uint8_t get8(pint_t addr) {
79b029fb59SBen Gras     uint8_t val;
80b029fb59SBen Gras     memcpy(&val, (void *)addr, sizeof(val));
81b029fb59SBen Gras     return val;
82b029fb59SBen Gras   }
83472758f3SLionel Sambuc 
get16(pint_t addr)84b029fb59SBen Gras   uint16_t get16(pint_t addr) {
85b029fb59SBen Gras     uint16_t val;
86b029fb59SBen Gras     memcpy(&val, (void *)addr, sizeof(val));
87b029fb59SBen Gras     return val;
88b029fb59SBen Gras   }
89472758f3SLionel Sambuc 
get32(pint_t addr)90b029fb59SBen Gras   uint32_t get32(pint_t addr) {
91b029fb59SBen Gras     uint32_t val;
92b029fb59SBen Gras     memcpy(&val, (void *)addr, sizeof(val));
93b029fb59SBen Gras     return val;
94b029fb59SBen Gras   }
95472758f3SLionel Sambuc 
get64(pint_t addr)96b029fb59SBen Gras   uint64_t get64(pint_t addr) {
97b029fb59SBen Gras     uint64_t val;
98b029fb59SBen Gras     memcpy(&val, (void *)addr, sizeof(val));
99b029fb59SBen Gras     return val;
100b029fb59SBen Gras   }
101472758f3SLionel Sambuc 
getP(pint_t addr)102472758f3SLionel Sambuc   uintptr_t getP(pint_t addr) {
103472758f3SLionel Sambuc     if (sizeof(uintptr_t) == sizeof(uint32_t))
104472758f3SLionel Sambuc       return get32(addr);
105472758f3SLionel Sambuc     else
106472758f3SLionel Sambuc       return get64(addr);
107472758f3SLionel Sambuc   }
108472758f3SLionel Sambuc 
getULEB128(pint_t & addr,pint_t end)109472758f3SLionel Sambuc   uint64_t getULEB128(pint_t &addr, pint_t end) {
110472758f3SLionel Sambuc     uint64_t result = 0;
111472758f3SLionel Sambuc     uint8_t byte;
112472758f3SLionel Sambuc     int bit = 0;
113472758f3SLionel Sambuc     do {
114472758f3SLionel Sambuc       uint64_t b;
115472758f3SLionel Sambuc 
116472758f3SLionel Sambuc       assert(addr != end);
117472758f3SLionel Sambuc 
118472758f3SLionel Sambuc       byte = get8(addr++);
119472758f3SLionel Sambuc       b = byte & 0x7f;
120472758f3SLionel Sambuc 
121472758f3SLionel Sambuc       assert(bit < 64);
122472758f3SLionel Sambuc       assert(b << bit >> bit == b);
123472758f3SLionel Sambuc 
124472758f3SLionel Sambuc       result |= b << bit;
125472758f3SLionel Sambuc       bit += 7;
126472758f3SLionel Sambuc     } while (byte >= 0x80);
127472758f3SLionel Sambuc     return result;
128472758f3SLionel Sambuc   }
129472758f3SLionel Sambuc 
getSLEB128(pint_t & addr,pint_t end)130472758f3SLionel Sambuc   int64_t getSLEB128(pint_t &addr, pint_t end) {
131472758f3SLionel Sambuc     uint64_t result = 0;
132472758f3SLionel Sambuc     uint8_t byte;
133472758f3SLionel Sambuc     int bit = 0;
134472758f3SLionel Sambuc     do {
135472758f3SLionel Sambuc       uint64_t b;
136472758f3SLionel Sambuc 
137472758f3SLionel Sambuc       assert(addr != end);
138472758f3SLionel Sambuc 
139472758f3SLionel Sambuc       byte = get8(addr++);
140472758f3SLionel Sambuc       b = byte & 0x7f;
141472758f3SLionel Sambuc 
142472758f3SLionel Sambuc       assert(bit < 64);
143472758f3SLionel Sambuc       assert(b << bit >> bit == b);
144472758f3SLionel Sambuc 
145472758f3SLionel Sambuc       result |= b << bit;
146472758f3SLionel Sambuc       bit += 7;
147472758f3SLionel Sambuc     } while (byte >= 0x80);
148472758f3SLionel Sambuc     // sign extend negative numbers
149472758f3SLionel Sambuc     if ((byte & 0x40) != 0)
150472758f3SLionel Sambuc       result |= (-1LL) << bit;
151472758f3SLionel Sambuc     return result;
152472758f3SLionel Sambuc   }
153472758f3SLionel Sambuc 
getEncodedP(pint_t & addr,pint_t end,uint8_t encoding,const unw_proc_info_t * ctx)154472758f3SLionel Sambuc   pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
155472758f3SLionel Sambuc                      const unw_proc_info_t *ctx) {
156472758f3SLionel Sambuc     pint_t startAddr = addr;
157472758f3SLionel Sambuc     const uint8_t *p = (uint8_t *)addr;
158472758f3SLionel Sambuc     pint_t result;
159472758f3SLionel Sambuc 
160472758f3SLionel Sambuc     if (encoding == DW_EH_PE_omit)
161472758f3SLionel Sambuc       return 0;
162472758f3SLionel Sambuc     if (encoding == DW_EH_PE_aligned) {
163472758f3SLionel Sambuc       addr = (addr + sizeof(pint_t) - 1) & sizeof(pint_t);
164472758f3SLionel Sambuc       return getP(addr);
165472758f3SLionel Sambuc     }
166472758f3SLionel Sambuc 
167472758f3SLionel Sambuc     // first get value
168472758f3SLionel Sambuc     switch (encoding & 0x0F) {
169472758f3SLionel Sambuc     case DW_EH_PE_ptr:
170472758f3SLionel Sambuc       result = getP(addr);
171472758f3SLionel Sambuc       p += sizeof(pint_t);
172472758f3SLionel Sambuc       addr = (pint_t)p;
173472758f3SLionel Sambuc       break;
174472758f3SLionel Sambuc     case DW_EH_PE_uleb128:
175472758f3SLionel Sambuc       result = getULEB128(addr, end);
176472758f3SLionel Sambuc       break;
177472758f3SLionel Sambuc     case DW_EH_PE_udata2:
178472758f3SLionel Sambuc       result = get16(addr);
179472758f3SLionel Sambuc       p += 2;
180472758f3SLionel Sambuc       addr = (pint_t)p;
181472758f3SLionel Sambuc       break;
182472758f3SLionel Sambuc     case DW_EH_PE_udata4:
183472758f3SLionel Sambuc       result = get32(addr);
184472758f3SLionel Sambuc       p += 4;
185472758f3SLionel Sambuc       addr = (pint_t)p;
186472758f3SLionel Sambuc       break;
187472758f3SLionel Sambuc     case DW_EH_PE_udata8:
188472758f3SLionel Sambuc       result = get64(addr);
189472758f3SLionel Sambuc       p += 8;
190472758f3SLionel Sambuc       addr = (pint_t)p;
191472758f3SLionel Sambuc       break;
192472758f3SLionel Sambuc     case DW_EH_PE_sleb128:
193472758f3SLionel Sambuc       result = getSLEB128(addr, end);
194472758f3SLionel Sambuc       break;
195472758f3SLionel Sambuc     case DW_EH_PE_sdata2:
196472758f3SLionel Sambuc       result = (int16_t)get16(addr);
197472758f3SLionel Sambuc       p += 2;
198472758f3SLionel Sambuc       addr = (pint_t)p;
199472758f3SLionel Sambuc       break;
200472758f3SLionel Sambuc     case DW_EH_PE_sdata4:
201472758f3SLionel Sambuc       result = (int32_t)get32(addr);
202472758f3SLionel Sambuc       p += 4;
203472758f3SLionel Sambuc       addr = (pint_t)p;
204472758f3SLionel Sambuc       break;
205472758f3SLionel Sambuc     case DW_EH_PE_sdata8:
206472758f3SLionel Sambuc       result = get64(addr);
207472758f3SLionel Sambuc       p += 8;
208472758f3SLionel Sambuc       addr = (pint_t)p;
209472758f3SLionel Sambuc       break;
210472758f3SLionel Sambuc     case DW_EH_PE_omit:
211472758f3SLionel Sambuc       result = 0;
212472758f3SLionel Sambuc       break;
213472758f3SLionel Sambuc     default:
214472758f3SLionel Sambuc       assert(0 && "unknown pointer encoding");
215472758f3SLionel Sambuc     }
216472758f3SLionel Sambuc 
217472758f3SLionel Sambuc     // then add relative offset
218472758f3SLionel Sambuc     switch (encoding & 0x70) {
219472758f3SLionel Sambuc     case DW_EH_PE_absptr:
220472758f3SLionel Sambuc       // do nothing
221472758f3SLionel Sambuc       break;
222472758f3SLionel Sambuc     case DW_EH_PE_pcrel:
223472758f3SLionel Sambuc       result += startAddr;
224472758f3SLionel Sambuc       break;
225472758f3SLionel Sambuc     case DW_EH_PE_textrel:
226472758f3SLionel Sambuc       assert(0 && "DW_EH_PE_textrel pointer encoding not supported");
227472758f3SLionel Sambuc       break;
228472758f3SLionel Sambuc     case DW_EH_PE_datarel:
229472758f3SLionel Sambuc       assert(ctx != NULL && "DW_EH_PE_datarel without context");
230472758f3SLionel Sambuc       if (ctx)
231472758f3SLionel Sambuc         result += ctx->data_base;
232472758f3SLionel Sambuc       break;
233472758f3SLionel Sambuc     case DW_EH_PE_funcrel:
234472758f3SLionel Sambuc       assert(ctx != NULL && "DW_EH_PE_funcrel without context");
235472758f3SLionel Sambuc       if (ctx)
236472758f3SLionel Sambuc         result += ctx->start_ip;
237472758f3SLionel Sambuc       break;
238472758f3SLionel Sambuc     case DW_EH_PE_aligned:
239472758f3SLionel Sambuc       __builtin_unreachable();
240472758f3SLionel Sambuc     default:
241472758f3SLionel Sambuc       assert(0 && "unknown pointer encoding");
242472758f3SLionel Sambuc       break;
243472758f3SLionel Sambuc     }
244472758f3SLionel Sambuc 
245472758f3SLionel Sambuc     if (encoding & DW_EH_PE_indirect)
246472758f3SLionel Sambuc       result = getP(result);
247472758f3SLionel Sambuc 
248472758f3SLionel Sambuc     return result;
249472758f3SLionel Sambuc   }
250472758f3SLionel Sambuc 
findFDE(pint_t pc,pint_t & fdeStart,pint_t & data_base)251472758f3SLionel Sambuc   bool findFDE(pint_t pc, pint_t &fdeStart, pint_t &data_base) {
252472758f3SLionel Sambuc     Range *n;
253472758f3SLionel Sambuc     for (;;) {
254472758f3SLionel Sambuc       pthread_rwlock_rdlock(&fdeTreeLock);
255472758f3SLionel Sambuc       n = (Range *)rb_tree_find_node(&segmentTree, &pc);
256472758f3SLionel Sambuc       pthread_rwlock_unlock(&fdeTreeLock);
257472758f3SLionel Sambuc       if (n != NULL)
258472758f3SLionel Sambuc         break;
259472758f3SLionel Sambuc       if (!needsReload)
260472758f3SLionel Sambuc         break;
261472758f3SLionel Sambuc       lazyReload();
262472758f3SLionel Sambuc     }
263472758f3SLionel Sambuc     if (n == NULL)
264472758f3SLionel Sambuc       return false;
265472758f3SLionel Sambuc     if (n->hdr_start == 0) {
266472758f3SLionel Sambuc       fdeStart = n->hdr_base;
267b029fb59SBen Gras       data_base = n->data_base;
268472758f3SLionel Sambuc       return true;
269472758f3SLionel Sambuc     }
270472758f3SLionel Sambuc 
271472758f3SLionel Sambuc     pint_t base = n->hdr_base;
272472758f3SLionel Sambuc     pint_t first = n->hdr_start;
273*0a6a1f1dSLionel Sambuc     for (pint_t len = n->hdr_entries; len > 1; ) {
274*0a6a1f1dSLionel Sambuc       pint_t next = first + (len / 2) * 8;
275472758f3SLionel Sambuc       pint_t nextPC = base + (int32_t)get32(next);
276472758f3SLionel Sambuc       if (nextPC == pc) {
277472758f3SLionel Sambuc         first = next;
278472758f3SLionel Sambuc         break;
279472758f3SLionel Sambuc       }
280472758f3SLionel Sambuc       if (nextPC < pc) {
281472758f3SLionel Sambuc         first = next;
282*0a6a1f1dSLionel Sambuc         len -= (len / 2);
283*0a6a1f1dSLionel Sambuc       } else {
284*0a6a1f1dSLionel Sambuc         len /= 2;
285*0a6a1f1dSLionel Sambuc       }
286472758f3SLionel Sambuc     }
287472758f3SLionel Sambuc     fdeStart = base + (int32_t)get32(first + 4);
288b029fb59SBen Gras     data_base = n->data_base;
289472758f3SLionel Sambuc     return true;
290472758f3SLionel Sambuc   }
291472758f3SLionel Sambuc 
addFDE(pint_t pcStart,pint_t pcEnd,pint_t fde)292472758f3SLionel Sambuc   bool addFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) {
293472758f3SLionel Sambuc     pthread_rwlock_wrlock(&fdeTreeLock);
294472758f3SLionel Sambuc     Range *n = (Range *)malloc(sizeof(*n));
295472758f3SLionel Sambuc     n->hdr_base = fde;
296472758f3SLionel Sambuc     n->hdr_start = 0;
297472758f3SLionel Sambuc     n->hdr_entries = 0;
298472758f3SLionel Sambuc     n->first_pc = pcStart;
299472758f3SLionel Sambuc     n->last_pc = pcEnd;
300472758f3SLionel Sambuc     n->data_base = 0;
301472758f3SLionel Sambuc     n->ehframe_base = 0;
302*0a6a1f1dSLionel Sambuc     if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) == n) {
303472758f3SLionel Sambuc       pthread_rwlock_unlock(&fdeTreeLock);
304472758f3SLionel Sambuc       return true;
305472758f3SLionel Sambuc     }
306472758f3SLionel Sambuc     free(n);
307472758f3SLionel Sambuc     pthread_rwlock_unlock(&fdeTreeLock);
308472758f3SLionel Sambuc     return false;
309472758f3SLionel Sambuc   }
310472758f3SLionel Sambuc 
removeFDE(pint_t pcStart,pint_t pcEnd,pint_t fde)311472758f3SLionel Sambuc   bool removeFDE(pint_t pcStart, pint_t pcEnd, pint_t fde) {
312472758f3SLionel Sambuc     pthread_rwlock_wrlock(&fdeTreeLock);
313*0a6a1f1dSLionel Sambuc     Range *n = static_cast<Range *>(rb_tree_find_node(&segmentTree, &pcStart));
314472758f3SLionel Sambuc     if (n == NULL) {
315472758f3SLionel Sambuc       pthread_rwlock_unlock(&fdeTreeLock);
316472758f3SLionel Sambuc       return false;
317472758f3SLionel Sambuc     }
318472758f3SLionel Sambuc     assert(n->first_pc == pcStart);
319472758f3SLionel Sambuc     assert(n->last_pc == pcEnd);
320472758f3SLionel Sambuc     assert(n->hdr_base == fde);
321472758f3SLionel Sambuc     assert(n->hdr_start == 0);
322472758f3SLionel Sambuc     assert(n->hdr_entries == 0);
323472758f3SLionel Sambuc     assert(n->data_base == 0);
324472758f3SLionel Sambuc     assert(n->ehframe_base == 0);
325472758f3SLionel Sambuc     rb_tree_remove_node(&segmentTree, n);
326472758f3SLionel Sambuc     free(n);
327472758f3SLionel Sambuc     pthread_rwlock_unlock(&fdeTreeLock);
328472758f3SLionel Sambuc     return true;
329472758f3SLionel Sambuc   }
330472758f3SLionel Sambuc 
removeDSO(pint_t ehFrameBase)331472758f3SLionel Sambuc   void removeDSO(pint_t ehFrameBase) {
332472758f3SLionel Sambuc     pthread_rwlock_wrlock(&fdeTreeLock);
333472758f3SLionel Sambuc     Range *n;
334472758f3SLionel Sambuc     n = (Range *)rb_tree_find_node(&dsoTree, &ehFrameBase);
335472758f3SLionel Sambuc     if (n == NULL) {
336472758f3SLionel Sambuc       pthread_rwlock_unlock(&fdeTreeLock);
337472758f3SLionel Sambuc       return;
338472758f3SLionel Sambuc     }
339472758f3SLionel Sambuc     rb_tree_remove_node(&dsoTree, n);
340472758f3SLionel Sambuc     rb_tree_remove_node(&segmentTree, n);
341472758f3SLionel Sambuc     free(n);
342472758f3SLionel Sambuc     pthread_rwlock_unlock(&fdeTreeLock);
343472758f3SLionel Sambuc   }
344472758f3SLionel Sambuc 
setLazyReload()345472758f3SLionel Sambuc   void setLazyReload() {
346472758f3SLionel Sambuc     pthread_rwlock_wrlock(&fdeTreeLock);
347472758f3SLionel Sambuc     needsReload = true;
348472758f3SLionel Sambuc     pthread_rwlock_unlock(&fdeTreeLock);
349472758f3SLionel Sambuc   }
350472758f3SLionel Sambuc 
351472758f3SLionel Sambuc private:
352472758f3SLionel Sambuc   findPCRange_t findPCRange;
353472758f3SLionel Sambuc   bool needsReload;
354*0a6a1f1dSLionel Sambuc #if !defined(__minix)
355472758f3SLionel Sambuc   pthread_rwlock_t fdeTreeLock;
356*0a6a1f1dSLionel Sambuc #endif /* !defined(__minix) */
357472758f3SLionel Sambuc   rb_tree_t segmentTree;
358472758f3SLionel Sambuc   rb_tree_t dsoTree;
359472758f3SLionel Sambuc 
360472758f3SLionel Sambuc   friend int phdr_callback(struct dl_phdr_info *, size_t, void *);
361472758f3SLionel Sambuc   friend int rangeCmp(void *, const void *, const void *);
362472758f3SLionel Sambuc   friend int rangeCmpKey(void *, const void *, const void *);
363472758f3SLionel Sambuc   friend int dsoTableCmp(void *, const void *, const void *);
364472758f3SLionel Sambuc   friend int dsoTableCmpKey(void *, const void *, const void *);
365472758f3SLionel Sambuc 
366472758f3SLionel Sambuc   void updateRange();
367472758f3SLionel Sambuc 
368472758f3SLionel Sambuc   struct Range {
369472758f3SLionel Sambuc     rb_node_t range_link;
370472758f3SLionel Sambuc     rb_node_t dso_link;
371472758f3SLionel Sambuc     pint_t hdr_base; // Pointer to FDE if hdr_start == 0
372472758f3SLionel Sambuc     pint_t hdr_start;
373472758f3SLionel Sambuc     pint_t hdr_entries;
374472758f3SLionel Sambuc     pint_t first_pc;
375472758f3SLionel Sambuc     pint_t last_pc;
376472758f3SLionel Sambuc     pint_t data_base;
377472758f3SLionel Sambuc     pint_t ehframe_base;
378472758f3SLionel Sambuc   };
379472758f3SLionel Sambuc 
lazyReload()380472758f3SLionel Sambuc   void lazyReload() {
381472758f3SLionel Sambuc     pthread_rwlock_wrlock(&fdeTreeLock);
382472758f3SLionel Sambuc     dl_iterate_phdr(phdr_callback, this);
383472758f3SLionel Sambuc     needsReload = false;
384472758f3SLionel Sambuc     pthread_rwlock_unlock(&fdeTreeLock);
385472758f3SLionel Sambuc   }
386472758f3SLionel Sambuc 
addDSO(pint_t header,pint_t data_base)387472758f3SLionel Sambuc   void addDSO(pint_t header, pint_t data_base) {
388472758f3SLionel Sambuc     if (header == 0)
389472758f3SLionel Sambuc       return;
390472758f3SLionel Sambuc     if (get8(header) != 1)
391472758f3SLionel Sambuc       return;
392472758f3SLionel Sambuc     if (get8(header + 3) != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
393472758f3SLionel Sambuc       return;
394472758f3SLionel Sambuc     pint_t end = header + 4;
395472758f3SLionel Sambuc     pint_t ehframe_base = getEncodedP(end, 0, get8(header + 1), NULL);
396472758f3SLionel Sambuc     pint_t entries = getEncodedP(end, 0, get8(header + 2), NULL);
397472758f3SLionel Sambuc     pint_t start = (end + 3) & ~pint_t(3);
398472758f3SLionel Sambuc     if (entries == 0)
399472758f3SLionel Sambuc       return;
400472758f3SLionel Sambuc     Range *n = (Range *)malloc(sizeof(*n));
401472758f3SLionel Sambuc     n->hdr_base = header;
402472758f3SLionel Sambuc     n->hdr_start = start;
403472758f3SLionel Sambuc     n->hdr_entries = entries;
404472758f3SLionel Sambuc     n->first_pc = header + (int32_t)get32(n->hdr_start);
405472758f3SLionel Sambuc     pint_t tmp;
406472758f3SLionel Sambuc     (*findPCRange)(
407472758f3SLionel Sambuc         *this, header + (int32_t)get32(n->hdr_start + (entries - 1) * 8 + 4),
408472758f3SLionel Sambuc         tmp, n->last_pc);
409472758f3SLionel Sambuc     n->data_base = data_base;
410472758f3SLionel Sambuc     n->ehframe_base = ehframe_base;
411472758f3SLionel Sambuc 
412*0a6a1f1dSLionel Sambuc     if (static_cast<Range *>(rb_tree_insert_node(&segmentTree, n)) != n) {
413472758f3SLionel Sambuc       free(n);
414472758f3SLionel Sambuc       return;
415472758f3SLionel Sambuc     }
416472758f3SLionel Sambuc     rb_tree_insert_node(&dsoTree, n);
417472758f3SLionel Sambuc   }
418472758f3SLionel Sambuc };
419472758f3SLionel Sambuc 
phdr_callback(struct dl_phdr_info * info,size_t size,void * data_)420472758f3SLionel Sambuc static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data_) {
421472758f3SLionel Sambuc   LocalAddressSpace *data = (LocalAddressSpace *)data_;
422472758f3SLionel Sambuc   size_t eh_frame = 0, data_base = 0;
423472758f3SLionel Sambuc   const Elf_Phdr *hdr = info->dlpi_phdr;
424472758f3SLionel Sambuc   const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum;
425472758f3SLionel Sambuc   const Elf_Dyn *dyn;
426472758f3SLionel Sambuc 
427472758f3SLionel Sambuc   for (; hdr != last_hdr; ++hdr) {
428472758f3SLionel Sambuc     switch (hdr->p_type) {
429472758f3SLionel Sambuc     case PT_GNU_EH_FRAME:
430472758f3SLionel Sambuc       eh_frame = info->dlpi_addr + hdr->p_vaddr;
431472758f3SLionel Sambuc       break;
432472758f3SLionel Sambuc     case PT_DYNAMIC:
433472758f3SLionel Sambuc       dyn = (const Elf_Dyn *)(info->dlpi_addr + hdr->p_vaddr);
434472758f3SLionel Sambuc       while (dyn->d_tag != DT_NULL) {
435472758f3SLionel Sambuc         if (dyn->d_tag == DT_PLTGOT) {
436472758f3SLionel Sambuc           data_base = info->dlpi_addr + dyn->d_un.d_ptr;
437472758f3SLionel Sambuc           break;
438472758f3SLionel Sambuc         }
439472758f3SLionel Sambuc         ++dyn;
440472758f3SLionel Sambuc       }
441472758f3SLionel Sambuc     }
442472758f3SLionel Sambuc   }
443472758f3SLionel Sambuc 
444472758f3SLionel Sambuc   if (eh_frame)
445472758f3SLionel Sambuc     data->addDSO(eh_frame, data_base);
446472758f3SLionel Sambuc 
447472758f3SLionel Sambuc   return 0;
448472758f3SLionel Sambuc }
449472758f3SLionel Sambuc 
rangeCmp(void * context,const void * n1_,const void * n2_)450472758f3SLionel Sambuc static int rangeCmp(void *context, const void *n1_, const void *n2_) {
451b029fb59SBen Gras   const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_;
452b029fb59SBen Gras   const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_;
453472758f3SLionel Sambuc 
454472758f3SLionel Sambuc   if (n1->first_pc < n2->first_pc)
455472758f3SLionel Sambuc     return -1;
456472758f3SLionel Sambuc   if (n1->first_pc > n2->first_pc)
457472758f3SLionel Sambuc     return 1;
458472758f3SLionel Sambuc   assert(n1->last_pc == n2->last_pc);
459472758f3SLionel Sambuc   return 0;
460472758f3SLionel Sambuc }
461472758f3SLionel Sambuc 
rangeCmpKey(void * context,const void * n_,const void * pc_)462472758f3SLionel Sambuc static int rangeCmpKey(void *context, const void *n_, const void *pc_) {
463b029fb59SBen Gras   const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_;
464b029fb59SBen Gras   const LocalAddressSpace::pint_t *pc = (const LocalAddressSpace::pint_t *)pc_;
465472758f3SLionel Sambuc   if (n->last_pc < *pc)
466472758f3SLionel Sambuc     return -1;
467472758f3SLionel Sambuc   if (n->first_pc > *pc)
468472758f3SLionel Sambuc     return 1;
469472758f3SLionel Sambuc   return 0;
470472758f3SLionel Sambuc }
471472758f3SLionel Sambuc 
dsoTableCmp(void * context,const void * n1_,const void * n2_)472472758f3SLionel Sambuc static int dsoTableCmp(void *context, const void *n1_, const void *n2_) {
473b029fb59SBen Gras   const LocalAddressSpace::Range *n1 = (const LocalAddressSpace::Range *)n1_;
474b029fb59SBen Gras   const LocalAddressSpace::Range *n2 = (const LocalAddressSpace::Range *)n2_;
475472758f3SLionel Sambuc 
476472758f3SLionel Sambuc   if (n1->ehframe_base < n2->ehframe_base)
477472758f3SLionel Sambuc     return -1;
478472758f3SLionel Sambuc   if (n1->ehframe_base > n2->ehframe_base)
479472758f3SLionel Sambuc     return 1;
480472758f3SLionel Sambuc   return 0;
481472758f3SLionel Sambuc }
482472758f3SLionel Sambuc 
dsoTableCmpKey(void * context,const void * n_,const void * ptr_)483472758f3SLionel Sambuc static int dsoTableCmpKey(void *context, const void *n_, const void *ptr_) {
484b029fb59SBen Gras   const LocalAddressSpace::Range *n = (const LocalAddressSpace::Range *)n_;
485b029fb59SBen Gras   const LocalAddressSpace::pint_t *ptr = (const LocalAddressSpace::pint_t *)ptr_;
486472758f3SLionel Sambuc   if (n->ehframe_base < *ptr)
487472758f3SLionel Sambuc     return -1;
488472758f3SLionel Sambuc   if (n->ehframe_base > *ptr)
489472758f3SLionel Sambuc     return 1;
490472758f3SLionel Sambuc   return 0;
491472758f3SLionel Sambuc }
492472758f3SLionel Sambuc 
493472758f3SLionel Sambuc } // namespace _Unwind
494472758f3SLionel Sambuc 
495472758f3SLionel Sambuc #endif // __ADDRESSSPACE_HPP__
496