1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2007-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#include <mach/mach_types.h>
27#include <mach/machine.h>
28#include <new>
29
30#include "libunwind.h"
31#include "libunwind_priv.h"
32
33#include "UnwindCursor.hpp"
34
35
36
37using namespace libunwind;
38
39
40// setup debug logging hooks
41INITIALIZE_DEBUG_PRINT_API
42INITIALIZE_DEBUG_PRINT_UNWINDING
43
44
45#if __ppc__ || __i386__ ||  __x86_64__
46
47// internal object to represent this processes address space
48static LocalAddressSpace sThisAddressSpace;
49
50///
51/// record the registers and stack position of the caller
52///
53extern int unw_getcontext(unw_context_t*);
54// note: unw_getcontext() implemented in assembly
55
56
57///
58/// create a cursor of a thread in this process given 'context' recorded by unw_getcontext()
59///
60EXPORT int unw_init_local(unw_cursor_t* cursor, unw_context_t* context)
61{
62	DEBUG_PRINT_API("unw_init_local(cursor=%p, context=%p)\n", cursor, context);
63	// use "placement new" to allocate UnwindCursor in the cursor buffer
64#if __i386__
65	new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86>(context, sThisAddressSpace);
66#elif __x86_64__
67	new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86_64>(context, sThisAddressSpace);
68#elif __ppc__
69	new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_ppc>(context, sThisAddressSpace);
70#endif
71	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
72	co->setInfoBasedOnIPRegister();
73
74	return UNW_ESUCCESS;
75}
76
77#if UNW_REMOTE
78
79EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace;
80
81///
82/// create a cursor into a thread in another process
83///
84EXPORT int unw_init_remote_thread(unw_cursor_t* cursor, unw_addr_space_t as, thread_t thread)
85{
86	// special case: unw_init_remote(xx, unw_local_addr_space, xx)
87	if ( as == (unw_addr_space_t)&sThisAddressSpace )
88		return unw_init_local(cursor, NULL); //FIXME
89
90	// use "placement new" to allocate UnwindCursor in the cursor buffer
91	switch ( as->cpuType ) {
92		case CPU_TYPE_I386:
93			new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >,
94											Registers_x86>(((unw_addr_space_i386*)as)->oas, thread);
95			break;
96		case CPU_TYPE_X86_64:
97			new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer64<LittleEndian> >,
98											Registers_x86_64>(((unw_addr_space_x86_64*)as)->oas, thread);
99			break;
100		case CPU_TYPE_POWERPC:
101			new ((void*)cursor) UnwindCursor<OtherAddressSpace<Pointer32<BigEndian> >,
102											Registers_ppc>(((unw_addr_space_ppc*)as)->oas, thread);
103			break;
104		default:
105			return UNW_EUNSPEC;
106	}
107	return UNW_ESUCCESS;
108}
109
110static bool rosetta(task_t task)
111{
112	return false; // FIXME
113}
114
115static bool is64bit(task_t task)
116{
117	return false; // FIXME
118}
119
120///
121/// create an address_space object for use in examining another task
122///
123EXPORT unw_addr_space_t unw_create_addr_space_for_task(task_t task)
124{
125#if __i386__
126	if ( rosetta(task) ) {
127		unw_addr_space_ppc* as = new unw_addr_space_ppc(task);
128		as->taskPort = task;
129		as->cpuType = CPU_TYPE_POWERPC;
130		//as->oas
131	}
132	else if ( is64bit(task) ) {
133		unw_addr_space_x86_64* as = new unw_addr_space_x86_64(task);
134		as->taskPort = task;
135		as->cpuType = CPU_TYPE_X86_64;
136		//as->oas
137	}
138	else  {
139		unw_addr_space_i386* as = new unw_addr_space_i386(task);
140		as->taskPort = task;
141		as->cpuType = CPU_TYPE_I386;
142		//as->oas
143	}
144#else
145	// FIXME
146#endif
147}
148
149
150///
151/// delete an address_space object
152///
153EXPORT void unw_destroy_addr_space(unw_addr_space_t asp)
154{
155	switch ( asp->cpuType ) {
156#if __i386__ || __x86_64__
157		case CPU_TYPE_I386:
158			{
159				unw_addr_space_i386* as = (unw_addr_space_i386*)asp;
160				delete as;
161			}
162			break;
163		case CPU_TYPE_X86_64:
164			{
165				unw_addr_space_x86_64* as = (unw_addr_space_x86_64*)asp;
166				delete as;
167			}
168			break;
169#endif
170		case CPU_TYPE_POWERPC:
171			{
172				unw_addr_space_ppc* as = (unw_addr_space_ppc*)asp;
173				delete as;
174			}
175			break;
176	}
177}
178#endif // UNW_REMOTE
179
180
181///
182/// get value of specified register at cursor position in stack frame
183///
184EXPORT int unw_get_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t* value)
185{
186	DEBUG_PRINT_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
187	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
188	if ( co->validReg(regNum) ) {
189		*value = co->getReg(regNum);
190		return UNW_ESUCCESS;
191	}
192	return UNW_EBADREG;
193}
194
195
196///
197/// set value of specified register at cursor position in stack frame
198///
199EXPORT int unw_set_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t value)
200{
201	DEBUG_PRINT_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", cursor, regNum, value);
202	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
203	if ( co->validReg(regNum) ) {
204		co->setReg(regNum, value);
205		// specical case altering IP to re-find info (being called by personality function)
206		if ( regNum == UNW_REG_IP ) {
207			unw_proc_info_t info;
208			co->getInfo(&info);
209			uint64_t orgArgSize = info.gp;
210			uint64_t orgFuncStart = info.start_ip;
211			co->setInfoBasedOnIPRegister(false);
212			// and adjust REG_SP if there was a DW_CFA_GNU_args_size
213			if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) )
214				co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize);
215		}
216		return UNW_ESUCCESS;
217	}
218	return UNW_EBADREG;
219}
220
221
222///
223/// get value of specified float register at cursor position in stack frame
224///
225EXPORT int unw_get_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t* value)
226{
227	DEBUG_PRINT_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
228	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
229	if ( co->validFloatReg(regNum) ) {
230		*value = co->getFloatReg(regNum);
231		return UNW_ESUCCESS;
232	}
233	return UNW_EBADREG;
234}
235
236///
237/// set value of specified float register at cursor position in stack frame
238///
239EXPORT int unw_set_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t value)
240{
241	DEBUG_PRINT_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", cursor, regNum, value);
242	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
243	if ( co->validFloatReg(regNum) ) {
244		co->setFloatReg(regNum, value);
245		return UNW_ESUCCESS;
246	}
247	return UNW_EBADREG;
248}
249
250
251///
252/// move cursor to next frame
253///
254EXPORT int unw_step(unw_cursor_t* cursor)
255{
256	DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor);
257	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
258	return co->step();
259}
260
261
262///
263/// get unwind info at cursor position in stack frame
264///
265EXPORT int unw_get_proc_info(unw_cursor_t* cursor, unw_proc_info_t* info)
266{
267	DEBUG_PRINT_API("unw_get_proc_info(cursor=%p, &info=%p)\n", cursor, info);
268	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
269	co->getInfo(info);
270	if ( info->end_ip == 0 )
271		return UNW_ENOINFO;
272	else
273		return UNW_ESUCCESS;
274}
275
276///
277/// resume execution at cursor position (aka longjump)
278///
279EXPORT int unw_resume(unw_cursor_t* cursor)
280{
281	DEBUG_PRINT_API("unw_resume(cursor=%p)\n", cursor);
282	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
283	co->jumpto();
284	return UNW_EUNSPEC;
285}
286
287
288///
289/// get name of function at cursor position in stack frame
290///
291EXPORT int unw_get_proc_name(unw_cursor_t* cursor, char* buf, size_t bufLen, unw_word_t* offset)
292{
293	DEBUG_PRINT_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%ld)\n", cursor, buf, bufLen);
294	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
295	if ( co->getFunctionName(buf, bufLen, offset) )
296		return UNW_ESUCCESS;
297	else
298		return UNW_EUNSPEC;
299}
300
301
302///
303/// checks if a register is a floating-point register
304///
305EXPORT int unw_is_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum)
306{
307	DEBUG_PRINT_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", cursor, regNum);
308	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
309	return co->validFloatReg(regNum);
310}
311
312
313///
314/// checks if a register is a floating-point register
315///
316EXPORT const char* unw_regname(unw_cursor_t* cursor, unw_regnum_t regNum)
317{
318	DEBUG_PRINT_API("unw_regname(cursor=%p, regNum=%d)\n", cursor, regNum);
319	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
320	return co->getRegisterName(regNum);
321}
322
323
324///
325/// checks if current frame is signal trampoline
326///
327EXPORT int unw_is_signal_frame(unw_cursor_t* cursor)
328{
329	DEBUG_PRINT_API("unw_is_signal_frame(cursor=%p)\n", cursor);
330	AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
331	return co->isSignalFrame();
332}
333
334#if !FOR_DYLD
335///
336/// SPI: walks cached dwarf entries
337///
338EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh))
339{
340	DEBUG_PRINT_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func);
341	DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
342}
343#endif // !FOR_DYLD
344
345
346#if !FOR_DYLD
347//
348// IPI: for __register_frame()
349//
350void _unw_add_dynamic_fde(unw_word_t fde)
351{
352	CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
353	CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
354	const char* message = CFI_Parser<LocalAddressSpace>::decodeFDE(sThisAddressSpace, (LocalAddressSpace::pint_t)fde, & fdeInfo, &cieInfo);
355	if ( message == NULL ) {
356		// dynamically registered FDEs don't have a mach_header group they are in.  Use fde as mh_group
357		unw_word_t mh_group = fdeInfo.fdeStart;
358		DwarfFDECache<LocalAddressSpace>::add(mh_group, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart);
359	}
360	else {
361		DEBUG_MESSAGE("_unw_add_dynamic_fde: bad fde: %s", message);
362	}
363}
364
365//
366// IPI: for __deregister_frame()
367//
368void _unw_remove_dynamic_fde(unw_word_t fde)
369{
370	// fde is own mh_group
371	DwarfFDECache<LocalAddressSpace>::removeAllIn(fde);
372}
373#endif
374
375
376#endif // __ppc__ || __i386__ ||  __x86_64__
377
378
379
380
381