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..55c1cb1 --- /dev/null +++ b/include/boost/stacktrace/detail/addr_base_msvc.hpp @@ -0,0 +1,72 @@ +// 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 + +#include +#include + +#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 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]; + std::unique_ptr modules_allocated; + HMODULE* modules = modules_stack; + + DWORD needed_bytes = 0; + uintptr_t addr_base = 0; + + 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.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 (std::size_t i = 0; i < (needed_bytes / sizeof(HMODULE)); ++i) { + MODULEINFO module_info; + + // Get the module name + 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); + break; + } + } + } + + CloseHandle(process_handle); + + return addr_base; + } + +}}} // 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 d774e7e..17bf2e2 100644 --- a/include/boost/stacktrace/detail/frame_msvc.ipp +++ b/include/boost/stacktrace/detail/frame_msvc.ipp @@ -19,6 +19,10 @@ #include #include +#ifndef BOOST_STACKTRACE_DISABLE_OFFSET_ADDR_BASE +#include +#endif + #ifdef WIN32_LEAN_AND_MEAN #include #else @@ -391,7 +395,13 @@ public: if (!name.empty()) { res += name; } else { +#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(); +#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 a985515..645564a 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,12 @@ public: if (!Base::res.empty()) { Base::res = boost::core::demangle(Base::res.c_str()); } else { +#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(); +#endif } if (Base::prepare_source_location(addr)) { 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(); }