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 ;
|
||||
|
||||
HaikuSubInclude ltrace ;
|
||||
HaikuSubInclude profile ;
|
||||
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