diff --git a/rx/CMakeLists.txt b/rx/CMakeLists.txt index 6517f1c0d..7ddbf70aa 100644 --- a/rx/CMakeLists.txt +++ b/rx/CMakeLists.txt @@ -4,6 +4,7 @@ find_package(Git) add_library(${PROJECT_NAME} OBJECT + src/debug.cpp src/die.cpp src/hexdump.cpp src/mem.cpp diff --git a/rx/include/rx/debug.hpp b/rx/include/rx/debug.hpp new file mode 100644 index 000000000..ba8b8d4d3 --- /dev/null +++ b/rx/include/rx/debug.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace rx { +bool isDebuggerPresent(); +void waitForDebugger(); +void runDebugger(); +void breakpoint(); +void breakpointIfDebugging(); +} // namespace rx diff --git a/rx/src/debug.cpp b/rx/src/debug.cpp new file mode 100644 index 000000000..c8ed6855c --- /dev/null +++ b/rx/src/debug.cpp @@ -0,0 +1,101 @@ +#include "debug.hpp" +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#include +#include +#include + +bool rx::isDebuggerPresent() { + std::ifstream in("/proc/self/status"); + std::string line; + while (std::getline(in, line)) { + const std::string_view tracerPrefix = "TracerPid:\t"; + + if (!line.starts_with(tracerPrefix)) { + continue; + } + + std::string_view tracer = line; + tracer.remove_prefix(tracerPrefix.size()); + + if (tracer.size() == 1 && tracer[0] == '0') + return false; + + return true; + } + + return false; +} + +void rx::waitForDebugger() { + if (isDebuggerPresent()) { + return; + } + + std::println(stderr, "waiting for debugger, pid {}", ::getpid()); + + while (!isDebuggerPresent()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + std::println(stderr, "debugger was attached"); + std::this_thread::sleep_for(std::chrono::seconds(3)); + breakpoint(); +} + +void rx::runDebugger() { + int pid = ::getpid(); + char path[PATH_MAX]; + ::readlink("/proc/self/exe", path, sizeof(path)); + if (fork()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + waitForDebugger(); + return; + } + + auto pidString = std::to_string(pid); + const char *gdbPath = "/usr/bin/gdb"; + + std::list storage; + std::vector argv; + argv.push_back(gdbPath); + argv.push_back(path); + argv.push_back(pidString.c_str()); + argv.push_back("-iex"); + argv.push_back("set pagination off"); + argv.push_back("-ex"); + argv.push_back("handle SIGSYS nostop noprint"); + argv.push_back("-ex"); + argv.push_back("handle SIGUSR1 nostop noprint"); + // TODO: collect elfs + // argv.push_back("-ex"); + // argv.push_back("add-symbol-file 0x400000"); + argv.push_back(nullptr); + + execv(gdbPath, (char **)argv.data()); +} + +#else +bool rx::isDebuggerPresent() { return false; } +void rx::waitForDebugger() {} +void rx::runDebugger() {} +#endif + +void rx::breakpoint() { +#if __has_builtin(__builtin_debugtrap) + __builtin_debugtrap(); +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + __asm__ volatile("int3"); +#endif +} + +void rx::breakpointIfDebugging() { + if (isDebuggerPresent()) { + breakpoint(); + } +}