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
CreatePatchEntry34 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
HashKeyOriginalTableDefinition60 size_t HashKey(const void* key) const
61 {
62 return (addr_t)key >> 2;
63 }
64
HashOriginalTableDefinition65 size_t Hash(PatchEntry* value) const
66 {
67 return HashKey(value->originalFunction);
68 }
69
CompareOriginalTableDefinition70 bool Compare(const void* key, PatchEntry* value) const
71 {
72 return value->originalFunction == key;
73 }
74
GetLinkOriginalTableDefinition75 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
HashKeyPatchedTableDefinition86 size_t HashKey(const void* key) const
87 {
88 return (addr_t)key >> 2;
89 }
90
HashPatchedTableDefinition91 size_t Hash(PatchEntry* value) const
92 {
93 return HashKey(value->patchedFunction);
94 }
95
ComparePatchedTableDefinition96 bool Compare(const void* key, PatchEntry* value) const
97 {
98 return value->patchedFunction == key;
99 }
100
GetLinkPatchedTableDefinition101 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*
function_call_callback(const void * stub,const void * _args)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
symbol_patcher(void * cookie,image_t * rootImage,image_t * image,const char * name,image_t ** foundInImage,void ** symbol,int32 * type)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
ltrace_stub_init(rld_export * standardInterface,runtime_loader_add_on_export * addOnInterface)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
ltrace_stub_image_loaded(image_t * image)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
ltrace_stub_image_relocated(image_t * image)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
ltrace_stub_image_initialized(image_t * image)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
ltrace_stub_image_uninitializing(image_t * image)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
ltrace_stub_image_unloading(image_t * image)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