From fbcd543b5180b8ee307fef6fbfc34aa372d330cf Mon Sep 17 00:00:00 2001 From: ja2142 Date: Sun, 27 Aug 2023 20:34:56 +0200 Subject: [PATCH] fix addr2line for pie binaries (#138) --- .../stacktrace/detail/addr2line_impls.hpp | 89 ++++++++++++++++--- include/boost/stacktrace/detail/addr_base.hpp | 88 ++++++++++++++++++ 2 files changed, 166 insertions(+), 11 deletions(-) create mode 100644 include/boost/stacktrace/detail/addr_base.hpp diff --git a/include/boost/stacktrace/detail/addr2line_impls.hpp b/include/boost/stacktrace/detail/addr2line_impls.hpp index c1a0eef..ca598f7 100644 --- a/include/boost/stacktrace/detail/addr2line_impls.hpp +++ b/include/boost/stacktrace/detail/addr2line_impls.hpp @@ -12,6 +12,7 @@ # pragma once #endif +#include #include #include #include @@ -155,6 +156,19 @@ inline std::string addr2line(const char* flag, const void* addr) { return res; } +inline std::string source_location(const void* addr, bool position_independent) { + uintptr_t addr_base = 0; + if (position_independent) { + addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); + } + const void* offset = reinterpret_cast(reinterpret_cast(addr) - addr_base); + std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", reinterpret_cast(offset)); + if (source_line.empty() || source_line[0] == '?') { + return ""; + } + + return source_line; +} struct to_string_using_addr2line { std::string res; @@ -163,9 +177,20 @@ struct to_string_using_addr2line { } bool prepare_source_location(const void* addr) { - //return addr2line("-Cfipe", addr); // Does not seem to work in all cases - std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr); - if (!source_line.empty() && source_line[0] != '?') { + // general idea in all addr2line uses: + // in each case: + // - try to resolve whole address as if it was a non-pie binary + // - if that didn't work, try to resolve just an offset from binary base address + // this is needed because: + // - in pie binaries just passing an address to addr2line won't work (it needs an offset in this case) + // - in non-pie binaries whole address is needed (offset won't work) + // - there is no easy way to test if binary is position independent (that I know of) + std::string source_line = boost::stacktrace::detail::source_location(addr, false); + if(source_line.empty()) { + source_line = boost::stacktrace::detail::source_location(addr, true); + } + + if (!source_line.empty()) { res += " at "; res += source_line; return true; @@ -178,8 +203,13 @@ struct to_string_using_addr2line { template class to_string_impl_base; typedef to_string_impl_base to_string_impl; -inline std::string name_impl(const void* addr) { - std::string res = boost::stacktrace::detail::addr2line("-fe", addr); +inline std::string name(const void* addr, bool position_independent) { + uintptr_t addr_base = 0; + if(position_independent){ + addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); + } + const void* offset = reinterpret_cast(reinterpret_cast(addr) - addr_base); + std::string res = boost::stacktrace::detail::addr2line("-fe", offset); res = res.substr(0, res.find_last_of('\n')); res = boost::core::demangle(res.c_str()); @@ -190,11 +220,23 @@ inline std::string name_impl(const void* addr) { return res; } -} // namespace detail +inline std::string name_impl(const void* addr) { + std::string res = boost::stacktrace::detail::name(addr, false); + if (res.empty()) { + res = boost::stacktrace::detail::name(addr, true); + } -std::string frame::source_file() const { + return res; +} + +inline std::string source_file(const void* addr, bool position_independent) { std::string res; - res = boost::stacktrace::detail::addr2line("-e", addr_); + uintptr_t addr_base = 0; + if(position_independent){ + addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); + } + const void* offset = reinterpret_cast(reinterpret_cast(addr) - addr_base); + res = boost::stacktrace::detail::addr2line("-e", offset); res = res.substr(0, res.find_last_of(':')); if (res == "??") { res.clear(); @@ -203,10 +245,14 @@ std::string frame::source_file() const { return res; } - -std::size_t frame::source_line() const { +inline std::size_t source_line(const void* addr, bool position_independent) { std::size_t line_num = 0; - std::string res = boost::stacktrace::detail::addr2line("-e", addr_); + uintptr_t addr_base = 0; + if(position_independent){ + addr_base = boost::stacktrace::detail::get_own_proc_addr_base(addr); + } + const void* offset = reinterpret_cast(reinterpret_cast(addr) - addr_base); + std::string res = boost::stacktrace::detail::addr2line("-e", offset); const std::size_t last = res.find_last_of(':'); if (last == std::string::npos) { return 0; @@ -220,6 +266,27 @@ std::size_t frame::source_line() const { return line_num; } +} // namespace detail + + +std::string frame::source_file() const { + std::string res = boost::stacktrace::detail::source_file(addr_, false); + if (res.empty()) { + res = boost::stacktrace::detail::source_file(addr_, true); + } + + return res; +} + +std::size_t frame::source_line() const { + std::size_t line_num = boost::stacktrace::detail::source_line(addr_, false); + if (line_num == 0) { + line_num = boost::stacktrace::detail::source_line(addr_, true); + } + + return line_num; +} + }} // namespace boost::stacktrace diff --git a/include/boost/stacktrace/detail/addr_base.hpp b/include/boost/stacktrace/detail/addr_base.hpp new file mode 100644 index 0000000..50569e5 --- /dev/null +++ b/include/boost/stacktrace/detail/addr_base.hpp @@ -0,0 +1,88 @@ +// Copyright Antony Polukhin, 2016-2023. +// +// 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_HPP +#define BOOST_STACKTRACE_DETAIL_ADDR_BASE_HPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include + +namespace boost { namespace stacktrace { namespace detail { + +struct mapping_entry_t { + uintptr_t start = 0; + uintptr_t end = 0; + uintptr_t offset_from_base = 0; + + inline bool contains_addr(const void* addr) const { + uintptr_t addr_uint = reinterpret_cast(addr); + return addr_uint >= start && addr_uint < end; + } +}; + +inline uintptr_t hex_str_to_int(const std::string& str) { + uintptr_t out; + std::stringstream ss; + ss << std::hex << str; + ss >> out; + if(ss.eof() && !ss.fail()) { // whole stream read, with no errors + return out; + } else { + throw std::invalid_argument(std::string("can't convert '") + str + "' to hex"); + } +} + +// parse line from /proc//maps +// format: +// 7fb60d1ea000-7fb60d20c000 r--p 00000000 103:02 120327460 /usr/lib/libc.so.6 +// only parts 0 and 2 are interesting, these are: +// 0. mapping address range +// 2. mapping offset from base +inline mapping_entry_t parse_proc_maps_line(const std::string& line) { + std::string mapping_range_str, permissions_str, offset_from_base_str; + std::istringstream line_stream(line); + if(!std::getline(line_stream, mapping_range_str, ' ') || + !std::getline(line_stream, permissions_str, ' ') || + !std::getline(line_stream, offset_from_base_str, ' ')) { + return mapping_entry_t{}; + } + std::string mapping_start_str, mapping_end_str; + std::istringstream mapping_range_stream(mapping_range_str); + if(!std::getline(mapping_range_stream, mapping_start_str, '-') || + !std::getline(mapping_range_stream, mapping_end_str)) { + return mapping_entry_t{}; + } + mapping_entry_t mapping{}; + try { + mapping.start = hex_str_to_int(mapping_start_str); + mapping.end = hex_str_to_int(mapping_end_str); + mapping.offset_from_base = hex_str_to_int(offset_from_base_str); + return mapping; + } catch(std::invalid_argument& e) { + return mapping_entry_t{}; + } +} + +inline uintptr_t get_own_proc_addr_base(const void* addr) { + std::ifstream maps_file("/proc/self/maps"); + for (std::string line; std::getline(maps_file, line); ) { + const mapping_entry_t mapping = parse_proc_maps_line(line); + if (mapping.contains_addr(addr)) { + return mapping.start - mapping.offset_from_base; + } + } + return 0; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_ADDR_BASE_HPP