Implement signal-safe resolution of basic object information

This commit is contained in:
Jeremy 2023-11-19 22:49:58 -06:00
parent 327a6e8318
commit ac13e71877
No known key found for this signature in database
GPG Key ID: B4C8300FEC395042
6 changed files with 109 additions and 1 deletions

View File

@ -484,4 +484,8 @@ if(CPPTRACE_BUILD_TESTING)
COMMAND dsymutil $<TARGET_FILE:demo>
)
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()

View File

@ -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)
};
}
}
}

59
src/binary/safe_dl.hpp Normal file
View File

@ -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 <string>
#include <vector>
#include <mutex>
#include <unordered_map>
#include <cstring>
#include <iostream>
#if IS_LINUX || IS_APPLE
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
#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<void*>(address), &result) == 0) {
out->raw_address = address;
out->address_relative_to_object_base_in_memory = address - reinterpret_cast<frame_ptr>(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"<<std::endl;
out->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

View File

@ -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);
}

View File

@ -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<frame_ptr> frames;
unw_context_t context;
unw_cursor_t cursor;
// thread and signal-safe https://www.nongnu.org/libunwind/man/unw_getcontext(3).html

28
test/signal_tracer.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <unistd.h>
#include <cstdio>
#include <iostream>
#include <cpptrace/cpptrace.hpp>
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 "<<res<<" "<<sizeof(frame)<<std::endl;
break;
} else {
trace.frames.push_back(frame.resolve());
}
}
trace.resolve().print();
}