From 95312e7c68370fb280821136659834c858cff64e Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Sun, 15 Sep 2024 13:55:28 +0200 Subject: [PATCH 1/7] Substract base address from a frame to make it decodable with the original binary --- include/boost/stacktrace/detail/frame_unwind.ipp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/boost/stacktrace/detail/frame_unwind.ipp b/include/boost/stacktrace/detail/frame_unwind.ipp index a985515..dd0aaee 100644 --- a/include/boost/stacktrace/detail/frame_unwind.ipp +++ b/include/boost/stacktrace/detail/frame_unwind.ipp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,8 @@ public: if (!Base::res.empty()) { Base::res = boost::core::demangle(Base::res.c_str()); } else { - Base::res = to_hex_array(addr).data(); + auto addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); + Base::res = to_hex_array(reinterpret_cast(addr) - addr_base).data(); } if (Base::prepare_source_location(addr)) { From a15867f20611430a5068f5a17e3645377c1ae425 Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Sun, 29 Sep 2024 14:35:46 +0200 Subject: [PATCH 2/7] MSVC early implementation --- .../boost/stacktrace/detail/frame_msvc.ipp | 54 +++++++++++++++++++ .../boost/stacktrace/detail/frame_unwind.ipp | 9 +++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/boost/stacktrace/detail/frame_msvc.ipp b/include/boost/stacktrace/detail/frame_msvc.ipp index d774e7e..0e80306 100644 --- a/include/boost/stacktrace/detail/frame_msvc.ipp +++ b/include/boost/stacktrace/detail/frame_msvc.ipp @@ -19,13 +19,22 @@ #include #include +//Testing +#define BOOST_STACKTRACE_OFFSET_ADDR_BASE + #ifdef WIN32_LEAN_AND_MEAN #include +# ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#include +# endif #else // Prevent inclusion of extra Windows SDK headers which can cause conflict // with other code using Windows SDK #define WIN32_LEAN_AND_MEAN #include +# ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#include +# endif #undef WIN32_LEAN_AND_MEAN #endif @@ -381,6 +390,36 @@ public: return result; } +#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE + const uintptr_t get_module_base_impl(const void* addr) const { + HANDLE processHandle = GetCurrentProcess(); + HMODULE modules[1024]; + + DWORD needed; + + if (EnumProcessModulesEx(processHandle, modules, sizeof(modules), &needed, LIST_MODULES_ALL)) { + int moduleCount = needed / sizeof(HMODULE); + + for (int i = 0; i < moduleCount; ++i) { + MODULEINFO moduleInfo; + TCHAR moduleName[MAX_PATH]; + + // Get the module name + if (GetModuleBaseName(processHandle, modules[i], moduleName, sizeof(moduleName) / sizeof(TCHAR))) { + // Get module information + if (GetModuleInformation(processHandle, modules[i], &moduleInfo, sizeof(moduleInfo))) { + if (moduleInfo.lpBaseOfDll <= addr && addr < LPBYTE(moduleInfo.lpBaseOfDll) + moduleInfo.SizeOfImage) { + // Module contains the address + return reinterpret_cast(moduleInfo.lpBaseOfDll); + } + } + } + } + } + + return 0; + } +#endif void to_string_impl(const void* addr, std::string& res) const { if (!is_inited()) { return; @@ -390,8 +429,23 @@ public: std::string name = this->get_name_impl(addr, &module_name); if (!name.empty()) { res += name; + // const uintptr_t base_addr = get_module_base_impl(addr); + // res += '('; + // res += to_hex_array(reinterpret_cast(addr) - base_addr).data(); + // res += '-'; + // res += to_hex_array(base_addr).data(); + // res += ')'; } else { +#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE + // Get own base address + const uintptr_t base_addr = get_module_base_impl(addr); + res += to_hex_array(reinterpret_cast(addr) - base_addr).data(); + // res += '+'; + // res += to_hex_array(base_addr).data(); + // res += ')'; +#else res += to_hex_array(addr).data(); +#endif } std::pair source_line = this->get_source_file_line_impl(addr); diff --git a/include/boost/stacktrace/detail/frame_unwind.ipp b/include/boost/stacktrace/detail/frame_unwind.ipp index dd0aaee..864093c 100644 --- a/include/boost/stacktrace/detail/frame_unwind.ipp +++ b/include/boost/stacktrace/detail/frame_unwind.ipp @@ -30,6 +30,9 @@ # include #endif +//Testing +#define BOOST_STACKTRACE_OFFSET_ADDR_BASE + namespace boost { namespace stacktrace { namespace detail { template @@ -41,8 +44,12 @@ public: if (!Base::res.empty()) { Base::res = boost::core::demangle(Base::res.c_str()); } else { - auto addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); +#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE + const auto addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); Base::res = to_hex_array(reinterpret_cast(addr) - addr_base).data(); +#else + Base::res = to_hex_array(addr).data(); +#endif } if (Base::prepare_source_location(addr)) { From 75aefd385925a34ce6afcb56e6b14236e71f23eb Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Sat, 12 Oct 2024 18:28:04 +0200 Subject: [PATCH 3/7] Extract get_own_proc_addr_base to own header --- .../stacktrace/detail/addr_base_msvc.hpp | 59 +++++++++++++++++++ .../boost/stacktrace/detail/frame_msvc.ipp | 45 ++------------ 2 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 include/boost/stacktrace/detail/addr_base_msvc.hpp diff --git a/include/boost/stacktrace/detail/addr_base_msvc.hpp b/include/boost/stacktrace/detail/addr_base_msvc.hpp new file mode 100644 index 0000000..ea447e5 --- /dev/null +++ b/include/boost/stacktrace/detail/addr_base_msvc.hpp @@ -0,0 +1,59 @@ +// Copyright Antony Polukhin, 2016-2024. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_STACKTRACE_DETAIL_ADDR_BASE_MSVC_HPP +#define BOOST_STACKTRACE_DETAIL_ADDR_BASE_MSVC_HPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#ifdef WIN32_LEAN_AND_MEAN +#include +#include +#else +// Prevent inclusion of extra Windows SDK headers which can cause conflict +// with other code using Windows SDK +#define WIN32_LEAN_AND_MEAN +#include +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +namespace boost { namespace stacktrace { namespace detail { + inline uintptr_t get_own_proc_addr_base(const void* addr) { + HANDLE processHandle = GetCurrentProcess(); + HMODULE modules[1024]; + + DWORD needed; + + if (EnumProcessModulesEx(processHandle, modules, sizeof(modules), &needed, LIST_MODULES_ALL)) { + int moduleCount = needed / sizeof(HMODULE); + + for (int i = 0; i < moduleCount; ++i) { + MODULEINFO moduleInfo; + TCHAR moduleName[MAX_PATH]; + + // Get the module name + if (GetModuleBaseName(processHandle, modules[i], moduleName, sizeof(moduleName) / sizeof(TCHAR))) { + // Get module information + if (GetModuleInformation(processHandle, modules[i], &moduleInfo, sizeof(moduleInfo))) { + if (moduleInfo.lpBaseOfDll <= addr && addr < LPBYTE(moduleInfo.lpBaseOfDll) + moduleInfo.SizeOfImage) { + // Module contains the address + return reinterpret_cast(moduleInfo.lpBaseOfDll); + } + } + } + } + } + + return 0; + } + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_ADDR_BASE_MSVC_HPP diff --git a/include/boost/stacktrace/detail/frame_msvc.ipp b/include/boost/stacktrace/detail/frame_msvc.ipp index 0e80306..d15f106 100644 --- a/include/boost/stacktrace/detail/frame_msvc.ipp +++ b/include/boost/stacktrace/detail/frame_msvc.ipp @@ -22,6 +22,10 @@ //Testing #define BOOST_STACKTRACE_OFFSET_ADDR_BASE +#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#include +#endif + #ifdef WIN32_LEAN_AND_MEAN #include # ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE @@ -390,36 +394,6 @@ public: return result; } -#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE - const uintptr_t get_module_base_impl(const void* addr) const { - HANDLE processHandle = GetCurrentProcess(); - HMODULE modules[1024]; - - DWORD needed; - - if (EnumProcessModulesEx(processHandle, modules, sizeof(modules), &needed, LIST_MODULES_ALL)) { - int moduleCount = needed / sizeof(HMODULE); - - for (int i = 0; i < moduleCount; ++i) { - MODULEINFO moduleInfo; - TCHAR moduleName[MAX_PATH]; - - // Get the module name - if (GetModuleBaseName(processHandle, modules[i], moduleName, sizeof(moduleName) / sizeof(TCHAR))) { - // Get module information - if (GetModuleInformation(processHandle, modules[i], &moduleInfo, sizeof(moduleInfo))) { - if (moduleInfo.lpBaseOfDll <= addr && addr < LPBYTE(moduleInfo.lpBaseOfDll) + moduleInfo.SizeOfImage) { - // Module contains the address - return reinterpret_cast(moduleInfo.lpBaseOfDll); - } - } - } - } - } - - return 0; - } -#endif void to_string_impl(const void* addr, std::string& res) const { if (!is_inited()) { return; @@ -429,20 +403,11 @@ public: std::string name = this->get_name_impl(addr, &module_name); if (!name.empty()) { res += name; - // const uintptr_t base_addr = get_module_base_impl(addr); - // res += '('; - // res += to_hex_array(reinterpret_cast(addr) - base_addr).data(); - // res += '-'; - // res += to_hex_array(base_addr).data(); - // res += ')'; } else { #ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE // Get own base address - const uintptr_t base_addr = get_module_base_impl(addr); + const uintptr_t base_addr = get_own_proc_addr_base(addr); res += to_hex_array(reinterpret_cast(addr) - base_addr).data(); - // res += '+'; - // res += to_hex_array(base_addr).data(); - // res += ')'; #else res += to_hex_array(addr).data(); #endif From 9df0f24db6d4f6bcfd10c6f367928ec34ded2600 Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Wed, 25 Dec 2024 14:35:25 +0100 Subject: [PATCH 4/7] Make base addr offset default and redo MSVC implementation. --- .../stacktrace/detail/addr_base_msvc.hpp | 53 ++++++++++++------- .../boost/stacktrace/detail/frame_msvc.ipp | 17 ++---- .../boost/stacktrace/detail/frame_unwind.ipp | 9 ++-- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/include/boost/stacktrace/detail/addr_base_msvc.hpp b/include/boost/stacktrace/detail/addr_base_msvc.hpp index ea447e5..6d2bbfb 100644 --- a/include/boost/stacktrace/detail/addr_base_msvc.hpp +++ b/include/boost/stacktrace/detail/addr_base_msvc.hpp @@ -1,5 +1,3 @@ -// Copyright Antony Polukhin, 2016-2024. -// // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -26,32 +24,47 @@ namespace boost { namespace stacktrace { namespace detail { inline uintptr_t get_own_proc_addr_base(const void* addr) { - HANDLE processHandle = GetCurrentProcess(); - HMODULE modules[1024]; - - DWORD needed; + // Try to avoid allocating memory for the modules array if possible. + // The stack buffer should be large enough for most processes. + HMODULE modules_stack[1024]; + HMODULE* modules_allocated = nullptr; + HMODULE* modules = modules_stack; - if (EnumProcessModulesEx(processHandle, modules, sizeof(modules), &needed, LIST_MODULES_ALL)) { - int moduleCount = needed / sizeof(HMODULE); + DWORD needed_bytes = 0; + uintptr_t addr_base = 0; - for (int i = 0; i < moduleCount; ++i) { - MODULEINFO moduleInfo; - TCHAR moduleName[MAX_PATH]; + HANDLE process_handle = GetCurrentProcess(); + bool enum_process_result = EnumProcessModules(process_handle, modules, sizeof(modules), &needed_bytes); + + // Check if the error is because the buffer is too small. + if (!enum_process_result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + modules_allocated = new HMODULE[needed_bytes / sizeof(HMODULE)]; + enum_process_result = EnumProcessModules(process_handle, modules, sizeof(modules), &needed_bytes); + modules = modules_allocated; + } + + if (enum_process_result) { + for (int i = 0; i < (needed_bytes / sizeof(HMODULE)); ++i) { + MODULEINFO module_info; + TCHAR module_name[MAX_PATH]; // Get the module name - if (GetModuleBaseName(processHandle, modules[i], moduleName, sizeof(moduleName) / sizeof(TCHAR))) { - // Get module information - if (GetModuleInformation(processHandle, modules[i], &moduleInfo, sizeof(moduleInfo))) { - if (moduleInfo.lpBaseOfDll <= addr && addr < LPBYTE(moduleInfo.lpBaseOfDll) + moduleInfo.SizeOfImage) { - // Module contains the address - return reinterpret_cast(moduleInfo.lpBaseOfDll); - } - } + if (GetModuleBaseName(process_handle, modules[i], module_name, sizeof(module_name) / sizeof(TCHAR)) + && GetModuleInformation(process_handle, modules[i], &module_info, sizeof(module_info)) + && module_info.lpBaseOfDll <= addr && addr < LPBYTE(module_info.lpBaseOfDll) + module_info.SizeOfImage) { + // Module contains the address + addr_base = reinterpret_cast(module_info.lpBaseOfDll); + break; } } } - return 0; + CloseHandle(process_handle); + if (modules_allocated) { + delete[] modules_allocated; + } + + return addr_base; } }}} // namespace boost::stacktrace::detail diff --git a/include/boost/stacktrace/detail/frame_msvc.ipp b/include/boost/stacktrace/detail/frame_msvc.ipp index d15f106..17bf2e2 100644 --- a/include/boost/stacktrace/detail/frame_msvc.ipp +++ b/include/boost/stacktrace/detail/frame_msvc.ipp @@ -19,26 +19,17 @@ #include #include -//Testing -#define BOOST_STACKTRACE_OFFSET_ADDR_BASE - -#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#ifndef BOOST_STACKTRACE_DISABLE_OFFSET_ADDR_BASE #include #endif #ifdef WIN32_LEAN_AND_MEAN #include -# ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE -#include -# endif #else // Prevent inclusion of extra Windows SDK headers which can cause conflict // with other code using Windows SDK #define WIN32_LEAN_AND_MEAN #include -# ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE -#include -# endif #undef WIN32_LEAN_AND_MEAN #endif @@ -404,12 +395,12 @@ public: if (!name.empty()) { res += name; } else { -#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#ifdef BOOST_STACKTRACE_DISABLE_OFFSET_ADDR_BASE + res += to_hex_array(addr).data(); +#else // Get own base address const uintptr_t base_addr = get_own_proc_addr_base(addr); res += to_hex_array(reinterpret_cast(addr) - base_addr).data(); -#else - res += to_hex_array(addr).data(); #endif } diff --git a/include/boost/stacktrace/detail/frame_unwind.ipp b/include/boost/stacktrace/detail/frame_unwind.ipp index 864093c..645564a 100644 --- a/include/boost/stacktrace/detail/frame_unwind.ipp +++ b/include/boost/stacktrace/detail/frame_unwind.ipp @@ -30,9 +30,6 @@ # include #endif -//Testing -#define BOOST_STACKTRACE_OFFSET_ADDR_BASE - namespace boost { namespace stacktrace { namespace detail { template @@ -44,11 +41,11 @@ public: if (!Base::res.empty()) { Base::res = boost::core::demangle(Base::res.c_str()); } else { -#ifdef BOOST_STACKTRACE_OFFSET_ADDR_BASE +#ifdef BOOST_STACKTRACE_DISABLE_OFFSET_ADDR_BASE + Base::res = to_hex_array(addr).data(); +#else const auto addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); Base::res = to_hex_array(reinterpret_cast(addr) - addr_base).data(); -#else - Base::res = to_hex_array(addr).data(); #endif } From 412167dff3edc6b6a0297f07198a72434f3473ac Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Wed, 25 Dec 2024 14:50:35 +0100 Subject: [PATCH 5/7] fixup! Make base addr offset default and redo MSVC implementation. --- include/boost/stacktrace/detail/addr_base_msvc.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/stacktrace/detail/addr_base_msvc.hpp b/include/boost/stacktrace/detail/addr_base_msvc.hpp index 6d2bbfb..7175e06 100644 --- a/include/boost/stacktrace/detail/addr_base_msvc.hpp +++ b/include/boost/stacktrace/detail/addr_base_msvc.hpp @@ -1,3 +1,5 @@ +// Copyright Antony Polukhin, 2016-2024. +// // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) From 9c2bfa24c40d5fab1250553fe3bb7ab8bd15ce76 Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Wed, 1 Jan 2025 19:15:05 +0100 Subject: [PATCH 6/7] Review fixes --- .../stacktrace/detail/addr_base_msvc.hpp | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/include/boost/stacktrace/detail/addr_base_msvc.hpp b/include/boost/stacktrace/detail/addr_base_msvc.hpp index 7175e06..55c1cb1 100644 --- a/include/boost/stacktrace/detail/addr_base_msvc.hpp +++ b/include/boost/stacktrace/detail/addr_base_msvc.hpp @@ -12,6 +12,9 @@ # pragma once #endif +#include +#include + #ifdef WIN32_LEAN_AND_MEAN #include #include @@ -25,11 +28,11 @@ #endif namespace boost { namespace stacktrace { namespace detail { - inline uintptr_t get_own_proc_addr_base(const void* addr) { + inline std::uintptr_t get_own_proc_addr_base(const void* addr) { // Try to avoid allocating memory for the modules array if possible. // The stack buffer should be large enough for most processes. HMODULE modules_stack[1024]; - HMODULE* modules_allocated = nullptr; + std::unique_ptr modules_allocated; HMODULE* modules = modules_stack; DWORD needed_bytes = 0; @@ -40,31 +43,26 @@ namespace boost { namespace stacktrace { namespace detail { // Check if the error is because the buffer is too small. if (!enum_process_result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - modules_allocated = new HMODULE[needed_bytes / sizeof(HMODULE)]; - enum_process_result = EnumProcessModules(process_handle, modules, sizeof(modules), &needed_bytes); - modules = modules_allocated; + modules_allocated.reset(new HMODULE[needed_bytes / sizeof(HMODULE)]); + modules = modules_allocated.get(); + enum_process_result = EnumProcessModules(process_handle, modules, needed_bytes, &needed_bytes); } if (enum_process_result) { - for (int i = 0; i < (needed_bytes / sizeof(HMODULE)); ++i) { + for (std::size_t i = 0; i < (needed_bytes / sizeof(HMODULE)); ++i) { MODULEINFO module_info; - TCHAR module_name[MAX_PATH]; // Get the module name - if (GetModuleBaseName(process_handle, modules[i], module_name, sizeof(module_name) / sizeof(TCHAR)) - && GetModuleInformation(process_handle, modules[i], &module_info, sizeof(module_info)) + if (GetModuleInformation(process_handle, modules[i], &module_info, sizeof(module_info)) && module_info.lpBaseOfDll <= addr && addr < LPBYTE(module_info.lpBaseOfDll) + module_info.SizeOfImage) { // Module contains the address - addr_base = reinterpret_cast(module_info.lpBaseOfDll); + addr_base = reinterpret_cast(module_info.lpBaseOfDll); break; } } } CloseHandle(process_handle); - if (modules_allocated) { - delete[] modules_allocated; - } return addr_base; } From bf9b566d2d68f1aae7b69f0a081e461b0abdfcac Mon Sep 17 00:00:00 2001 From: Maciej Czarnecki Date: Sun, 5 Jan 2025 20:19:00 +0100 Subject: [PATCH 7/7] Add smoke test for relative virtual address --- test/test.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/test/test.cpp b/test/test.cpp index e9cb5bd..003f774 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -263,7 +264,51 @@ void test_stacktrace_limits() BOOST_TEST_EQ(boost::stacktrace::stacktrace(1, 1).size(), 1); } -int main() { +std::size_t get_file_size(const char* file_name) { + std::ifstream file(file_name, std::ios::binary | std::ios::ate); + const auto file_size = file.tellg(); + BOOST_TEST(file_size > 0); + return static_cast(file_size); +} + +uintptr_t get_address_from_frame(const std::string& frame) { + std::size_t address = 0; + std::string hex_address; + std::size_t pos = frame.find("0x"); + + if (pos != std::string::npos) { + // Extract the hex address substring + hex_address = frame.substr(pos + 2); // Skip "0x" + + // Convert hex string to std::size_t + std::stringstream ss; + ss << std::hex << hex_address; + ss >> address; + } + + return address; +} + +void test_relative_virtual_address(const char* file_path) +{ + const auto frame = to_string(boost::stacktrace::stacktrace(0, 1).as_vector().front()); + + // Skip the test if the frame does not contain an address + if (frame.find("0x") == std::string::npos) { + return; + } + + const auto file_size = get_file_size(file_path); + BOOST_TEST(file_size > 0); + + const auto address = get_address_from_frame(frame); + BOOST_TEST(address > 0); + + // Verify that the address is within the binary + BOOST_TEST(address <= file_size); +} + +int main(const int, const char* argv[]) { test_deeply_nested_namespaces(); test_frames_string_data_validity(); test_nested<15>(); @@ -283,6 +328,7 @@ int main() { test_nested<260>(false); test_stacktrace_limits(); + test_relative_virtual_address(argv[0]); return boost::report_errors(); }