diff --git a/rx/CMakeLists.txt b/rx/CMakeLists.txt index cf3f48323..102c81373 100644 --- a/rx/CMakeLists.txt +++ b/rx/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(${PROJECT_NAME} OBJECT src/debug.cpp src/die.cpp src/FileLock.cpp + src/filesystem.cpp src/hexdump.cpp src/Mappable.cpp src/mem.cpp diff --git a/rx/include/rx/filesystem.hpp b/rx/include/rx/filesystem.hpp new file mode 100644 index 000000000..80f12ad01 --- /dev/null +++ b/rx/include/rx/filesystem.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace rx { +// Get the full path to the current executable +std::string getExecutablePath(); + +// Find an executable by name in system PATH +// Returns the full path if found, empty string otherwise +std::string findExecutable(const std::string &name); +} // namespace rx diff --git a/rx/src/debug.cpp b/rx/src/debug.cpp index eed39bf1b..7d408af64 100644 --- a/rx/src/debug.cpp +++ b/rx/src/debug.cpp @@ -1,8 +1,8 @@ #include "debug.hpp" #include "Process.hpp" +#include "filesystem.hpp" #include "print.hpp" #include -#include #include #include @@ -15,7 +15,6 @@ #else #ifdef __linux__ -#include #include #endif #include @@ -86,37 +85,36 @@ void rx::waitForDebugger() { } void rx::runDebugger() { -#ifdef __linux__ - 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(); + auto pid = getCurrentProcessId(); + auto path = rx::getExecutablePath(); + + auto pidString = std::to_string(pid); + + // Find gdb in PATH + auto gdbPath = rx::findExecutable("gdb"); + + if (gdbPath.empty()) { return; } - auto pidString = std::to_string(pid); - const char *gdbPath = "/usr/bin/gdb"; + std::vector args = { + path, + pidString, + "-iex", + "set pagination off", + "-ex", + "handle SIGSYS nostop noprint", + "-ex", + "handle SIGUSR1 nostop noprint" + // TODO: collect elfs + // "-ex", "add-symbol-file 0x400000" + }; - 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()); -#endif + ProcessId debuggerPid = rx::spawn(gdbPath, args); + if (debuggerPid != static_cast(-1)) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + waitForDebugger(); + } } void rx::breakpoint() { diff --git a/rx/src/filesystem.cpp b/rx/src/filesystem.cpp new file mode 100644 index 000000000..8073f8002 --- /dev/null +++ b/rx/src/filesystem.cpp @@ -0,0 +1,248 @@ +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +#include + +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#elif defined(__FreeBSD__) || defined(__DragonFly__) +#include +#include +#elif defined(__NetBSD__) | defined(__OpenBSD__) +#include +#elif defined(__sun) +#include +#endif + +std::string rx::getExecutablePath() { +#ifdef _WIN32 + wchar_t path[MAX_PATH]; + DWORD len = ::GetModuleFileNameW(nullptr, path, MAX_PATH); + if (len == 0 || len == MAX_PATH) { + return {}; + } + + // Convert wide string to narrow string + int size = + ::WideCharToMultiByte(CP_UTF8, 0, path, -1, nullptr, 0, nullptr, nullptr); + if (size <= 0) { + return {}; + } + + std::string result(size - 1, '\0'); + ::WideCharToMultiByte(CP_UTF8, 0, path, -1, result.data(), size, nullptr, + nullptr); + return result; + +#elif defined(__APPLE__) + char path[PATH_MAX]; + uint32_t size = sizeof(path); + if (_NSGetExecutablePath(path, &size) == 0) { + return path; + } + + // Buffer too small, allocate larger + std::string result(size, '\0'); + if (_NSGetExecutablePath(result.data(), &size) == 0) { + result.resize(size - 1); // Remove null terminator + return result; + } + return {}; + +#elif defined(__linux__) || defined(__ANDROID__) + char path[PATH_MAX]; + ssize_t len = ::readlink("/proc/self/exe", path, sizeof(path) - 1); + if (len == -1) { + return {}; + } + path[len] = '\0'; + return path; + +#elif defined(__FreeBSD__) || defined(__DragonFly__) + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + char path[PATH_MAX]; + size_t len = sizeof(path); + if (sysctl(mib, 4, path, &len, nullptr, 0) == 0) { + return path; + } + return {}; + +#elif defined(__NetBSD__) + int mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME}; + char path[PATH_MAX]; + size_t len = sizeof(path); + if (sysctl(mib, 4, path, &len, nullptr, 0) == 0) { + return path; + } + return {}; + +#elif defined(__OpenBSD__) + // OpenBSD doesn't provide a direct way to get executable path + // Return empty string as fallback + return {}; + +#elif defined(__sun) + // Solaris + const char *path = getexecname(); + if (path) { + if (path[0] == '/') { + return std::string(path); + } + // Relative path, need to prepend current directory + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd))) { + return std::string(cwd) + "/" + path; + } + } + return {}; + +#else +#error "rx::filesystem::getExecutablePath not implemented for this platform" +#endif +} + +std::string rx::findExecutable(const std::string &name) { + if (name.empty()) { + return {}; + } + +#ifdef _WIN32 + // On Windows, search PATH with automatic .exe extension handling + std::vector extensions; + + // Check if name already has an extension + bool hasExtension = name.find('.') != std::string::npos; + if (hasExtension) { + extensions.push_back(""); + } else { + // Try common executable extensions + const char *pathExt = std::getenv("PATHEXT"); + if (pathExt) { + std::string pathExtStr = pathExt; + size_t start = 0; + while (start < pathExtStr.size()) { + size_t end = pathExtStr.find(';', start); + if (end == std::string::npos) { + end = pathExtStr.size(); + } + std::string ext = pathExtStr.substr(start, end - start); + extensions.push_back(ext); + start = end + 1; + } + } else { + extensions = {".exe", ".bat", ".cmd", ".com"}; + } + } + + // Check if it's an absolute or relative path + if (name.find('\\') != std::string::npos || + name.find('/') != std::string::npos || + (name.size() >= 2 && name[1] == ':')) { + // It's a path, check if file exists + for (const auto &ext : extensions) { + std::string fullPath = name + ext; + DWORD attrs = ::GetFileAttributesA(fullPath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && + !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { + return fullPath; + } + } + return {}; + } + + // Search in PATH + const char *pathEnv = std::getenv("PATH"); + if (!pathEnv) { + return {}; + } + + std::string_view pathStr = pathEnv; + size_t start = 0; + while (start < pathStr.size()) { + size_t end = pathStr.find(';', start); + if (end == std::string::npos) { + end = pathStr.size(); + } + + auto dir = std::string(pathStr.substr(start, end - start)); + if (!dir.empty()) { + // Add separator if needed + if (dir.back() != '\\' && dir.back() != '/') { + dir += '\\'; + } + + for (const auto &ext : extensions) { + std::string fullPath = dir + name + ext; + DWORD attrs = ::GetFileAttributesA(fullPath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && + !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { + return fullPath; + } + } + } + + start = end + 1; + } + + return {}; + +#else + // Unix-like systems + // Check if it's already a path (contains /) + if (name.find('/') != std::string::npos) { + struct stat st; + if (stat(name.c_str(), &st) == 0 && S_ISREG(st.st_mode) && + (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + return name; + } + return {}; + } + + // Search in PATH + const char *pathEnv = std::getenv("PATH"); + if (!pathEnv) { + return {}; + } + + std::string pathStr = pathEnv; + size_t start = 0; + while (start < pathStr.size()) { + size_t end = pathStr.find(':', start); + if (end == std::string::npos) { + end = pathStr.size(); + } + + std::string dir = pathStr.substr(start, end - start); + if (!dir.empty()) { + // Add separator if needed + if (dir.back() != '/') { + dir += '/'; + } + + std::string fullPath = dir + name; + struct stat st; + if (stat(fullPath.c_str(), &st) == 0 && S_ISREG(st.st_mode) && + (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + return fullPath; + } + } + + start = end + 1; + } + + return {}; +#endif +}