Skip to content

Commit

Permalink
libexec/libdebug: Allow for attaching to / symbolication of running p…
Browse files Browse the repository at this point in the history
…rocesses
  • Loading branch information
byteduck committed Apr 13, 2024
1 parent c9cce88 commit 780dfe3
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 66 deletions.
4 changes: 2 additions & 2 deletions libraries/libdebug/CMakeLists.txt
@@ -1,3 +1,3 @@
SET(SOURCES Debugger.cpp)
SET(SOURCES Debugger.cpp Info.cpp LiveDebugger.cpp)
MAKE_LIBRARY(libdebug)
TARGET_LINK_LIBRARIES(libdebug libtui)
TARGET_LINK_LIBRARIES(libdebug libduck libsys libexec)
45 changes: 45 additions & 0 deletions libraries/libdebug/Debugger.cpp
Expand Up @@ -2,6 +2,51 @@
/* Copyright © 2016-2023 Byteduck */

#include "Debugger.h"
#include <cxxabi.h>

using namespace Debug;
using Duck::ResultRet, Duck::Result, Duck::Ptr;

Duck::ResultRet<AddressInfo> Debugger::info_at(size_t addr) {
auto obj = TRY(object_at(addr));

if (addr < obj->memloc || addr > (obj->memloc + obj->memsz))
return Result("Symbol outside of object memory");

auto sym = obj->get_symbol(addr - obj->memloc);
if (!sym.name)
return Result("No such symbol exists");

// Demangle symbol name
int status = 0;
auto* demangled_name = abi::__cxa_demangle(sym.name, nullptr, nullptr, &status);
const AddressInfo ret {
.symbol_name = status == 0 ? demangled_name : sym.name,
.symbol_offset = sym.offset,
.object = obj
};
if (status == 0)
free(demangled_name);
return ret;
}

Duck::ResultRet<std::vector<AddressInfo>> Debugger::walk_stack() {
auto regs = TRY(get_registers());
std::vector<AddressInfo> ret;
ret.push_back(TRY(info_at(regs.eip)));
while (true) {
auto retaddr_res = peek(regs.ebp + 4);
if (retaddr_res.is_error() || !retaddr_res.value())
break;
auto info_res = info_at(retaddr_res.value());
if (info_res.is_error())
ret.push_back({"???", retaddr_res.value(), nullptr});
else
ret.push_back(info_res.value());
auto ebp_res = peek(regs.ebp);
if (ebp_res.is_error())
break;
regs.ebp = ebp_res.value();
}
return ret;
}
17 changes: 13 additions & 4 deletions libraries/libdebug/Debugger.h
Expand Up @@ -4,16 +4,25 @@
#pragma once
#include <sys/ptrace.h>
#include <libduck/Result.h>
#include <libsys/Process.h>
#include <libexec/Object.h>
#include "Info.h"
#include <sys/registers.h>

namespace Debug {

/// Generic interface for debugging - could either be a live process or core dump.
class Debugger {
public:
Debugger() = default;

Duck::Result attach(pid_t pid);
Duck::Result detach();
virtual Duck::ResultRet<uintptr_t> peek(size_t addr) = 0;
virtual Duck::Result poke(size_t addr, uintptr_t val) = 0;
virtual Duck::ResultRet<Sys::Process::MemoryRegion> region_at(size_t addr) = 0;
virtual Duck::ResultRet<Exec::Object*> object_at(size_t addr) = 0;
virtual Duck::ResultRet<PTraceRegisters> get_registers() = 0;

private:
pid_t m_attached_pid = 0;
Duck::ResultRet<AddressInfo> info_at(size_t addr);
Duck::ResultRet<std::vector<AddressInfo>> walk_stack();
};
}
4 changes: 4 additions & 0 deletions libraries/libdebug/Info.cpp
@@ -0,0 +1,4 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#include "Info.h"
15 changes: 15 additions & 0 deletions libraries/libdebug/Info.h
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#pragma once

#include <string>
#include <libexec/Object.h>

namespace Debug {
struct AddressInfo {
std::string symbol_name;
size_t symbol_offset;
Exec::Object* object;
};
}
102 changes: 102 additions & 0 deletions libraries/libdebug/LiveDebugger.cpp
@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#include "LiveDebugger.h"
#include <sys/ptrace.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <libduck/MappedBuffer.h>

using namespace Debug;
using Duck::Result, Duck::ResultRet;

Result LiveDebugger::attach(pid_t pid, tid_t tid) {
if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) < 0)
return Result(errno);
m_pid = pid;
m_tid = tid;
TRYRES(reload_process_info());
return Result::SUCCESS;
}

ResultRet<uintptr_t> Debug::LiveDebugger::peek(size_t addr) {
if (!m_pid || !m_tid)
return Result("Not attached");
uintptr_t data;
if (ptrace(PTRACE_PEEK, m_tid, (void*) addr, &data) < 0)
return Result(errno);
return data;
}

Result LiveDebugger::poke(size_t addr, uintptr_t val) {
if (!m_pid || !m_tid)
return Result("Not attached");
if (ptrace(PTRACE_POKE, m_tid, (void*) addr, (void*) val) < 0)
return Result(errno);
return Result::SUCCESS;
}

ResultRet<Sys::Process::MemoryRegion> LiveDebugger::region_at(size_t addr) {
for (auto& region : m_memory_regions) {
if (region.start + region.size > addr) {
// Assume they're in order
if (region.start > addr)
return Result("No such memory region");
return region;
}
}
return Result("No such memory region");
}

