The beginnings of an ltrace command. Currently merely a proof of concept
for the runtime loader add-on interface. There's a small library (libltrace_stub.so) that when preloaded reroutes all library calls through a stub that prints the function name and some of its arguments to the debug output. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28476 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -7,5 +7,6 @@ UsePrivateSystemHeaders ;
|
|||||||
|
|
||||||
StaticLibrary <bin>debug_utils.a : debug_utils.cpp ;
|
StaticLibrary <bin>debug_utils.a : debug_utils.cpp ;
|
||||||
|
|
||||||
|
HaikuSubInclude ltrace ;
|
||||||
HaikuSubInclude profile ;
|
HaikuSubInclude profile ;
|
||||||
HaikuSubInclude strace ;
|
HaikuSubInclude strace ;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
SubDir HAIKU_TOP src bin debug ltrace ;
|
||||||
|
|
||||||
|
UsePrivateHeaders debug ;
|
||||||
|
UsePrivateHeaders kernel ;
|
||||||
|
UsePrivateHeaders runtime_loader ;
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
UsePrivateSystemHeaders ;
|
||||||
|
|
||||||
|
BinCommand ltrace
|
||||||
|
:
|
||||||
|
ltrace.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
SharedLibrary libltrace_stub.so
|
||||||
|
:
|
||||||
|
ltrace_stub.cpp
|
||||||
|
:
|
||||||
|
arch_ltrace_stub.a
|
||||||
|
;
|
||||||
|
|
||||||
|
HaikuSubInclude arch $(TARGET_ARCH) ;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef ARCH_LTRACE_STUB_H
|
||||||
|
#define ARCH_LTRACE_STUB_H
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
size_t arch_call_stub_size();
|
||||||
|
void arch_init_call_stub(void* stub, void* callback, void* function);
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ARCH_LTRACE_STUB_H
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SubDir HAIKU_TOP src bin debug ltrace arch m68k ;
|
||||||
|
|
||||||
|
# TODO: Implement!
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SubDir HAIKU_TOP src bin debug ltrace arch ppc ;
|
||||||
|
|
||||||
|
# TODO: Implement!
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
SubDir HAIKU_TOP src bin debug ltrace arch x86 ;
|
||||||
|
|
||||||
|
UsePrivateSystemHeaders ;
|
||||||
|
|
||||||
|
StaticLibrary arch_ltrace_stub.a
|
||||||
|
:
|
||||||
|
arch_ltrace_stub.S
|
||||||
|
;
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm_defs.h>
|
||||||
|
|
||||||
|
|
||||||
|
call_stub:
|
||||||
|
// push a pointer to arguments and a pointer to ourselves on the stack
|
||||||
|
lea 4(%esp), %eax
|
||||||
|
push %eax
|
||||||
|
call 1f
|
||||||
|
1:
|
||||||
|
pop %eax
|
||||||
|
subl $(1b - call_stub), %eax
|
||||||
|
push %eax
|
||||||
|
|
||||||
|
// call the wrapper function
|
||||||
|
movl (call_stub_callback_address - call_stub)(%eax), %eax
|
||||||
|
call *%eax
|
||||||
|
// returns a pointer to the actual function
|
||||||
|
lea 8(%esp), %esp
|
||||||
|
|
||||||
|
jmp *%eax
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
call_stub_callback_address:
|
||||||
|
.long 0
|
||||||
|
call_stub_end:
|
||||||
|
|
||||||
|
|
||||||
|
// size_t arch_call_stub_size();
|
||||||
|
FUNCTION(arch_call_stub_size):
|
||||||
|
movl $(call_stub_end - call_stub), %eax
|
||||||
|
ret
|
||||||
|
FUNCTION_END(arch_call_stub_size)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// void arch_init_call_stub(void* stub, void* callback, void* function);
|
||||||
|
FUNCTION(arch_init_call_stub):
|
||||||
|
push %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
|
||||||
|
// stub address to %edi
|
||||||
|
push %edi
|
||||||
|
movl 8(%ebp), %edi
|
||||||
|
|
||||||
|
// copy the stub
|
||||||
|
movl $(call_stub_end - call_stub), %eax
|
||||||
|
push %eax
|
||||||
|
movl $call_stub, %eax
|
||||||
|
push %eax
|
||||||
|
push %edi
|
||||||
|
call memcpy
|
||||||
|
lea 12(%esp), %esp
|
||||||
|
|
||||||
|
// set the callback address in the stub
|
||||||
|
movl 12(%ebp), %eax
|
||||||
|
movl %eax, (call_stub_callback_address - call_stub)(%edi)
|
||||||
|
|
||||||
|
// restore %edi
|
||||||
|
pop %edi
|
||||||
|
|
||||||
|
movl %ebp, %esp
|
||||||
|
pop %ebp
|
||||||
|
ret
|
||||||
|
FUNCTION_END(arch_init_call_stub)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
#include <runtime_loader.h>
|
||||||
|
#include <util/OpenHashTable.h>
|
||||||
|
|
||||||
|
#include "arch/ltrace_stub.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void* function_call_callback(const void* stub, const void* args);
|
||||||
|
|
||||||
|
|
||||||
|
struct PatchEntry {
|
||||||
|
HashTableLink<PatchEntry> originalTableLink;
|
||||||
|
HashTableLink<PatchEntry> patchedTableLink;
|
||||||
|
|
||||||
|
void* originalFunction;
|
||||||
|
void* patchedFunction;
|
||||||
|
const char* functionName;
|
||||||
|
|
||||||
|
static PatchEntry* Create(const char* name, void* function)
|
||||||
|
{
|
||||||
|
void* memory = malloc(_ALIGN(sizeof(PatchEntry))
|
||||||
|
+ arch_call_stub_size());
|
||||||
|
if (memory == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
PatchEntry* entry = new(memory) PatchEntry;
|
||||||
|
|
||||||
|
void* stub = (uint8*)memory + _ALIGN(sizeof(PatchEntry));
|
||||||
|
arch_init_call_stub(stub, &function_call_callback, function);
|
||||||
|
|
||||||
|
entry->originalFunction = function;
|
||||||
|
entry->patchedFunction = stub;
|
||||||
|
entry->functionName = name;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct OriginalTableDefinition {
|
||||||
|
typedef const void* KeyType;
|
||||||
|
typedef PatchEntry ValueType;
|
||||||
|
|
||||||
|
size_t HashKey(const void* key) const
|
||||||
|
{
|
||||||
|
return (addr_t)key >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Hash(PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return HashKey(value->originalFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compare(const void* key, PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return value->originalFunction == key;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableLink<PatchEntry>* GetLink(PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return &value->originalTableLink;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct PatchedTableDefinition {
|
||||||
|
typedef const void* KeyType;
|
||||||
|
typedef PatchEntry ValueType;
|
||||||
|
|
||||||
|
size_t HashKey(const void* key) const
|
||||||
|
{
|
||||||
|
return (addr_t)key >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Hash(PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return HashKey(value->patchedFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compare(const void* key, PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return value->patchedFunction == key;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableLink<PatchEntry>* GetLink(PatchEntry* value) const
|
||||||
|
{
|
||||||
|
return &value->patchedTableLink;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static rld_export* sRuntimeLoaderInterface;
|
||||||
|
static runtime_loader_add_on_export* sRuntimeLoaderAddOnInterface;
|
||||||
|
|
||||||
|
static OpenHashTable<OriginalTableDefinition> sOriginalTable;
|
||||||
|
static OpenHashTable<PatchedTableDefinition> sPatchedTable;
|
||||||
|
|
||||||
|
|
||||||
|
static void*
|
||||||
|
function_call_callback(const void* stub, const void* _args)
|
||||||
|
{
|
||||||
|
PatchEntry* entry = sPatchedTable.Lookup(stub);
|
||||||
|
if (entry == NULL)
|
||||||
|
{
|
||||||
|
debug_printf("function_call_callback(): CALLED FOR UNKNOWN FUNCTION!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32* args = (const uint32*)_args;
|
||||||
|
debug_printf("ltrace: %s(", entry->functionName);
|
||||||
|
for (int32 i = 0; i < 5; i++)
|
||||||
|
debug_printf("%s%#lx", i == 0 ? "" : ", ", args[i]);
|
||||||
|
debug_printf(")\n");
|
||||||
|
|
||||||
|
return entry->originalFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_patcher(void* cookie, image_t* rootImage, image_t* image,
|
||||||
|
const char* name, image_t** foundInImage, void** symbol, int32* type)
|
||||||
|
{
|
||||||
|
debug_printf("symbol_patcher(%p, %p, %p, \"%s\", %p, %p, %ld)\n",
|
||||||
|
cookie, rootImage, image, name, *foundInImage, *symbol, *type);
|
||||||
|
|
||||||
|
// patch functions only
|
||||||
|
if (*type != B_SYMBOL_TYPE_TEXT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// already patched?
|
||||||
|
PatchEntry* entry = sOriginalTable.Lookup(*symbol);
|
||||||
|
if (entry != NULL) {
|
||||||
|
*foundInImage = NULL;
|
||||||
|
*symbol = entry->patchedFunction;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = PatchEntry::Create(name, *symbol);
|
||||||
|
if (entry == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sOriginalTable.Insert(entry);
|
||||||
|
sPatchedTable.Insert(entry);
|
||||||
|
|
||||||
|
debug_printf(" -> patching to %p\n", entry->patchedFunction);
|
||||||
|
|
||||||
|
*foundInImage = NULL;
|
||||||
|
*symbol = entry->patchedFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_init(rld_export* standardInterface,
|
||||||
|
runtime_loader_add_on_export* addOnInterface)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_init(%p, %p)\n", standardInterface, addOnInterface);
|
||||||
|
sRuntimeLoaderInterface = standardInterface;
|
||||||
|
sRuntimeLoaderAddOnInterface = addOnInterface;
|
||||||
|
|
||||||
|
sOriginalTable.Init();
|
||||||
|
sPatchedTable.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_image_loaded(image_t* image)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_image_loaded(%p): \"%s\" (%ld)\n", image, image->path,
|
||||||
|
image->id);
|
||||||
|
|
||||||
|
if (sRuntimeLoaderAddOnInterface->register_undefined_symbol_patcher(image,
|
||||||
|
symbol_patcher, (void*)(addr_t)0xc0011eaf) != B_OK) {
|
||||||
|
debug_printf(" failed to install symbol patcher\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_image_relocated(image_t* image)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_image_relocated(%p): \"%s\" (%ld)\n", image,
|
||||||
|
image->path, image->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_image_initialized(image_t* image)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_image_initialized(%p): \"%s\" (%ld)\n", image,
|
||||||
|
image->path, image->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_image_uninitializing(image_t* image)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_image_uninitializing(%p): \"%s\" (%ld)\n", image,
|
||||||
|
image->path, image->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ltrace_stub_image_unloading(image_t* image)
|
||||||
|
{
|
||||||
|
debug_printf("ltrace_stub_image_unloading(%p): \"%s\" (%ld)\n", image,
|
||||||
|
image->path, image->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// interface for the runtime loader
|
||||||
|
runtime_loader_add_on __gRuntimeLoaderAddOn = {
|
||||||
|
RUNTIME_LOADER_ADD_ON_VERSION, // version
|
||||||
|
0, // flags
|
||||||
|
|
||||||
|
ltrace_stub_init,
|
||||||
|
|
||||||
|
ltrace_stub_image_loaded,
|
||||||
|
ltrace_stub_image_relocated,
|
||||||
|
ltrace_stub_image_initialized,
|
||||||
|
ltrace_stub_image_uninitializing,
|
||||||
|
ltrace_stub_image_unloading
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user