Skip to content

Add support for modules loaded at runtime #247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 29, 2025
Merged
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 |
Expand Down
4 changes: 4 additions & 0 deletions include/cpptrace/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ namespace cpptrace {
CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable<std::size_t> 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
74 changes: 74 additions & 0 deletions src/symbols/symbols_with_dbghelp.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP

#include <cpptrace/basic.hpp>
#include <cpptrace/utils.hpp>
#include "symbols/symbols.hpp"
#include "platform/dbghelp_utils.hpp"
#include "binary/object.hpp"
Expand All @@ -18,6 +19,7 @@
#endif
#include <windows.h>
#include <dbghelp.h>
#include <psapi.h>

namespace cpptrace {
namespace detail {
Expand Down Expand Up @@ -449,4 +451,76 @@ 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

#include <cpptrace/utils.hpp>

namespace cpptrace {
namespace experimental {

void load_symbols_for_file(const std::string& name) {
(void)name;
}
}

}
#endif
Loading