diff --git a/src/bin/debug/ltrace/ltrace_stub.cpp b/src/bin/debug/ltrace/ltrace_stub.cpp index 28958da4ca..b46b8554ad 100644 --- a/src/bin/debug/ltrace/ltrace_stub.cpp +++ b/src/bin/debug/ltrace/ltrace_stub.cpp @@ -1,5 +1,6 @@ /* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2026, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -13,43 +14,112 @@ #include #include +#include + #include "arch/ltrace_stub.h" -//#define TRACE_PRINTF debug_printf -#define TRACE_PRINTF ktrace_printf +//#define TRACE_STUB +#ifdef TRACE_STUB +# define TRACE(x...) ktrace_printf(x) +#else +# define TRACE(x...) ; +#endif static void* function_call_callback(const void* stub, const void* args); -struct PatchEntry { - PatchEntry* originalTableLink; - PatchEntry* patchedTableLink; +// TODO: Per-image_t patch memory (so it can be freed, +// or marked RX-only after symbol resolution completes?) +static const size_t kPatchMemoryChunkSize = B_PAGE_SIZE * 16; +static const size_t kPatchMemoryReserveSize = 1 * 1024 * 1024; +static uint8* sPatchMemoryChunk = NULL; +static size_t sPatchMemoryChunkRemaining = 0; +static area_id sPatchMemoryChunkArea = 0; +static size_t sPatchMemoryChunkAreaSize = 0; - void* originalFunction; - void* patchedFunction; - const char* functionName; + +static uint8* +patch_malloc(size_t size) +{ + if (sPatchMemoryChunkRemaining < size) { + status_t status = B_NO_MEMORY; + if (sPatchMemoryChunkArea != 0) { + // Try to resize the previous area instead of creating a new one. + status = _kern_resize_area(sPatchMemoryChunkArea, + sPatchMemoryChunkAreaSize + kPatchMemoryChunkSize); + if (status == B_OK) { + sPatchMemoryChunkAreaSize += kPatchMemoryChunkSize; + sPatchMemoryChunkRemaining += kPatchMemoryChunkSize; + } + } + if (status != B_OK) { + void* reservedBase; + status = _kern_reserve_address_range((addr_t*)&reservedBase, + B_RANDOMIZED_ANY_ADDRESS, kPatchMemoryReserveSize); + if (status != B_OK) + return NULL; + + void* base = reservedBase; + area_id area = _kern_create_area("ltrace patches", &base, + B_EXACT_ADDRESS, size, B_NO_LOCK, + B_READ_AREA | B_WRITE_AREA | B_EXECUTE_AREA); + if (area < 0) { + _kern_unreserve_address_range((addr_t)reservedBase, + kPatchMemoryReserveSize); + return NULL; + } + + sPatchMemoryChunk = (uint8*)base; + sPatchMemoryChunkArea = area; + sPatchMemoryChunkRemaining = sPatchMemoryChunkAreaSize = size; + } + } + + uint8* allocation = sPatchMemoryChunk; + sPatchMemoryChunk += size; + sPatchMemoryChunkRemaining -= size; + return allocation; +} + + +struct PatchEntry { + PatchEntry* original_table_link; + + void* original_function; + const char* function_name; + + bool trace; static PatchEntry* Create(const char* name, void* function) { - // TODO memory should be executable, use mmap with PROT_EXEC - void* memory = malloc(_ALIGN(sizeof(PatchEntry)) + void* memory = patch_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)); + void* stub = (uint8*)memory + OffsetToStub(); arch_init_call_stub(stub, &function_call_callback, function); - entry->originalFunction = function; - entry->patchedFunction = stub; - entry->functionName = name; + entry->original_function = function; + entry->function_name = name; + entry->trace = true; return entry; } + + static size_t OffsetToStub() + { + return _ALIGN(sizeof(PatchEntry)); + } + + void* Stub() + { + return (uint8*)this + OffsetToStub(); + } }; @@ -64,43 +134,17 @@ struct OriginalTableDefinition { size_t Hash(PatchEntry* value) const { - return HashKey(value->originalFunction); + return HashKey(value->original_function); } bool Compare(const void* key, PatchEntry* value) const { - return value->originalFunction == key; + return value->original_function == key; } 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; - } - - PatchEntry*& GetLink(PatchEntry* value) const - { - return value->patchedTableLink; + return value->original_table_link; } }; @@ -109,18 +153,15 @@ static rld_export* sRuntimeLoaderInterface; static runtime_loader_add_on_export* sRuntimeLoaderAddOnInterface; static BOpenHashTable sOriginalTable; -static BOpenHashTable sPatchedTable; static void* function_call_callback(const void* stub, const void* _args) { - PatchEntry* entry = sPatchedTable.Lookup(stub); - if (entry == NULL) -{ -TRACE_PRINTF("function_call_callback(): CALLED FOR UNKNOWN FUNCTION!\n"); - return NULL; -} + PatchEntry* entry = (PatchEntry*)((uint8*)stub - PatchEntry::OffsetToStub()); + + if (!entry->trace) + return entry->original_function; char buffer[1024]; size_t bufferSize = sizeof(buffer); @@ -128,15 +169,15 @@ TRACE_PRINTF("function_call_callback(): CALLED FOR UNKNOWN FUNCTION!\n"); const ulong* args = (const ulong*)_args; written += snprintf(buffer, bufferSize, "ltrace: %s(", - entry->functionName); + entry->function_name); for (int32 i = 0; i < 5; i++) { written += snprintf(buffer + written, bufferSize - written, "%s%#lx", i == 0 ? "" : ", ", args[i]); } - written += snprintf(buffer + written, bufferSize - written, ")"); - TRACE_PRINTF("%s\n", buffer); + written += snprintf(buffer + written, bufferSize - written, ")\n"); + write(0, buffer, written); - return entry->originalFunction; + return entry->original_function; } @@ -144,7 +185,7 @@ static void symbol_patcher(void* cookie, image_t* rootImage, image_t* image, const char* name, image_t** foundInImage, void** symbol, int32* type) { - TRACE_PRINTF("symbol_patcher(%p, %p, %p, \"%s\", %p, %p, %" B_PRId32 ")\n", + TRACE("symbol_patcher(%p, %p, %p, \"%s\", %p, %p, %" B_PRId32 ")\n", cookie, rootImage, image, name, *foundInImage, *symbol, *type); // patch functions only @@ -155,7 +196,7 @@ symbol_patcher(void* cookie, image_t* rootImage, image_t* image, PatchEntry* entry = sOriginalTable.Lookup(*symbol); if (entry != NULL) { *foundInImage = NULL; - *symbol = entry->patchedFunction; + *symbol = entry->Stub(); return; } @@ -164,12 +205,11 @@ symbol_patcher(void* cookie, image_t* rootImage, image_t* image, return; sOriginalTable.Insert(entry); - sPatchedTable.Insert(entry); - TRACE_PRINTF(" -> patching to %p\n", entry->patchedFunction); + TRACE(" -> patching to %p\n", entry->Stub()); *foundInImage = NULL; - *symbol = entry->patchedFunction; + *symbol = entry->Stub(); } @@ -177,24 +217,23 @@ static void ltrace_stub_init(rld_export* standardInterface, runtime_loader_add_on_export* addOnInterface) { - TRACE_PRINTF("ltrace_stub_init(%p, %p)\n", standardInterface, addOnInterface); + TRACE("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) { - TRACE_PRINTF("ltrace_stub_image_loaded(%p): \"%s\" (%" B_PRId32 ")\n", + TRACE("ltrace_stub_image_loaded(%p): \"%s\" (%" B_PRId32 ")\n", image, image->path, image->id); if (sRuntimeLoaderAddOnInterface->register_undefined_symbol_patcher(image, symbol_patcher, (void*)(addr_t)0xc0011eaf) != B_OK) { - TRACE_PRINTF(" failed to install symbol patcher\n"); + TRACE(" failed to install symbol patcher\n"); } } @@ -202,7 +241,7 @@ ltrace_stub_image_loaded(image_t* image) static void ltrace_stub_image_relocated(image_t* image) { - TRACE_PRINTF("ltrace_stub_image_relocated(%p): \"%s\" (%" B_PRId32 ")\n", + TRACE("ltrace_stub_image_relocated(%p): \"%s\" (%" B_PRId32 ")\n", image, image->path, image->id); } @@ -210,7 +249,7 @@ ltrace_stub_image_relocated(image_t* image) static void ltrace_stub_image_initialized(image_t* image) { - TRACE_PRINTF("ltrace_stub_image_initialized(%p): \"%s\" (%" B_PRId32 ")\n", + TRACE("ltrace_stub_image_initialized(%p): \"%s\" (%" B_PRId32 ")\n", image, image->path, image->id); } @@ -218,7 +257,7 @@ ltrace_stub_image_initialized(image_t* image) static void ltrace_stub_image_uninitializing(image_t* image) { - TRACE_PRINTF("ltrace_stub_image_uninitializing(%p): \"%s\" (%" B_PRId32 + TRACE("ltrace_stub_image_uninitializing(%p): \"%s\" (%" B_PRId32 ")\n",image, image->path, image->id); } @@ -226,7 +265,7 @@ ltrace_stub_image_uninitializing(image_t* image) static void ltrace_stub_image_unloading(image_t* image) { - TRACE_PRINTF("ltrace_stub_image_unloading(%p): \"%s\" (%" B_PRId32 ")\n", + TRACE("ltrace_stub_image_unloading(%p): \"%s\" (%" B_PRId32 ")\n", image, image->path, image->id); }