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