From c508d70bca0736e7e0a135f2e0008a211b8d89d1 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Thu, 22 May 2025 09:42:56 +0200 Subject: [PATCH 01/11] Add cpptrace::experimental::load_symbols_for_file For now, this function is only implemented for Windows when CPPTRACE_GET_SYMBOLS_WITH_DBGHELP is defined. For other configurations, it does nothing. load_symbols_for_file will load symbols from the given file with SymLoadModuleEx. It should be called when a module was loaded at runtime with LoadLibrary. Otherwise, the symbols for frames inside that loaded module will not be resolvable. --- README.md | 7 +++ include/cpptrace/utils.hpp | 4 ++ src/symbols/symbols_with_dbghelp.cpp | 71 ++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/README.md b/README.md index 72fd664d..c1f0d3ec 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Cpptrace also has a C API, docs [here](docs/c-api.md). - [Headers](#headers) - [Libdwarf Tuning](#libdwarf-tuning) - [JIT Support](#jit-support) + - [Loading Libraries at Runtime](#loading-libraries-at-runtime) - [Supported Debug Formats](#supported-debug-formats) - [How to Include The Library](#how-to-include-the-library) - [CMake FetchContent](#cmake-fetchcontent) @@ -939,6 +940,12 @@ registered with cpptrace. [jitci]: https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html +## Loading Libraries at Runtime + +When loading libraries at runtime, e.g. with `dlopen` or `LoadLibrary`, +`cpptrace::experimental::load_symbols_from_file` should be called with the path to the library. +Otherwise, `cpptrace` will not be able to resolve symbols in the library. + # Supported Debug Formats | Format | Supported | diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 5055d87d..916fb74a 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -49,6 +49,10 @@ namespace cpptrace { CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable); } + + namespace experimental { + CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); + } } #endif diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 22b168e1..c4de996e 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -18,6 +18,7 @@ #endif #include #include +#include namespace cpptrace { namespace detail { @@ -449,4 +450,74 @@ namespace dbghelp { } } +namespace cpptrace { +namespace experimental { +/* +When a module was loaded at runtime with LoadLibrary after SymInitialize was already called, +it is necessary to manually load the symbols from that module with SymLoadModuleEx. + +See "Symbol Handler Initialization" in Microsoft documentation at +https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization +*/ +void load_symbols_for_file(const std::string& name) { + HMODULE module = GetModuleHandleA(name.c_str()); + if (module == NULL) { + std::fprintf( + stderr, + "Error: Unable to load symbols for file %s: %s\n", + name.c_str(), + std::system_error(GetLastError(), std::system_category()).what() + ); + return; + } + + MODULEINFO moduleInfo; + if (0 == GetModuleInformation( + GetCurrentProcess(), + module, + &moduleInfo, + sizeof(moduleInfo) + )) { + std::fprintf( + stderr, + "Error: Unable to get module information for %s: %s\n", + name.c_str(), + std::system_error(GetLastError(), std::system_category()).what() + ); + return; + } + + auto lock = cpptrace::detail::get_dbghelp_lock(); + HANDLE syminit_handle = cpptrace::detail::ensure_syminit().get_process_handle(); + if (0 == SymLoadModuleEx( + syminit_handle, + NULL, + name.c_str(), + NULL, + (DWORD64)moduleInfo.lpBaseOfDll, + moduleInfo.SizeOfImage, + NULL, + 0 + )) { + std::fprintf( + stderr, + "Error: Unable to load symbols for file %s: %s\n", + name.c_str(), + std::system_error(GetLastError(), std::system_category()).what() + ); + } +} +} +} +#else + +namespace cpptrace { +namespace experimental { + +void load_symbols_for_file(const std::string& name) { + (void)name; +} +} + +} #endif From c0070fd4c47cc77ecac9435992fc3652d2defa0a Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Thu, 22 May 2025 10:30:24 +0200 Subject: [PATCH 02/11] Add missing include --- src/symbols/symbols_with_dbghelp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index c4de996e..893863df 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -1,6 +1,7 @@ #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP #include +#include #include "symbols/symbols.hpp" #include "platform/dbghelp_utils.hpp" #include "binary/object.hpp" From f1e9aa8ec3f38cfce689123981012bbb4cebcddf Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Thu, 22 May 2025 10:35:42 +0200 Subject: [PATCH 03/11] Add missing include to the #else case --- src/symbols/symbols_with_dbghelp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 893863df..fc3fc438 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -512,6 +512,8 @@ void load_symbols_for_file(const std::string& name) { } #else +#include + namespace cpptrace { namespace experimental { From 4ebd08ca22c3420ec7edaa269556a43054089fa3 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Fri, 23 May 2025 10:26:30 +0200 Subject: [PATCH 04/11] Get rid of yoda conditions --- src/symbols/symbols_with_dbghelp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index fc3fc438..bafb76dd 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -473,7 +473,7 @@ void load_symbols_for_file(const std::string& name) { } MODULEINFO moduleInfo; - if (0 == GetModuleInformation( + if (!GetModuleInformation( GetCurrentProcess(), module, &moduleInfo, @@ -490,7 +490,7 @@ void load_symbols_for_file(const std::string& name) { auto lock = cpptrace::detail::get_dbghelp_lock(); HANDLE syminit_handle = cpptrace::detail::ensure_syminit().get_process_handle(); - if (0 == SymLoadModuleEx( + if (!SymLoadModuleEx( syminit_handle, NULL, name.c_str(), From 6952660eba9c3ba27696e338b7c18f90e0c121b7 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Fri, 23 May 2025 10:29:22 +0200 Subject: [PATCH 05/11] Throw exceptions instead of printing an error message --- src/symbols/symbols_with_dbghelp.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index bafb76dd..4a59f57e 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -463,13 +463,11 @@ See "Symbol Handler Initialization" in Microsoft documentation at void load_symbols_for_file(const std::string& name) { HMODULE module = GetModuleHandleA(name.c_str()); if (module == NULL) { - std::fprintf( - stderr, - "Error: Unable to load symbols for file %s: %s\n", - name.c_str(), + throw cpptrace::detail::internal_error( + "Unable to get module handle for file '{}' : {}", + name, std::system_error(GetLastError(), std::system_category()).what() ); - return; } MODULEINFO moduleInfo; @@ -479,13 +477,11 @@ void load_symbols_for_file(const std::string& name) { &moduleInfo, sizeof(moduleInfo) )) { - std::fprintf( - stderr, - "Error: Unable to get module information for %s: %s\n", - name.c_str(), + throw cpptrace::detail::internal_error( + "Unable to get module information for file '{}' : {}", + name, std::system_error(GetLastError(), std::system_category()).what() ); - return; } auto lock = cpptrace::detail::get_dbghelp_lock(); @@ -500,10 +496,9 @@ void load_symbols_for_file(const std::string& name) { NULL, 0 )) { - std::fprintf( - stderr, - "Error: Unable to load symbols for file %s: %s\n", - name.c_str(), + throw cpptrace::detail::internal_error( + "Unable to load symbols for file '{}' : {}", + name, std::system_error(GetLastError(), std::system_category()).what() ); } From ccd233ef9121b090053e85a87a92fb9e124cbe35 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Mon, 26 May 2025 13:24:04 +0200 Subject: [PATCH 06/11] Change load_symbols_for_file to load_symbols_for_module The function now takes an HMODULE handle to the DLL instead of the filename. This should make it more explicit that this is a Windows-only function. It also reduces the (rather minor) risk of passing a different filename into the function that doesn't actually refer to a loaded module. It is still necessary to get the filename with GetModuleFileName, but as long as the module handle is valid, this should rarely be an issue. --- README.md | 19 ++++++++-- include/cpptrace/utils.hpp | 12 ++++-- src/symbols/symbols_core.cpp | 19 ++++++++++ src/symbols/symbols_with_dbghelp.cpp | 55 ++++++++++++++++------------ 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c1f0d3ec..6af37385 100644 --- a/README.md +++ b/README.md @@ -942,9 +942,22 @@ registered with cpptrace. ## Loading Libraries at Runtime -When loading libraries at runtime, e.g. with `dlopen` or `LoadLibrary`, -`cpptrace::experimental::load_symbols_from_file` should be called with the path to the library. -Otherwise, `cpptrace` will not be able to resolve symbols in the library. +This section only applies to the dbghelp backend (`CPPTRACE_GET_SYMBOLS_WITH_DBGHELP`) on Windows. + +When loading a DLL at runtime with `LoadLibrary` after a stacktrace has already been generated, +symbols from that library may not be resolved correctly for subsequent stacktraces. To fix this, +call `cpptrace::experimental::load_symbols_for_module` with the module handle, i.e. the return +value of `LoadLibrary`. + +```cpp +HMODULE hModule = LoadLibrary("mydll.dll"); +if (hModule) { + cpptrace::experimental::load_symbols_for_module(hModule); +} +``` + +For backends other than dbghelp, `load_symbols_for_module` does nothing. For platforms other than +Windows, it is not declared. # Supported Debug Formats diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 916fb74a..360dae9a 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -49,10 +49,16 @@ namespace cpptrace { CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable); } +} - namespace experimental { - CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); + +#if defined(_WIN32) + #include + namespace cpptrace { + namespace experimental { + CPPTRACE_EXPORT void load_symbols_for_module(HMODULE hModule); + } } -} +#endif #endif diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp index 46485513..305782f3 100644 --- a/src/symbols/symbols_core.cpp +++ b/src/symbols/symbols_core.cpp @@ -152,3 +152,22 @@ namespace detail { } } } + + +/* +Fallback definition for cpptrace::experimental::load_symbols_for_module. If +CPPTRACE_GET_SYMBOLS_WITH_DBGHELP is defined, this function is defined in symbols_with_dbghelp.cpp. +*/ +#if IS_WINDOWS && !defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) + +#include + +namespace cpptrace { +namespace experimental { + +void load_symbols_for_module(HMODULE hModule) { + (void)hModule; +} +} +} +#endif diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 4a59f57e..8e866638 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -460,62 +460,71 @@ it is necessary to manually load the symbols from that module with SymLoadModule See "Symbol Handler Initialization" in Microsoft documentation at https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization */ -void load_symbols_for_file(const std::string& name) { - HMODULE module = GetModuleHandleA(name.c_str()); - if (module == NULL) { +void load_symbols_for_module(HMODULE hModule) { + + /* + Get filename for module, required by SymLoadModuleEx. Also, it makes for nicer error messages + to include a filename rather than the module handle. + */ + std::string filename; + filename.resize(MAX_PATH); + DWORD bufferSize = filename.size(); + DWORD filenameLength = GetModuleFileNameA(hModule, &filename[0], bufferSize); + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + filename.resize(filenameLength); + bufferSize = filenameLength; + filenameLength = GetModuleFileNameA(hModule, &filename[0], bufferSize); + } + if (GetLastError() != ERROR_SUCCESS) { throw cpptrace::detail::internal_error( - "Unable to get module handle for file '{}' : {}", - name, + "Unable to get module file name for module handle {} : {}", + hModule, std::system_error(GetLastError(), std::system_category()).what() ); } + else { + filename.resize(filenameLength); + } + /* + SymLoadModuleEx needs the module's base address and size, so get these with GetModuleInformation. + */ MODULEINFO moduleInfo; if (!GetModuleInformation( GetCurrentProcess(), - module, + hModule, &moduleInfo, sizeof(moduleInfo) )) { throw cpptrace::detail::internal_error( "Unable to get module information for file '{}' : {}", - name, + filename, std::system_error(GetLastError(), std::system_category()).what() ); } + /* + Finally, load the actual symbols + */ auto lock = cpptrace::detail::get_dbghelp_lock(); HANDLE syminit_handle = cpptrace::detail::ensure_syminit().get_process_handle(); if (!SymLoadModuleEx( syminit_handle, NULL, - name.c_str(), + filename.c_str(), NULL, (DWORD64)moduleInfo.lpBaseOfDll, - moduleInfo.SizeOfImage, + moduleInfo.SizeOfImage, // The documentation says this is optional, but if omitted (0), symbol loading fails NULL, 0 )) { throw cpptrace::detail::internal_error( "Unable to load symbols for file '{}' : {}", - name, + filename, std::system_error(GetLastError(), std::system_category()).what() ); } } } -} -#else - -#include - -namespace cpptrace { -namespace experimental { - -void load_symbols_for_file(const std::string& name) { - (void)name; -} -} - } #endif From 1592aa60d66dfc4afd867a2d87c50c4284a96c27 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Mon, 26 May 2025 13:32:38 +0200 Subject: [PATCH 07/11] Fix formatting call to print HMODULE as a hex number --- src/symbols/symbols_with_dbghelp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 8e866638..d1a8b8d9 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -468,7 +468,7 @@ void load_symbols_for_module(HMODULE hModule) { */ std::string filename; filename.resize(MAX_PATH); - DWORD bufferSize = filename.size(); + DWORD bufferSize = static_cast(filename.size()); DWORD filenameLength = GetModuleFileNameA(hModule, &filename[0], bufferSize); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { filename.resize(filenameLength); @@ -477,8 +477,8 @@ void load_symbols_for_module(HMODULE hModule) { } if (GetLastError() != ERROR_SUCCESS) { throw cpptrace::detail::internal_error( - "Unable to get module file name for module handle {} : {}", - hModule, + "Unable to get module file name for module handle {:h} : {}", + reinterpret_cast(hModule), std::system_error(GetLastError(), std::system_category()).what() ); } From 94ddc47a1f44dccbcb3d7ca56043e2714de31603 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Mon, 26 May 2025 13:33:51 +0200 Subject: [PATCH 08/11] Include Windows.h instead of windef.h Unfortunate, because would have been more lightweight, but apparently expects additional defines to be already set before being included. --- include/cpptrace/utils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 360dae9a..477b9a41 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -53,7 +53,7 @@ namespace cpptrace { #if defined(_WIN32) - #include + #include namespace cpptrace { namespace experimental { CPPTRACE_EXPORT void load_symbols_for_module(HMODULE hModule); From b2a6e5f0ca2f4a0fb2a8cf49399de516e6e3f1e1 Mon Sep 17 00:00:00 2001 From: Benjamin Kloster Date: Mon, 26 May 2025 14:41:48 +0200 Subject: [PATCH 09/11] Revert to load_symbols_for_file to avoid including Windows.h Having the HMODULE data type in the public interface requires including , since there doesn't seem to be a "proper" way of forward declaring HMODULE. However, including comes with a host of problems. For one, its sheer size. For another, it defines some popular names like `min` as macros, which breaks `std::min` and similar. Passing a filename instead of the module handle seems *slightly* less safe due to the risk of passing a string that doesn't refer to a module, but it's also much less hassle. --- README.md | 8 ++++---- include/cpptrace/utils.hpp | 3 +-- src/symbols/symbols_core.cpp | 6 +++--- src/symbols/symbols_with_dbghelp.cpp | 26 ++++++++------------------ 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6af37385..54dd61fe 100644 --- a/README.md +++ b/README.md @@ -946,17 +946,17 @@ This section only applies to the dbghelp backend (`CPPTRACE_GET_SYMBOLS_WITH_DBG When loading a DLL at runtime with `LoadLibrary` after a stacktrace has already been generated, symbols from that library may not be resolved correctly for subsequent stacktraces. To fix this, -call `cpptrace::experimental::load_symbols_for_module` with the module handle, i.e. the return -value of `LoadLibrary`. +call `cpptrace::experimental::load_symbols_for_file` with the same filename that was passed to +`LoadLibrary`. ```cpp HMODULE hModule = LoadLibrary("mydll.dll"); if (hModule) { - cpptrace::experimental::load_symbols_for_module(hModule); + cpptrace::experimental::load_symbols_for_file("mydll.dll"); } ``` -For backends other than dbghelp, `load_symbols_for_module` does nothing. For platforms other than +For backends other than dbghelp, `load_symbols_for_file` does nothing. For platforms other than Windows, it is not declared. # Supported Debug Formats diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 477b9a41..d7ed8a78 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -53,10 +53,9 @@ namespace cpptrace { #if defined(_WIN32) - #include namespace cpptrace { namespace experimental { - CPPTRACE_EXPORT void load_symbols_for_module(HMODULE hModule); + CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); } } #endif diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp index 305782f3..99f6d4ba 100644 --- a/src/symbols/symbols_core.cpp +++ b/src/symbols/symbols_core.cpp @@ -155,7 +155,7 @@ namespace detail { /* -Fallback definition for cpptrace::experimental::load_symbols_for_module. If +Fallback definition for cpptrace::experimental::load_symbols_for_file. If CPPTRACE_GET_SYMBOLS_WITH_DBGHELP is defined, this function is defined in symbols_with_dbghelp.cpp. */ #if IS_WINDOWS && !defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) @@ -165,8 +165,8 @@ CPPTRACE_GET_SYMBOLS_WITH_DBGHELP is defined, this function is defined in symbol namespace cpptrace { namespace experimental { -void load_symbols_for_module(HMODULE hModule) { - (void)hModule; +void load_symbols_for_file(const std::string& filename) { + (void)filename; } } } diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index d1a8b8d9..9e86f210 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -453,6 +453,7 @@ namespace dbghelp { namespace cpptrace { namespace experimental { + /* When a module was loaded at runtime with LoadLibrary after SymInitialize was already called, it is necessary to manually load the symbols from that module with SymLoadModuleEx. @@ -460,31 +461,19 @@ it is necessary to manually load the symbols from that module with SymLoadModule See "Symbol Handler Initialization" in Microsoft documentation at https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization */ -void load_symbols_for_module(HMODULE hModule) { +void load_symbols_for_file(const std::string& filename) { /* - Get filename for module, required by SymLoadModuleEx. Also, it makes for nicer error messages - to include a filename rather than the module handle. + Get module handle for filename so we can call GetModuleInformation below. */ - std::string filename; - filename.resize(MAX_PATH); - DWORD bufferSize = static_cast(filename.size()); - DWORD filenameLength = GetModuleFileNameA(hModule, &filename[0], bufferSize); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - filename.resize(filenameLength); - bufferSize = filenameLength; - filenameLength = GetModuleFileNameA(hModule, &filename[0], bufferSize); - } - if (GetLastError() != ERROR_SUCCESS) { + HMODULE hModule = GetModuleHandleA(filename.c_str()); + if (hModule == NULL) { throw cpptrace::detail::internal_error( - "Unable to get module file name for module handle {:h} : {}", - reinterpret_cast(hModule), + "Unable to get module handle for file '{}' : {}", + filename, std::system_error(GetLastError(), std::system_category()).what() ); } - else { - filename.resize(filenameLength); - } /* SymLoadModuleEx needs the module's base address and size, so get these with GetModuleInformation. @@ -525,6 +514,7 @@ void load_symbols_for_module(HMODULE hModule) { ); } } + } } #endif From 581484e27c9f7dc7e309f840395c56d4c506248f Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Thu, 29 May 2025 12:55:38 -0500 Subject: [PATCH 10/11] Update organization, fix a couple merge issues, and a couple very minor style things --- include/cpptrace/utils.hpp | 10 +-- src/symbols/symbols_core.cpp | 20 +++-- src/symbols/symbols_with_dbghelp.cpp | 111 +++++++++++++-------------- 3 files changed, 63 insertions(+), 78 deletions(-) diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 252d579e..88903ed4 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -55,16 +55,8 @@ CPPTRACE_BEGIN_NAMESPACE namespace experimental { CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable); + CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); } CPPTRACE_END_NAMESPACE - -#if defined(_WIN32) - namespace cpptrace { - namespace experimental { - CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); - } - } -#endif - #endif diff --git a/src/symbols/symbols_core.cpp b/src/symbols/symbols_core.cpp index 61259818..8ff4258e 100644 --- a/src/symbols/symbols_core.cpp +++ b/src/symbols/symbols_core.cpp @@ -1,5 +1,7 @@ #include +#include +#include "cpptrace/forward.hpp" #include "symbols/symbols.hpp" #include @@ -155,19 +157,15 @@ namespace internal { /* -Fallback definition for cpptrace::experimental::load_symbols_for_file. If +Fallback definition for cpptrace::experimental::load_symbols_for_file. If CPPTRACE_GET_SYMBOLS_WITH_DBGHELP is defined, this function is defined in symbols_with_dbghelp.cpp. */ -#if IS_WINDOWS && !defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) - -#include - -namespace cpptrace { +#ifndef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP +CPPTRACE_BEGIN_NAMESPACE namespace experimental { - -void load_symbols_for_file(const std::string& filename) { - (void)filename; -} -} + void load_symbols_for_file(const std::string& filename) { + (void)filename; + } } +CPPTRACE_END_NAMESPACE #endif diff --git a/src/symbols/symbols_with_dbghelp.cpp b/src/symbols/symbols_with_dbghelp.cpp index 6acb1846..bd8763a7 100644 --- a/src/symbols/symbols_with_dbghelp.cpp +++ b/src/symbols/symbols_with_dbghelp.cpp @@ -452,70 +452,65 @@ namespace dbghelp { } } -namespace cpptrace { +CPPTRACE_BEGIN_NAMESPACE namespace experimental { - -/* -When a module was loaded at runtime with LoadLibrary after SymInitialize was already called, -it is necessary to manually load the symbols from that module with SymLoadModuleEx. - -See "Symbol Handler Initialization" in Microsoft documentation at -https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization -*/ -void load_symbols_for_file(const std::string& filename) { - /* - Get module handle for filename so we can call GetModuleInformation below. - */ - HMODULE hModule = GetModuleHandleA(filename.c_str()); - if (hModule == NULL) { - throw cpptrace::detail::internal_error( - "Unable to get module handle for file '{}' : {}", - filename, - std::system_error(GetLastError(), std::system_category()).what() - ); - } + When a module was loaded at runtime with LoadLibrary after SymInitialize was already called, + it is necessary to manually load the symbols from that module with SymLoadModuleEx. - /* - SymLoadModuleEx needs the module's base address and size, so get these with GetModuleInformation. + See "Symbol Handler Initialization" in Microsoft documentation at + https://learn.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization */ - MODULEINFO moduleInfo; - if (!GetModuleInformation( - GetCurrentProcess(), - hModule, - &moduleInfo, - sizeof(moduleInfo) - )) { - throw cpptrace::detail::internal_error( - "Unable to get module information for file '{}' : {}", - filename, - std::system_error(GetLastError(), std::system_category()).what() - ); - } + void load_symbols_for_file(const std::string& filename) { + HMODULE hModule = GetModuleHandleA(filename.c_str()); + if (hModule == NULL) { + throw internal::internal_error( + "Unable to get module handle for file '{}' : {}", + filename, + std::system_error(GetLastError(), std::system_category()).what() + ); + } - /* - Finally, load the actual symbols - */ - auto lock = cpptrace::detail::get_dbghelp_lock(); - HANDLE syminit_handle = cpptrace::detail::ensure_syminit().get_process_handle(); - if (!SymLoadModuleEx( - syminit_handle, - NULL, - filename.c_str(), - NULL, - (DWORD64)moduleInfo.lpBaseOfDll, - moduleInfo.SizeOfImage, // The documentation says this is optional, but if omitted (0), symbol loading fails - NULL, - 0 - )) { - throw cpptrace::detail::internal_error( - "Unable to load symbols for file '{}' : {}", - filename, - std::system_error(GetLastError(), std::system_category()).what() - ); + // SymLoadModuleEx needs the module's base address and size, so get these with GetModuleInformation. + MODULEINFO module_info; + if ( + !GetModuleInformation( + GetCurrentProcess(), + hModule, + &module_info, + sizeof(module_info) + ) + ) { + throw internal::internal_error( + "Unable to get module information for file '{}' : {}", + filename, + std::system_error(GetLastError(), std::system_category()).what() + ); + } + + auto lock = internal::get_dbghelp_lock(); + HANDLE syminit_handle = internal::ensure_syminit().get_process_handle(); + if ( + !SymLoadModuleEx( + syminit_handle, + NULL, + filename.c_str(), + NULL, + (DWORD64)module_info.lpBaseOfDll, + // The documentation says this is optional, but if omitted (0), symbol loading fails + module_info.SizeOfImage, + NULL, + 0 + ) + ) { + throw internal::internal_error( + "Unable to load symbols for file '{}' : {}", + filename, + std::system_error(GetLastError(), std::system_category()).what() + ); + } } } +CPPTRACE_END_NAMESPACE -} -} #endif From 0486107e7cf9ff7bf175bbf2940d553449146967 Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Thu, 29 May 2025 13:10:07 -0500 Subject: [PATCH 11/11] Minor things --- include/cpptrace/utils.hpp | 4 ++++ src/cpptrace.cppm | 1 + 2 files changed, 5 insertions(+) diff --git a/include/cpptrace/utils.hpp b/include/cpptrace/utils.hpp index 88903ed4..0c4a29da 100644 --- a/include/cpptrace/utils.hpp +++ b/include/cpptrace/utils.hpp @@ -55,6 +55,10 @@ CPPTRACE_BEGIN_NAMESPACE namespace experimental { CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable max_entries); CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable); + } + + // dbghelp + namespace experimental { CPPTRACE_EXPORT void load_symbols_for_file(const std::string& filename); } CPPTRACE_END_NAMESPACE diff --git a/src/cpptrace.cppm b/src/cpptrace.cppm index 642aa4a5..6998c963 100644 --- a/src/cpptrace.cppm +++ b/src/cpptrace.cppm @@ -98,5 +98,6 @@ CPPTRACE_BEGIN_NAMESPACE export using cpptrace::experimental::set_cache_mode; export using cpptrace::experimental::set_dwarf_resolver_line_table_cache_size; export using cpptrace::experimental::set_dwarf_resolver_disable_aranges; + export using cpptrace::experimental::load_symbols_for_file; } CPPTRACE_END_NAMESPACE