]>
Commit | Line | Data |
---|---|---|
afe874b1 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2008 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | // | |
26 | // C++ interface to lower levels of libuwind | |
27 | // | |
28 | ||
29 | #ifndef __ADDRESSSPACE_HPP__ | |
30 | #define __ADDRESSSPACE_HPP__ | |
31 | ||
32 | #include <stdint.h> | |
33 | #include <stdio.h> | |
34 | #include <stdlib.h> | |
35 | #include <dlfcn.h> | |
36 | #include <mach-o/loader.h> | |
37 | #include <mach-o/getsect.h> | |
38 | #include <mach-o/dyld_priv.h> | |
39 | #include <mach/i386/thread_status.h> | |
40 | #include <Availability.h> | |
41 | ||
42 | #include "FileAbstraction.hpp" | |
43 | #include "libunwind.h" | |
44 | #include "InternalMacros.h" | |
45 | #include "dwarf2.h" | |
46 | ||
47 | ||
48 | #if 0 | |
49 | #if __i386__ || __x86_64__ | |
50 | // In 10.6 and later i386 and x86_64 don't have a __dyld section | |
51 | // We need one to access private _dyld_func_lookup function. | |
52 | ||
53 | struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); }; | |
54 | ||
55 | static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL }; | |
56 | ||
57 | ||
58 | static int my_dyld_func_lookup(const char* dyld_func_name, void **address) | |
59 | { | |
60 | return (*myDyldSection.lookup)(dyld_func_name, address); | |
61 | } | |
62 | #else | |
63 | #define my_dyld_func_lookup _dyld_func_lookup | |
64 | #endif | |
65 | ||
66 | ||
67 | bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) | |
68 | { | |
69 | static void* (*p)(void*, dyld_unwind_sections*) = NULL; | |
70 | ||
71 | if(p == NULL) | |
72 | my_dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); | |
73 | return p(addr, info); | |
74 | } | |
75 | #endif // 0 | |
76 | ||
77 | ||
78 | ||
79 | namespace libunwind { | |
80 | ||
81 | /// | |
82 | /// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread | |
83 | /// in the same process. It compiles away and making local unwinds very fast. | |
84 | /// | |
85 | class LocalAddressSpace | |
86 | { | |
87 | public: | |
88 | ||
89 | #if __LP64__ | |
90 | typedef uint64_t pint_t; | |
91 | typedef int64_t sint_t; | |
92 | #else | |
93 | typedef uint32_t pint_t; | |
94 | typedef int32_t sint_t; | |
95 | #endif | |
96 | uint8_t get8(pint_t addr) { return *((uint8_t*)addr); } | |
97 | uint16_t get16(pint_t addr) { return *((uint16_t*)addr); } | |
98 | uint32_t get32(pint_t addr) { return *((uint32_t*)addr); } | |
99 | uint64_t get64(pint_t addr) { return *((uint64_t*)addr); } | |
100 | double getDouble(pint_t addr) { return *((double*)addr); } | |
101 | v128 getVector(pint_t addr) { return *((v128*)addr); } | |
102 | uintptr_t getP(pint_t addr); | |
103 | static uint64_t getULEB128(pint_t& addr, pint_t end); | |
104 | static int64_t getSLEB128(pint_t& addr, pint_t end); | |
105 | ||
106 | pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); | |
107 | bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); | |
108 | bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); | |
109 | ||
110 | }; | |
111 | ||
112 | LocalAddressSpace sThisAddress; | |
113 | ||
114 | inline uintptr_t LocalAddressSpace::getP(pint_t addr) | |
115 | { | |
116 | #if __LP64__ | |
117 | return get64(addr); | |
118 | #else | |
119 | return get32(addr); | |
120 | #endif | |
121 | } | |
122 | ||
123 | /* Read a ULEB128 into a 64-bit word. */ | |
124 | inline uint64_t | |
125 | LocalAddressSpace::getULEB128(pint_t& addr, pint_t end) | |
126 | { | |
127 | const uint8_t* p = (uint8_t*)addr; | |
128 | const uint8_t* pend = (uint8_t*)end; | |
129 | uint64_t result = 0; | |
130 | int bit = 0; | |
131 | do { | |
132 | uint64_t b; | |
133 | ||
134 | if ( p == pend ) | |
135 | ABORT("truncated uleb128 expression"); | |
136 | ||
137 | b = *p & 0x7f; | |
138 | ||
139 | if (bit >= 64 || b << bit >> bit != b) { | |
140 | ABORT("malformed uleb128 expression"); | |
141 | } | |
142 | else { | |
143 | result |= b << bit; | |
144 | bit += 7; | |
145 | } | |
146 | } while ( *p++ >= 0x80 ); | |
147 | addr = (pint_t)p; | |
148 | return result; | |
149 | } | |
150 | ||
151 | /* Read a SLEB128 into a 64-bit word. */ | |
152 | inline int64_t | |
153 | LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end) | |
154 | { | |
155 | const uint8_t* p = (uint8_t*)addr; | |
156 | int64_t result = 0; | |
157 | int bit = 0; | |
158 | uint8_t byte; | |
159 | do { | |
160 | byte = *p++; | |
161 | result |= ((byte & 0x7f) << bit); | |
162 | bit += 7; | |
163 | } while (byte & 0x80); | |
164 | // sign extend negative numbers | |
165 | if ( (byte & 0x40) != 0 ) | |
166 | result |= (-1LL) << bit; | |
167 | addr = (pint_t)p; | |
168 | return result; | |
169 | } | |
170 | ||
171 | LocalAddressSpace::pint_t | |
172 | LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) | |
173 | { | |
174 | pint_t startAddr = addr; | |
175 | const uint8_t* p = (uint8_t*)addr; | |
176 | pint_t result; | |
177 | ||
178 | // first get value | |
179 | switch (encoding & 0x0F) { | |
180 | case DW_EH_PE_ptr: | |
181 | result = getP(addr); | |
182 | p += sizeof(pint_t); | |
183 | addr = (pint_t)p; | |
184 | break; | |
185 | case DW_EH_PE_uleb128: | |
186 | result = getULEB128(addr, end); | |
187 | break; | |
188 | case DW_EH_PE_udata2: | |
189 | result = get16(addr); | |
190 | p += 2; | |
191 | addr = (pint_t)p; | |
192 | break; | |
193 | case DW_EH_PE_udata4: | |
194 | result = get32(addr); | |
195 | p += 4; | |
196 | addr = (pint_t)p; | |
197 | break; | |
198 | case DW_EH_PE_udata8: | |
199 | result = get64(addr); | |
200 | p += 8; | |
201 | addr = (pint_t)p; | |
202 | break; | |
203 | case DW_EH_PE_sleb128: | |
204 | result = getSLEB128(addr, end); | |
205 | break; | |
206 | case DW_EH_PE_sdata2: | |
207 | result = (int16_t)get16(addr); | |
208 | p += 2; | |
209 | addr = (pint_t)p; | |
210 | break; | |
211 | case DW_EH_PE_sdata4: | |
212 | result = (int32_t)get32(addr); | |
213 | p += 4; | |
214 | addr = (pint_t)p; | |
215 | break; | |
216 | case DW_EH_PE_sdata8: | |
217 | result = get64(addr); | |
218 | p += 8; | |
219 | addr = (pint_t)p; | |
220 | break; | |
221 | default: | |
222 | ABORT("unknown pointer encoding"); | |
223 | } | |
224 | ||
225 | // then add relative offset | |
226 | switch ( encoding & 0x70 ) { | |
227 | case DW_EH_PE_absptr: | |
228 | // do nothing | |
229 | break; | |
230 | case DW_EH_PE_pcrel: | |
231 | result += startAddr; | |
232 | break; | |
233 | case DW_EH_PE_textrel: | |
234 | ABORT("DW_EH_PE_textrel pointer encoding not supported"); | |
235 | break; | |
236 | case DW_EH_PE_datarel: | |
237 | ABORT("DW_EH_PE_datarel pointer encoding not supported"); | |
238 | break; | |
239 | case DW_EH_PE_funcrel: | |
240 | ABORT("DW_EH_PE_funcrel pointer encoding not supported"); | |
241 | break; | |
242 | case DW_EH_PE_aligned: | |
243 | ABORT("DW_EH_PE_aligned pointer encoding not supported"); | |
244 | break; | |
245 | default: | |
246 | ABORT("unknown pointer encoding"); | |
247 | break; | |
248 | } | |
249 | ||
250 | if ( encoding & DW_EH_PE_indirect ) | |
251 | result = getP(result); | |
252 | ||
253 | return result; | |
254 | } | |
255 | ||
256 | ||
257 | inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) | |
258 | { | |
259 | dyld_unwind_sections info; | |
260 | if ( _dyld_find_unwind_sections((void*)addr, &info) ) { | |
261 | mh = (pint_t)info.mh; | |
262 | dwarfStart = (pint_t)info.dwarf_section; | |
263 | dwarfLen = (pint_t)info.dwarf_section_length; | |
264 | compactStart = (pint_t)info.compact_unwind_section; | |
265 | return true; | |
266 | } | |
267 | return false; | |
268 | } | |
269 | ||
270 | ||
271 | inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) | |
272 | { | |
273 | dl_info dyldInfo; | |
274 | if ( dladdr((void*)addr, &dyldInfo) ) { | |
275 | if ( dyldInfo.dli_sname != NULL ) { | |
276 | strlcpy(buf, dyldInfo.dli_sname, bufLen); | |
277 | *offset = (addr - (pint_t)dyldInfo.dli_saddr); | |
278 | return true; | |
279 | } | |
280 | } | |
281 | return false; | |
282 | } | |
283 | ||
284 | ||
285 | ||
286 | #if UNW_REMOTE | |
287 | ||
288 | /// | |
289 | /// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread | |
290 | /// in the another process. The other process can be a different endianness and a different | |
291 | /// pointer size and is handled by the P template parameter. | |
292 | /// | |
293 | template <typename P> | |
294 | class OtherAddressSpace | |
295 | { | |
296 | public: | |
297 | OtherAddressSpace(task_t task) : fTask(task) {} | |
298 | ||
299 | typedef typename P::uint_t pint_t; | |
300 | ||
301 | uint8_t get8(pint_t addr); | |
302 | uint16_t get16(pint_t addr); | |
303 | uint32_t get32(pint_t addr); | |
304 | uint64_t get64(pint_t addr); | |
305 | pint_t getP(pint_t addr); | |
306 | uint64_t getULEB128(pint_t& addr, pint_t end); | |
307 | int64_t getSLEB128(pint_t& addr, pint_t end); | |
308 | pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); | |
309 | bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); | |
310 | bool findUnwindSections(pint_t addr, unwind_sections& info); | |
311 | private: | |
312 | void* localCopy(pint_t addr); | |
313 | ||
314 | ||
315 | task_t fTask; | |
316 | }; | |
317 | ||
318 | ||
319 | template <typename P> | |
320 | uint8_t OtherAddressSpace<P>::get8(pint_t addr) | |
321 | { | |
322 | return *((uint8_t*)localCopy(addr)); | |
323 | } | |
324 | ||
325 | template <typename P> | |
326 | uint16_t OtherAddressSpace<P>::get16(pint_t addr) | |
327 | { | |
328 | return P::E::get16(*(uint16_t*)localCopy(addr)); | |
329 | } | |
330 | ||
331 | template <typename P> | |
332 | uint32_t OtherAddressSpace<P>::get32(pint_t addr) | |
333 | { | |
334 | return P::E::get32(*(uint32_t*)localCopy(addr)); | |
335 | } | |
336 | ||
337 | template <typename P> | |
338 | uint64_t OtherAddressSpace<P>::get64(pint_t addr) | |
339 | { | |
340 | return P::E::get64(*(uint64_t*)localCopy(addr)); | |
341 | } | |
342 | ||
343 | template <typename P> | |
344 | typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr) | |
345 | { | |
346 | return P::getP(*(uint64_t*)localCopy(addr)); | |
347 | } | |
348 | ||
349 | template <typename P> | |
350 | uint64_t OtherAddressSpace<P>::getULEB128(pint_t& addr, pint_t end) | |
351 | { | |
352 | uintptr_t size = (end - addr); | |
353 | LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); | |
354 | LocalAddressSpace::pint_t sladdr = laddr; | |
355 | uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr+size); | |
356 | addr += (laddr-sladdr); | |
357 | return result; | |
358 | } | |
359 | ||
360 | template <typename P> | |
361 | int64_t OtherAddressSpace<P>::getSLEB128(pint_t& addr, pint_t end) | |
362 | { | |
363 | uintptr_t size = (end - addr); | |
364 | LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); | |
365 | LocalAddressSpace::pint_t sladdr = laddr; | |
366 | uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr+size); | |
367 | addr += (laddr-sladdr); | |
368 | return result; | |
369 | } | |
370 | ||
371 | template <typename P> | |
372 | void* OtherAddressSpace<P>::localCopy(pint_t addr) | |
373 | { | |
374 | // FIX ME | |
375 | } | |
376 | ||
377 | template <typename P> | |
378 | bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) | |
379 | { | |
380 | // FIX ME | |
381 | } | |
382 | ||
383 | ||
384 | ||
385 | /// | |
386 | /// unw_addr_space is the base class that abstract unw_addr_space_t type in libunwind.h points to. | |
387 | /// | |
388 | struct unw_addr_space | |
389 | { | |
390 | cpu_type_t cpuType; | |
391 | task_t taskPort; | |
392 | }; | |
393 | ||
394 | ||
395 | /// | |
396 | /// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points to when examining | |
397 | /// a 32-bit intel process. | |
398 | /// | |
399 | struct unw_addr_space_i386 : public unw_addr_space | |
400 | { | |
401 | unw_addr_space_i386(task_t task) : oas(task) {} | |
402 | OtherAddressSpace<Pointer32<LittleEndian> > oas; | |
403 | }; | |
404 | ||
405 | ||
406 | /// | |
407 | /// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t points to when examining | |
408 | /// a 64-bit intel process. | |
409 | /// | |
410 | struct unw_addr_space_x86_64 : public unw_addr_space | |
411 | { | |
412 | unw_addr_space_x86_64(task_t task) : oas(task) {} | |
413 | OtherAddressSpace<Pointer64<LittleEndian> > oas; | |
414 | }; | |
415 | ||
416 | ||
417 | /// | |
418 | /// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining | |
419 | /// a 32-bit PowerPC process. | |
420 | /// | |
421 | struct unw_addr_space_ppc : public unw_addr_space | |
422 | { | |
423 | unw_addr_space_ppc(task_t task) : oas(task) {} | |
424 | OtherAddressSpace<Pointer32<BigEndian> > oas; | |
425 | }; | |
426 | ||
427 | ||
428 | #endif // UNW_REMOTE | |
429 | ||
430 | ||
431 | } // namespace libunwind | |
432 | ||
433 | ||
434 | ||
435 | #endif // __ADDRESSSPACE_HPP__ | |
436 | ||
437 | ||
438 | ||
439 |