Skip to content
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

Use Relative Virtual Addresses to allow their decoding without knowing the base address #200

Merged
merged 7 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions include/boost/stacktrace/detail/addr_base_msvc.hpp
Original file line number Diff line number Diff line change
@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif

#include <cstdio>
#include <memory>

#ifdef WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#else
// Prevent inclusion of extra Windows SDK headers which can cause conflict
// with other code using Windows SDK
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#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<HMODULE[]> 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<std::uintptr_t>(module_info.lpBaseOfDll);
break;
}
}
}

CloseHandle(process_handle);

return addr_base;
}

}}} // namespace boost::stacktrace::detail

#endif // BOOST_STACKTRACE_DETAIL_ADDR_BASE_MSVC_HPP
10 changes: 10 additions & 0 deletions include/boost/stacktrace/detail/frame_msvc.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
#include <boost/stacktrace/detail/to_dec_array.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>

#ifndef BOOST_STACKTRACE_DISABLE_OFFSET_ADDR_BASE
#include <boost/stacktrace/detail/addr_base_msvc.hpp>
#endif

#ifdef WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
Expand Down Expand Up @@ -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<uintptr_t>(addr) - base_addr).data();
#endif
}

std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr);
Expand Down
6 changes: 6 additions & 0 deletions include/boost/stacktrace/detail/frame_unwind.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/location_from_symbol.hpp>
#include <boost/stacktrace/detail/to_dec_array.hpp>
#include <boost/stacktrace/detail/addr_base.hpp>
#include <boost/core/demangle.hpp>

#include <cstdio>
Expand All @@ -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<uintptr_t>(addr) - addr_base).data();
#endif
}

if (Base::prepare_source_location(addr)) {
Expand Down
48 changes: 47 additions & 1 deletion test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <boost/stacktrace.hpp>
#include <stdexcept>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cctype>
Expand Down Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these helper functions be here or you prefer to move them to test_imp?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine to remain here

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<std::size_t>(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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to use BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES but there were configurations were it was set to 0, but the stacktrace had a function name included. Let me know if you're fine with this part.

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>();
Expand All @@ -283,6 +328,7 @@ int main() {
test_nested<260>(false);

test_stacktrace_limits();
test_relative_virtual_address(argv[0]);

return boost::report_errors();
}
Loading