1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2008-2011 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 <Availability.h>
40
41#include "FileAbstraction.hpp"
42#include "libunwind.h"
43#include "InternalMacros.h"
44#include "dwarf2.h"
45
46
47#if __i386__ && defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
48// For iOS simulator to link, we need a __dyld section
49// We need one to access private _dyld_func_lookup function.
50
51struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); };
52
53static volatile struct __DATA__dyld  myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL };
54
55
56static int my_dyld_func_lookup(const char* dyld_func_name, void **address)
57{
58	return (*myDyldSection.lookup)(dyld_func_name, address);
59}
60
61bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
62{
63    static void* (*p)(void*, dyld_unwind_sections*) = NULL;
64
65	if(p == NULL)
66	    my_dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p);
67	return p(addr, info);
68}
69#endif
70
71
72namespace libunwind {
73
74///
75/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
76/// in the same process.  It compiles away and making local unwinds very fast.
77///
78class LocalAddressSpace
79{
80public:
81
82	#if __LP64__
83		typedef uint64_t	pint_t;
84		typedef  int64_t	sint_t;
85	#else
86		typedef uint32_t	pint_t;
87		typedef  int32_t	sint_t;
88	#endif
89		uint8_t			get8(pint_t addr)	{ return *((uint8_t*)addr); }
90		uint16_t		get16(pint_t addr)	{ return *((uint16_t*)addr); }
91		uint32_t		get32(pint_t addr)	{ return *((uint32_t*)addr); }
92		uint64_t		get64(pint_t addr)	{ return *((uint64_t*)addr); }
93		double			getDouble(pint_t addr)	{ return *((double*)addr); }
94		v128			getVector(pint_t addr)	{ return *((v128*)addr); }
95		uintptr_t		getP(pint_t addr);
96	static uint64_t		getULEB128(pint_t& addr, pint_t end);
97	static int64_t		getSLEB128(pint_t& addr, pint_t end);
98
99		pint_t			getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
100		bool			findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
101		bool			findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart);
102
103};
104
105LocalAddressSpace sThisAddress;
106
107inline uintptr_t LocalAddressSpace::getP(pint_t addr)
108{
109#if __LP64__
110	return get64(addr);
111#else
112	return get32(addr);
113#endif
114}
115
116/* Read a ULEB128 into a 64-bit word.   */
117inline uint64_t
118LocalAddressSpace::getULEB128(pint_t& addr, pint_t end)
119{
120	const uint8_t* p = (uint8_t*)addr;
121	const uint8_t* pend = (uint8_t*)end;
122	uint64_t result = 0;
123	int bit = 0;
124	do  {
125		uint64_t b;
126
127		if ( p == pend )
128			ABORT("truncated uleb128 expression");
129
130		b = *p & 0x7f;
131
132		if (bit >= 64 || b << bit >> bit != b) {
133			ABORT("malformed uleb128 expression");
134		}
135		else {
136			result |= b << bit;
137			bit += 7;
138		}
139	} while ( *p++ >= 0x80 );
140	addr = (pint_t)p;
141	return result;
142}
143
144/* Read a SLEB128 into a 64-bit word.  */
145inline int64_t
146LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end)
147{
148	const uint8_t* p = (uint8_t*)addr;
149	int64_t result = 0;
150	int bit = 0;
151	uint8_t byte;
152	do {
153		byte = *p++;
154		result |= ((byte & 0x7f) << bit);
155		bit += 7;
156	} while (byte & 0x80);
157	// sign extend negative numbers
158	if ( (byte & 0x40) != 0 )
159		result |= (-1LL) << bit;
160	addr = (pint_t)p;
161	return result;
162}
163
164LocalAddressSpace::pint_t
165LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding)
166{
167	pint_t startAddr = addr;
168	const uint8_t* p = (uint8_t*)addr;
169	pint_t result;
170
171	// first get value
172	switch (encoding & 0x0F) {
173		case DW_EH_PE_ptr:
174			result = getP(addr);
175			p += sizeof(pint_t);
176			addr = (pint_t)p;
177			break;
178		case DW_EH_PE_uleb128:
179			result = getULEB128(addr, end);
180			break;
181		case DW_EH_PE_udata2:
182			result = get16(addr);
183			p += 2;
184			addr = (pint_t)p;
185			break;
186		case DW_EH_PE_udata4:
187			result = get32(addr);
188			p += 4;
189			addr = (pint_t)p;
190			break;
191		case DW_EH_PE_udata8:
192			result = get64(addr);
193			p += 8;
194			addr = (pint_t)p;
195			break;
196		case DW_EH_PE_sleb128:
197			result = getSLEB128(addr, end);
198			break;
199		case DW_EH_PE_sdata2:
200			result = (int16_t)get16(addr);
201			p += 2;
202			addr = (pint_t)p;
203			break;
204		case DW_EH_PE_sdata4:
205			result = (int32_t)get32(addr);
206			p += 4;
207			addr = (pint_t)p;
208			break;
209		case DW_EH_PE_sdata8:
210			result = get64(addr);
211			p += 8;
212			addr = (pint_t)p;
213			break;
214		default:
215			ABORT("unknown pointer encoding");
216	}
217
218	// then add relative offset
219	switch ( encoding & 0x70 ) {
220		case DW_EH_PE_absptr:
221			// do nothing
222			break;
223		case DW_EH_PE_pcrel:
224			result += startAddr;
225			break;
226		case DW_EH_PE_textrel:
227			ABORT("DW_EH_PE_textrel pointer encoding not supported");
228			break;
229		case DW_EH_PE_datarel:
230			ABORT("DW_EH_PE_datarel pointer encoding not supported");
231			break;
232		case DW_EH_PE_funcrel:
233			ABORT("DW_EH_PE_funcrel pointer encoding not supported");
234			break;
235		case DW_EH_PE_aligned:
236			ABORT("DW_EH_PE_aligned pointer encoding not supported");
237			break;
238		default:
239			ABORT("unknown pointer encoding");
240			break;
241	}
242
243	if ( encoding & DW_EH_PE_indirect )
244		result = getP(result);
245
246	return result;
247}
248
249
250inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart)
251{
252	dyld_unwind_sections info;
253	if ( _dyld_find_unwind_sections((void*)addr, &info) ) {
254		mh				= (pint_t)info.mh;
255		dwarfStart		= (pint_t)info.dwarf_section;
256		dwarfLen		= (pint_t)info.dwarf_section_length;
257		compactStart	= (pint_t)info.compact_unwind_section;
258		return true;
259	}
260	return false;
261}
262
263
264inline bool	LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
265{
266	dl_info dyldInfo;
267	if ( dladdr((void*)addr, &dyldInfo) ) {
268		if ( dyldInfo.dli_sname != NULL ) {
269			strlcpy(buf, dyldInfo.dli_sname, bufLen);
270			*offset = (addr - (pint_t)dyldInfo.dli_saddr);
271			return true;
272		}
273	}
274	return false;
275}
276
277
278
279#if UNW_REMOTE
280
281///
282/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
283/// in the another process.  The other process can be a different endianness and a different
284/// pointer size and is handled by the P template parameter.
285///
286template <typename P>
287class OtherAddressSpace
288{
289public:
290						OtherAddressSpace(task_t task) : fTask(task) {}
291
292		typedef typename P::uint_t	pint_t;
293
294		uint8_t			get8(pint_t addr);
295		uint16_t		get16(pint_t addr);
296		uint32_t		get32(pint_t addr);
297		uint64_t		get64(pint_t addr);
298		pint_t			getP(pint_t addr);
299		uint64_t		getULEB128(pint_t& addr, pint_t end);
300		int64_t			getSLEB128(pint_t& addr, pint_t end);
301		pint_t			getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
302		bool			findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
303		bool			findUnwindSections(pint_t addr, unwind_sections& info);
304private:
305		void*			localCopy(pint_t addr);
306
307
308		task_t			fTask;
309};
310
311
312template <typename P>
313uint8_t OtherAddressSpace<P>::get8(pint_t addr)
314{
315	return *((uint8_t*)localCopy(addr));
316}
317
318template <typename P>
319uint16_t OtherAddressSpace<P>::get16(pint_t addr)
320{
321	return P::E::get16(*(uint16_t*)localCopy(addr));
322}
323
324template <typename P>
325uint32_t OtherAddressSpace<P>::get32(pint_t addr)
326{
327	return P::E::get32(*(uint32_t*)localCopy(addr));
328}
329
330template <typename P>
331uint64_t OtherAddressSpace<P>::get64(pint_t addr)
332{
333	return P::E::get64(*(uint64_t*)localCopy(addr));
334}
335
336template <typename P>
337typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr)
338{
339	return P::getP(*(uint64_t*)localCopy(addr));
340}
341
342template <typename P>
343uint64_t OtherAddressSpace<P>::getULEB128(pint_t& addr, pint_t end)
344{
345	uintptr_t size = (end - addr);
346	LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr);
347	LocalAddressSpace::pint_t sladdr = laddr;
348	uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr+size);
349	addr += (laddr-sladdr);
350	return result;
351}
352
353template <typename P>
354int64_t OtherAddressSpace<P>::getSLEB128(pint_t& addr, pint_t end)
355{
356	uintptr_t size = (end - addr);
357	LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr);
358	LocalAddressSpace::pint_t sladdr = laddr;
359	uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr+size);
360	addr += (laddr-sladdr);
361	return result;
362}
363
364template <typename P>
365void* OtherAddressSpace<P>::localCopy(pint_t addr)
366{
367	// FIX ME
368}
369
370template <typename P>
371bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
372{
373	// FIX ME
374}
375
376
377
378///
379/// unw_addr_space is the base class that abstract unw_addr_space_t type in libunwind.h points to.
380///
381struct unw_addr_space
382{
383	cpu_type_t				cpuType;
384	task_t					taskPort;
385};
386
387
388///
389/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points to when examining
390/// a 32-bit intel process.
391///
392struct unw_addr_space_i386 : public unw_addr_space
393{
394													unw_addr_space_i386(task_t task) : oas(task) {}
395	OtherAddressSpace<Pointer32<LittleEndian> >		oas;
396};
397
398
399///
400/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t points to when examining
401/// a 64-bit intel process.
402///
403struct unw_addr_space_x86_64 : public unw_addr_space
404{
405													unw_addr_space_x86_64(task_t task) : oas(task) {}
406	OtherAddressSpace<Pointer64<LittleEndian> >		oas;
407};
408
409
410///
411/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining
412/// a 32-bit PowerPC process.
413///
414struct unw_addr_space_ppc : public unw_addr_space
415{
416													unw_addr_space_ppc(task_t task) : oas(task) {}
417	OtherAddressSpace<Pointer32<BigEndian> >		oas;
418};
419
420
421#endif // UNW_REMOTE
422
423
424} // namespace libunwind
425
426
427#endif // __ADDRESSSPACE_HPP__
428
429
430
431
432