1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 9 #include <new> 10 11 #include <OS.h> 12 13 #include <runtime_loader.h> 14 #include <util/OpenHashTable.h> 15 16 #include "arch/ltrace_stub.h" 17 18 19 //#define TRACE_PRINTF debug_printf 20 #define TRACE_PRINTF ktrace_printf 21 22 23 static void* function_call_callback(const void* stub, const void* args); 24 25 26 struct PatchEntry { 27 PatchEntry* originalTableLink; 28 PatchEntry* patchedTableLink; 29 30 void* originalFunction; 31 void* patchedFunction; 32 const char* functionName; 33 34 static PatchEntry* Create(const char* name, void* function) 35 { 36 // TODO memory should be executable, use mmap with PROT_EXEC 37 void* memory = malloc(_ALIGN(sizeof(PatchEntry)) 38 + arch_call_stub_size()); 39 if (memory == NULL) 40 return NULL; 41 42 PatchEntry* entry = new(memory) PatchEntry; 43 44 void* stub = (uint8*)memory + _ALIGN(sizeof(PatchEntry)); 45 arch_init_call_stub(stub, &function_call_callback, function); 46 47 entry->originalFunction = function; 48 entry->patchedFunction = stub; 49 entry->functionName = name; 50 51 return entry; 52 } 53 }; 54 55 56 struct OriginalTableDefinition { 57 typedef const void* KeyType; 58 typedef PatchEntry ValueType; 59 60 size_t HashKey(const void* key) const 61 { 62 return (addr_t)key >> 2; 63 } 64 65 size_t Hash(PatchEntry* value) const 66 { 67 return HashKey(value->originalFunction); 68 } 69 70 bool Compare(const void* key, PatchEntry* value) const 71 { 72 return value->originalFunction == key; 73 } 74 75 PatchEntry*& GetLink(PatchEntry* value) const 76 { 77 return value->originalTableLink; 78 } 79 }; 80 81 82 struct PatchedTableDefinition { 83 typedef const void* KeyType; 84 typedef PatchEntry ValueType; 85 86 size_t HashKey(const void* key) const 87 { 88 return (addr_t)key >> 2; 89 } 90 91 size_t Hash(PatchEntry* value) const 92 { 93 return HashKey(value->patchedFunction); 94 } 95 96 bool Compare(const void* key, PatchEntry* value) const 97 { 98 return value->patchedFunction == key; 99 } 100 101 PatchEntry*& GetLink(PatchEntry* value) const 102 { 103 return value->patchedTableLink; 104 } 105 }; 106 107 108 static rld_export* sRuntimeLoaderInterface; 109 static runtime_loader_add_on_export* sRuntimeLoaderAddOnInterface; 110 111 static BOpenHashTable<OriginalTableDefinition> sOriginalTable; 112 static BOpenHashTable<PatchedTableDefinition> sPatchedTable; 113 114 115 static void* 116 function_call_callback(const void* stub, const void* _args) 117 { 118 PatchEntry* entry = sPatchedTable.Lookup(stub); 119 if (entry == NULL) 120 { 121 TRACE_PRINTF("function_call_callback(): CALLED FOR UNKNOWN FUNCTION!\n"); 122 return NULL; 123 } 124 125 char buffer[1024]; 126 size_t bufferSize = sizeof(buffer); 127 size_t written = 0; 128 129 const ulong* args = (const ulong*)_args; 130 written += snprintf(buffer, bufferSize, "ltrace: %s(", 131 entry->functionName); 132 for (int32 i = 0; i < 5; i++) { 133 written += snprintf(buffer + written, bufferSize - written, "%s%#lx", 134 i == 0 ? "" : ", ", args[i]); 135 } 136 written += snprintf(buffer + written, bufferSize - written, ")"); 137 TRACE_PRINTF("%s\n", buffer); 138 139 return entry->originalFunction; 140 } 141 142 143 static void 144 symbol_patcher(void* cookie, image_t* rootImage, image_t* image, 145 const char* name, image_t** foundInImage, void** symbol, int32* type) 146 { 147 TRACE_PRINTF("symbol_patcher(%p, %p, %p, \"%s\", %p, %p, %" B_PRId32 ")\n", 148 cookie, rootImage, image, name, *foundInImage, *symbol, *type); 149 150 // patch functions only 151 if (*type != B_SYMBOL_TYPE_TEXT) 152 return; 153 154 // already patched? 155 PatchEntry* entry = sOriginalTable.Lookup(*symbol); 156 if (entry != NULL) { 157 *foundInImage = NULL; 158 *symbol = entry->patchedFunction; 159 return; 160 } 161 162 entry = PatchEntry::Create(name, *symbol); 163 if (entry == NULL) 164 return; 165 166 sOriginalTable.Insert(entry); 167 sPatchedTable.Insert(entry); 168 169 TRACE_PRINTF(" -> patching to %p\n", entry->patchedFunction); 170 171 *foundInImage = NULL; 172 *symbol = entry->patchedFunction; 173 } 174 175 176 static void 177 ltrace_stub_init(rld_export* standardInterface, 178 runtime_loader_add_on_export* addOnInterface) 179 { 180 TRACE_PRINTF("ltrace_stub_init(%p, %p)\n", standardInterface, addOnInterface); 181 sRuntimeLoaderInterface = standardInterface; 182 sRuntimeLoaderAddOnInterface = addOnInterface; 183 184 sOriginalTable.Init(); 185 sPatchedTable.Init(); 186 } 187 188 189 static void 190 ltrace_stub_image_loaded(image_t* image) 191 { 192 TRACE_PRINTF("ltrace_stub_image_loaded(%p): \"%s\" (%" B_PRId32 ")\n", 193 image, image->path, image->id); 194 195 if (sRuntimeLoaderAddOnInterface->register_undefined_symbol_patcher(image, 196 symbol_patcher, (void*)(addr_t)0xc0011eaf) != B_OK) { 197 TRACE_PRINTF(" failed to install symbol patcher\n"); 198 } 199 } 200 201 202 static void 203 ltrace_stub_image_relocated(image_t* image) 204 { 205 TRACE_PRINTF("ltrace_stub_image_relocated(%p): \"%s\" (%" B_PRId32 ")\n", 206 image, image->path, image->id); 207 } 208 209 210 static void 211 ltrace_stub_image_initialized(image_t* image) 212 { 213 TRACE_PRINTF("ltrace_stub_image_initialized(%p): \"%s\" (%" B_PRId32 ")\n", 214 image, image->path, image->id); 215 } 216 217 218 static void 219 ltrace_stub_image_uninitializing(image_t* image) 220 { 221 TRACE_PRINTF("ltrace_stub_image_uninitializing(%p): \"%s\" (%" B_PRId32 222 ")\n",image, image->path, image->id); 223 } 224 225 226 static void 227 ltrace_stub_image_unloading(image_t* image) 228 { 229 TRACE_PRINTF("ltrace_stub_image_unloading(%p): \"%s\" (%" B_PRId32 ")\n", 230 image, image->path, image->id); 231 } 232 233 234 // interface for the runtime loader 235 runtime_loader_add_on __gRuntimeLoaderAddOn = { 236 RUNTIME_LOADER_ADD_ON_VERSION, // version 237 0, // flags 238 239 ltrace_stub_init, 240 241 ltrace_stub_image_loaded, 242 ltrace_stub_image_relocated, 243 ltrace_stub_image_initialized, 244 ltrace_stub_image_uninitializing, 245 ltrace_stub_image_unloading 246 }; 247