From 2600324b57fa31cdea1627d584d314f2a579c4a8 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 3 Nov 2008 13:36:57 +0000 Subject: [PATCH] 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 --- src/bin/debug/Jamfile | 1 + src/bin/debug/ltrace/Jamfile | 21 ++ src/bin/debug/ltrace/arch/ltrace_stub.h | 17 ++ src/bin/debug/ltrace/arch/m68k/Jamfile | 3 + src/bin/debug/ltrace/arch/ppc/Jamfile | 3 + src/bin/debug/ltrace/arch/x86/Jamfile | 8 + .../debug/ltrace/arch/x86/arch_ltrace_stub.S | 69 ++++++ src/bin/debug/ltrace/ltrace.cpp | 10 + src/bin/debug/ltrace/ltrace_stub.cpp | 233 ++++++++++++++++++ 9 files changed, 365 insertions(+) create mode 100644 src/bin/debug/ltrace/Jamfile create mode 100644 src/bin/debug/ltrace/arch/ltrace_stub.h create mode 100644 src/bin/debug/ltrace/arch/m68k/Jamfile create mode 100644 src/bin/debug/ltrace/arch/ppc/Jamfile create mode 100644 src/bin/debug/ltrace/arch/x86/Jamfile create mode 100644 src/bin/debug/ltrace/arch/x86/arch_ltrace_stub.S create mode 100644 src/bin/debug/ltrace/ltrace.cpp create mode 100644 src/bin/debug/ltrace/ltrace_stub.cpp diff --git a/src/bin/debug/Jamfile b/src/bin/debug/Jamfile index 15bb45a80f..0759ab5e50 100644 --- a/src/bin/debug/Jamfile +++ b/src/bin/debug/Jamfile @@ -7,5 +7,6 @@ UsePrivateSystemHeaders ; StaticLibrary debug_utils.a : debug_utils.cpp ; +HaikuSubInclude ltrace ; HaikuSubInclude profile ; HaikuSubInclude strace ; diff --git a/src/bin/debug/ltrace/Jamfile b/src/bin/debug/ltrace/Jamfile new file mode 100644 index 0000000000..a7e78b6e6e --- /dev/null +++ b/src/bin/debug/ltrace/Jamfile @@ -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) ; diff --git a/src/bin/debug/ltrace/arch/ltrace_stub.h b/src/bin/debug/ltrace/arch/ltrace_stub.h new file mode 100644 index 0000000000..5dc83da931 --- /dev/null +++ b/src/bin/debug/ltrace/arch/ltrace_stub.h @@ -0,0 +1,17 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * 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 diff --git a/src/bin/debug/ltrace/arch/m68k/Jamfile b/src/bin/debug/ltrace/arch/m68k/Jamfile new file mode 100644 index 0000000000..b090c3c8c4 --- /dev/null +++ b/src/bin/debug/ltrace/arch/m68k/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src bin debug ltrace arch m68k ; + +# TODO: Implement! diff --git a/src/bin/debug/ltrace/arch/ppc/Jamfile b/src/bin/debug/ltrace/arch/ppc/Jamfile new file mode 100644 index 0000000000..6d165e63f1 --- /dev/null +++ b/src/bin/debug/ltrace/arch/ppc/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src bin debug ltrace arch ppc ; + +# TODO: Implement! diff --git a/src/bin/debug/ltrace/arch/x86/Jamfile b/src/bin/debug/ltrace/arch/x86/Jamfile new file mode 100644 index 0000000000..fb7a4faef8 --- /dev/null +++ b/src/bin/debug/ltrace/arch/x86/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src bin debug ltrace arch x86 ; + +UsePrivateSystemHeaders ; + +StaticLibrary arch_ltrace_stub.a + : + arch_ltrace_stub.S +; diff --git a/src/bin/debug/ltrace/arch/x86/arch_ltrace_stub.S b/src/bin/debug/ltrace/arch/x86/arch_ltrace_stub.S new file mode 100644 index 0000000000..78ed641675 --- /dev/null +++ b/src/bin/debug/ltrace/arch/x86/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 + + +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) diff --git a/src/bin/debug/ltrace/ltrace.cpp b/src/bin/debug/ltrace/ltrace.cpp new file mode 100644 index 0000000000..c782b62adf --- /dev/null +++ b/src/bin/debug/ltrace/ltrace.cpp @@ -0,0 +1,10 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +int +main() +{ + return 0; +} diff --git a/src/bin/debug/ltrace/ltrace_stub.cpp b/src/bin/debug/ltrace/ltrace_stub.cpp new file mode 100644 index 0000000000..0cc41248d7 --- /dev/null +++ b/src/bin/debug/ltrace/ltrace_stub.cpp @@ -0,0 +1,233 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include + +#include + +#include +#include + +#include "arch/ltrace_stub.h" + + +static void* function_call_callback(const void* stub, const void* args); + + +struct PatchEntry { + HashTableLink originalTableLink; + HashTableLink 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* 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* GetLink(PatchEntry* value) const + { + return &value->patchedTableLink; + } +}; + + +static rld_export* sRuntimeLoaderInterface; +static runtime_loader_add_on_export* sRuntimeLoaderAddOnInterface; + +static OpenHashTable sOriginalTable; +static OpenHashTable 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 +};