xref: /haiku/src/bin/debug/ltrace/ltrace_stub.cpp (revision 29ec639a6da8cc3c6852d421116cddc291ec18d8)
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