fix addr2line for pie binaries (#138)

This commit is contained in:
ja2142
2023-08-27 20:34:56 +02:00
committed by GitHub
parent d849b145a6
commit fbcd543b51
2 changed files with 166 additions and 11 deletions

View File

@@ -12,6 +12,7 @@
# pragma once
#endif
#include <boost/stacktrace/detail/addr_base.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/stacktrace/detail/to_dec_array.hpp>
#include <boost/stacktrace/detail/try_dec_convert.hpp>
@@ -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<void*>(reinterpret_cast<uintptr_t>(addr) - addr_base);
std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", reinterpret_cast<const void*>(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 Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_addr2line> 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<void*>(reinterpret_cast<uintptr_t>(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<void*>(reinterpret_cast<uintptr_t>(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<void*>(reinterpret_cast<uintptr_t>(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

View File

@@ -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 <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <fstream>
#include <sstream>
#include <cstdlib>
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<uintptr_t>(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/<id>/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