From ac13e71877845dd2fb4463a5d117a2e792051a1a Mon Sep 17 00:00:00 2001 From: Jeremy <51220084+jeremy-rifkin@users.noreply.github.com> Date: Sun, 19 Nov 2023 22:49:58 -0600 Subject: [PATCH] Implement signal-safe resolution of basic object information --- CMakeLists.txt | 4 ++ src/binary/object.hpp | 9 +++++ src/binary/safe_dl.hpp | 59 ++++++++++++++++++++++++++++ src/cpptrace.cpp | 9 +++++ src/unwind/unwind_with_libunwind.cpp | 1 - test/signal_tracer.cpp | 28 +++++++++++++ 6 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/binary/safe_dl.hpp create mode 100644 test/signal_tracer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 79d1855..56996ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -484,4 +484,8 @@ if(CPPTRACE_BUILD_TESTING) COMMAND dsymutil $ ) endif() + + add_executable(signal_tracer test/signal_tracer.cpp) + target_compile_features(signal_tracer PRIVATE cxx_std_11) + target_link_libraries(signal_tracer PRIVATE ${target_name}) endif() diff --git a/src/binary/object.hpp b/src/binary/object.hpp index 4520e02..13e1e9b 100644 --- a/src/binary/object.hpp +++ b/src/binary/object.hpp @@ -148,6 +148,15 @@ namespace detail { return frames; } #endif + + inline object_frame resolve_minimal_object_frame(const minimal_object_frame& frame) { + return { + frame.object_path, + "", + frame.raw_address, + frame.address_relative_to_object_base_in_memory + get_module_image_base(frame.object_path) + }; + } } } diff --git a/src/binary/safe_dl.hpp b/src/binary/safe_dl.hpp new file mode 100644 index 0000000..36cab12 --- /dev/null +++ b/src/binary/safe_dl.hpp @@ -0,0 +1,59 @@ +#ifndef SAFE_DL_HPP +#define SAFE_DL_HPP + +#include "../utils/common.hpp" +#include "../utils/utils.hpp" +#include "../utils/program_name.hpp" + +#include +#include +#include +#include +#include +#include + +#if IS_LINUX || IS_APPLE + #include + #include + #include +#endif + +namespace cpptrace { +namespace detail { + inline void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out) { + dl_find_object result; + if(_dl_find_object(reinterpret_cast(address), &result) == 0) { + out->raw_address = address; + out->address_relative_to_object_base_in_memory = address - reinterpret_cast(result.dlfo_map_start); + if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) { + std::size_t path_length = std::strlen(result.dlfo_link_map->l_name); + std::memcpy( + out->object_path, + result.dlfo_link_map->l_name, + std::min(path_length + 1, std::size_t(CPPTRACE_PATH_MAX + 1)) + ); + } else { + // empty l_name, this means it's the currently running executable + memset(out->object_path, 0, PATH_MAX + 1); + auto res = readlink("/proc/self/exe", out->object_path, PATH_MAX); + if(res == -1) { + // error handling? + } + // TODO: Special handling for /proc/pid/exe unlink edge case + } + } else { + // std::cout<<"error"<raw_address = address; + out->address_relative_to_object_base_in_memory = 0; + out->object_path[0] = 0; + } + // TODO: Handle this part of the documentation? + // The address can be a code address or data address. On architectures using function descriptors, no attempt is + // made to decode the function descriptor. Depending on how these descriptors are implemented, _dl_find_object + // may return the object that defines the function descriptor (and not the object that contains the code + // implementing the function), or fail to find any object at all. + } +} +} + +#endif diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 662c29b..d5e2da6 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -19,6 +19,7 @@ #include "utils/common.hpp" #include "utils/utils.hpp" #include "binary/object.hpp" +#include "binary/safe_dl.hpp" #define ESC "\033[" #define RESET ESC "0m" @@ -342,6 +343,14 @@ namespace cpptrace { } } + object_frame minimal_object_frame::resolve() const { + return detail::resolve_minimal_object_frame(*this); + } + + void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out) { + detail::get_minimal_object_frame(address, out); + } + std::string demangle(const std::string& name) { return detail::demangle(name); } diff --git a/src/unwind/unwind_with_libunwind.cpp b/src/unwind/unwind_with_libunwind.cpp index 204e4ed..0e3c2ac 100644 --- a/src/unwind/unwind_with_libunwind.cpp +++ b/src/unwind/unwind_with_libunwind.cpp @@ -41,7 +41,6 @@ namespace detail { std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth) { // some code duplication, but whatever skip++; - std::vector frames; unw_context_t context; unw_cursor_t cursor; // thread and signal-safe https://www.nongnu.org/libunwind/man/unw_getcontext(3).html diff --git a/test/signal_tracer.cpp b/test/signal_tracer.cpp new file mode 100644 index 0000000..048f545 --- /dev/null +++ b/test/signal_tracer.cpp @@ -0,0 +1,28 @@ +#include + +#include +#include + +#include + +int main() { + cpptrace::object_trace trace; + while(true) { + cpptrace::minimal_object_frame frame; + // std::size_t res = read(STDIN_FILENO, &frame, sizeof(frame)); + std::size_t res = fread(&frame, sizeof(frame), 1, stdin); + if(res == 0) { + break; + } else if(res == -1) { + perror("Oops"); + break; + //} else if(res != sizeof(frame)) { + } else if(res != 1) { + std::cerr<<"Oops, size mismatch "<