1
2//
3// TEST-OPTIONS:  -arch i386 -Wl,-no_compact_unwind
4// TEST-OPTIONS:  -arch x86_64 -Wl,-no_compact_unwind
5//
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/mman.h>
11#include <mach/mach.h>
12#include <libunwind.h>
13
14#include "unwind.h"
15
16// private keymgr stuff
17#define KEYMGR_GCC3_DW2_OBJ_LIST   302
18extern "C" {
19	extern void	 _keymgr_set_and_unlock_processwide_ptr(int key, void* ptr);
20	extern void* _keymgr_get_and_lock_processwide_ptr(int key);
21};
22
23
24// undocumented libgcc "struct object"
25struct libgcc_object
26{
27	void*			start;
28	void*			unused1;
29	void*			unused2;
30	void*			fde;
31	unsigned long	encoding;
32	void*			fde_end;
33	libgcc_object*	next;
34};
35
36// undocumented libgcc "struct km_object_info" referenced by KEYMGR_GCC3_DW2_OBJ_LIST
37struct libgcc_object_info {
38  struct libgcc_object*		seen_objects;
39  struct libgcc_object*		unseen_objects;
40  unsigned					spare[2];
41};
42
43
44// Some application have reverse engineer libgcc and do the equivalent
45// of my_keymgr_register_frame() to register dynamically created code.
46static void my_keymgr_register_frame(void* fde)
47{
48	// acquire exclusive access to libgcc_object_info
49	libgcc_object_info* head = (libgcc_object_info*)_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
50
51	// create new object
52	libgcc_object* ob = (libgcc_object*)calloc(sizeof(libgcc_object), 1);
53	ob->fde = fde;
54
55	// insert new object into linked list
56	ob->next = head->unseen_objects;
57	head->unseen_objects = ob;
58
59	// release libgcc_object_info
60	_keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head);
61}
62
63
64
65void check(bool shouldFindMain)
66{
67	unw_cursor_t cursor;
68	unw_context_t uc;
69	unw_word_t	offset;
70	bool		foundMain = false;
71	char	functionName[256];
72	//fprintf(stderr, "check(shouldFindMain=%d)\n", shouldFindMain);
73
74	int level = 1;
75	unw_getcontext(&uc);
76	unw_init_local(&cursor, &uc);
77	do {
78		unw_get_proc_name(&cursor, functionName, 64, &offset);
79		//fprintf(stderr, "level=%d in function: %s\n", level, functionName);
80		if ( strcmp(functionName, "main") == 0 )
81			foundMain = true;
82		++level;
83		strcpy(functionName, "junk");
84	} while (unw_step(&cursor) > 0);
85
86	if ( foundMain == shouldFindMain )
87		return;
88
89	fprintf(stderr, "failing because shouldFindMain=%d and foundMain=%d\n", shouldFindMain, foundMain);
90	exit(1);
91}
92
93
94
95
96void foo(bool shouldFindMain, void (*func)(bool))
97{
98	(*func)(shouldFindMain);
99	__asm__ volatile("");  // disable tail call optimization
100}
101
102typedef void (*checkFuncProc)(bool shouldFindMain, void (*func)(bool));
103
104
105int main()
106{
107	struct dwarf_eh_bases junk;
108	const void* foo_eh_src = _Unwind_Find_FDE((void*)((long)&foo + 1), &junk);
109	// copy foo and its fde to a dynamically allocated buffer
110	unsigned long deltaToFDE = (uintptr_t)foo_eh_src - (uintptr_t)&foo;
111	unsigned long size = deltaToFDE + 100;
112	size = ((size + 4095) & (-4096));
113	vm_address_t addr = 0;
114	if ( vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
115		exit(1);
116	memcpy((void*)addr, (void*)&foo, size);
117	mprotect((void*)addr, size, PROT_READ|PROT_EXEC);
118	// make pointer to copy of foo and copy of fde
119	checkFuncProc checkFunc = (checkFuncProc)addr;
120	void* foo_eh = (void*)(addr + deltaToFDE);
121
122	// call check() with unwind info unregistered, verify fails
123	(*checkFunc)(false, check);
124
125	// call check() with unwind info registered, verify succeeds
126	__register_frame(foo_eh);
127	(*checkFunc)(true, check);
128
129	// call check() with unwind info unregistered, verify fails
130	__deregister_frame(foo_eh);
131	(*checkFunc)(false, check);
132
133	// call check() with unwind info added via keymgr, verify succeeds
134	my_keymgr_register_frame(foo_eh);
135	(*checkFunc)(true, check);
136
137	return 0;
138}
139
140