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 23static void* function_call_callback(const void* stub, const void* args); 24 25 26struct 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 56struct 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 82struct 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 108static rld_export* sRuntimeLoaderInterface; 109static runtime_loader_add_on_export* sRuntimeLoaderAddOnInterface; 110 111static BOpenHashTable<OriginalTableDefinition> sOriginalTable; 112static BOpenHashTable<PatchedTableDefinition> sPatchedTable; 113 114 115static void* 116function_call_callback(const void* stub, const void* _args) 117{ 118 PatchEntry* entry = sPatchedTable.Lookup(stub); 119 if (entry == NULL) 120{ 121TRACE_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 143static void 144symbol_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 176static void 177ltrace_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 189static void 190ltrace_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 202static void 203ltrace_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 210static void 211ltrace_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 218static void 219ltrace_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 226static void 227ltrace_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 235runtime_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