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