ResultRet<Exec::Object*> LiveDebugger::object_at(size_t addr) {
auto reg = TRY(region_at(addr));
if (reg.type != Sys::Process::MemoryRegion::Inode)
return Result("Region not an object");

auto loadedobj = m_objects.find(reg.name);
if (loadedobj != m_objects.end()) {
auto obj = loadedobj->second.get();
if (!obj)
return Result("No object at region"); // Previously failed to load
return obj;
}

auto do_load = [&] () -> ResultRet<Duck::Ptr<Exec::Object>> {
auto objfile = TRY(Duck::File::open(reg.name, "r"));
auto mappedfile = TRY(Duck::MappedBuffer::make_file(objfile, Duck::MappedBuffer::R, Duck::MappedBuffer::SharedFile));
auto obj = std::make_shared<Exec::Object>();
obj->fd = objfile.fd();
obj->mapped_file = mappedfile->data<uint8_t>();
obj->mapped_size = mappedfile->size();
obj->memloc = reg.start;
obj->fd = objfile.fd();
obj->name = Duck::Path(reg.name).basename();
TRYRES(obj->load_for_debugger());
objfile.set_close_on_destroy(false);
mappedfile->set_unmap_on_destroy(false);
return obj;
};

auto load_res = do_load();
if (load_res.is_error()) {
m_objects[reg.name] = nullptr;
return load_res.result();
}
m_objects[reg.name] = load_res.value();
return load_res.value().get();
}

Duck::ResultRet<PTraceRegisters> LiveDebugger::get_registers() {
if (!m_pid || !m_tid)
return Result("Not attached");
PTraceRegisters ret;
if (ptrace(PTRACE_GETREGS, m_tid, nullptr, &ret) < 0)
return Result(errno);
return ret;
}

Result LiveDebugger::reload_process_info() {
m_process = TRY(Sys::Process::get(m_pid));
m_memory_regions = TRY(m_process.memory_regions());
return Result::SUCCESS;
}
28 changes: 28 additions & 0 deletions libraries/libdebug/LiveDebugger.h
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#pragma once

#include "Debugger.h"

namespace Debug {
class LiveDebugger: public Debugger {
public:
Duck::Result attach(pid_t pid, tid_t tid);

// Debugger
Duck::ResultRet<uintptr_t> peek(size_t addr) override;
Duck::Result poke(size_t addr, uintptr_t val) override;
Duck::ResultRet<Sys::Process::MemoryRegion> region_at(size_t addr) override;
Duck::ResultRet<Exec::Object*> object_at(size_t addr) override;
Duck::ResultRet<PTraceRegisters> get_registers() override;

private:
Duck::Result reload_process_info();

pid_t m_pid = 0, m_tid = 0;
std::vector<Sys::Process::MemoryRegion> m_memory_regions;
std::map<std::string, Duck::Ptr<Exec::Object>> m_objects;
Sys::Process m_process;
};
}
14 changes: 7 additions & 7 deletions libraries/libexec/Loader.cpp
Expand Up @@ -32,7 +32,7 @@ void Loader::set_main(Exec::Loader* loader) {
}

Duck::Result Loader::load() {
m_executable = new Object(*this);
m_executable = new Object();
m_objects[m_main_executable] = m_executable;

//Open the executable
Expand All @@ -52,22 +52,22 @@ Duck::Result Loader::load() {
m_executable->mapped_file = (uint8_t*) mapped_file;

//Load the executable and its dependencies
if(m_executable->load(m_main_executable.c_str(), true) < 0)
if(m_executable->load(*this, m_main_executable.c_str()) < 0)
return errno;

//Read the symbols from the libraries and executable
auto rev_it = m_objects.rbegin();
while(rev_it != m_objects.rend()) {
auto* object = rev_it->second;
object->read_symbols();
object->read_symbols(*this);
rev_it++;
}

//Relocate the libraries and executable
rev_it = m_objects.rbegin();
while(rev_it != m_objects.rend()) {
auto* object = rev_it->second;
object->relocate();
object->relocate(*this);
object->mprotect_sections();
rev_it++;
}
Expand Down Expand Up @@ -109,8 +109,8 @@ Object* Loader::main_executable() const {
return m_executable;
}

size_t Loader::get_memloc_for(Object* object, bool is_main_executable) {
if(is_main_executable) {
size_t Loader::get_memloc_for(Object* object) {
if(object == m_executable) {
size_t alloc_start = (object->calculated_base / PAGE_SIZE) * PAGE_SIZE;
size_t alloc_size = ((object->memsz + (object->calculated_base - alloc_start) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
m_current_brk = alloc_start + alloc_size;
Expand Down Expand Up @@ -147,7 +147,7 @@ Object* Loader::open_library(const char* library_name) {
}

//Add it to the objects map
auto* object = new Object(*this);
auto* object = new Object();
m_objects[library_name] = object;
object->fd = fd;
object->name = library_name;
Expand Down
2 changes: 1 addition & 1 deletion libraries/libexec/Loader.h
Expand Up @@ -18,7 +18,7 @@ namespace Exec {

Duck::Result load();

size_t get_memloc_for(Object* object, bool is_main_executable);
size_t get_memloc_for(Object* object);
Object* open_library(const char* library_name);
std::string find_library(const char* library_name);
Object* main_executable() const;
Expand Down

0 comments on commit 780dfe3

Please sign in to comment.