Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 780dfe3

Browse files
committed
libexec/libdebug: Allow for attaching to / symbolication of running processes
1 parent c9cce88 commit 780dfe3

13 files changed

+364
-66
lines changed

libraries/libdebug/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
SET(SOURCES Debugger.cpp)
1+
SET(SOURCES Debugger.cpp Info.cpp LiveDebugger.cpp)
22
MAKE_LIBRARY(libdebug)
3-
TARGET_LINK_LIBRARIES(libdebug libtui)
3+
TARGET_LINK_LIBRARIES(libdebug libduck libsys libexec)

libraries/libdebug/Debugger.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,51 @@
22
/* Copyright © 2016-2023 Byteduck */
33

44
#include "Debugger.h"
5+
#include <cxxabi.h>
56

67
using namespace Debug;
8+
using Duck::ResultRet, Duck::Result, Duck::Ptr;
79

10+
Duck::ResultRet<AddressInfo> Debugger::info_at(size_t addr) {
11+
auto obj = TRY(object_at(addr));
12+
13+
if (addr < obj->memloc || addr > (obj->memloc + obj->memsz))
14+
return Result("Symbol outside of object memory");
15+
16+
auto sym = obj->get_symbol(addr - obj->memloc);
17+
if (!sym.name)
18+
return Result("No such symbol exists");
19+
20+
// Demangle symbol name
21+
int status = 0;
22+
auto* demangled_name = abi::__cxa_demangle(sym.name, nullptr, nullptr, &status);
23+
const AddressInfo ret {
24+
.symbol_name = status == 0 ? demangled_name : sym.name,
25+
.symbol_offset = sym.offset,
26+
.object = obj
27+
};
28+
if (status == 0)
29+
free(demangled_name);
30+
return ret;
31+
}
32+
33+
Duck::ResultRet<std::vector<AddressInfo>> Debugger::walk_stack() {
34+
auto regs = TRY(get_registers());
35+
std::vector<AddressInfo> ret;
36+
ret.push_back(TRY(info_at(regs.eip)));
37+
while (true) {
38+
auto retaddr_res = peek(regs.ebp + 4);
39+
if (retaddr_res.is_error() || !retaddr_res.value())
40+
break;
41+
auto info_res = info_at(retaddr_res.value());
42+
if (info_res.is_error())
43+
ret.push_back({"???", retaddr_res.value(), nullptr});
44+
else
45+
ret.push_back(info_res.value());
46+
auto ebp_res = peek(regs.ebp);
47+
if (ebp_res.is_error())
48+
break;
49+
regs.ebp = ebp_res.value();
50+
}
51+
return ret;
52+
}

libraries/libdebug/Debugger.h

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,25 @@
44
#pragma once
55
#include <sys/ptrace.h>
66
#include <libduck/Result.h>
7+
#include <libsys/Process.h>
8+
#include <libexec/Object.h>
9+
#include "Info.h"
10+
#include <sys/registers.h>
711

812
namespace Debug {
13+
14+
/// Generic interface for debugging - could either be a live process or core dump.
915
class Debugger {
1016
public:
1117
Debugger() = default;
1218

13-
Duck::Result attach(pid_t pid);
14-
Duck::Result detach();
19+
virtual Duck::ResultRet<uintptr_t> peek(size_t addr) = 0;
20+
virtual Duck::Result poke(size_t addr, uintptr_t val) = 0;
21+
virtual Duck::ResultRet<Sys::Process::MemoryRegion> region_at(size_t addr) = 0;
22+
virtual Duck::ResultRet<Exec::Object*> object_at(size_t addr) = 0;
23+
virtual Duck::ResultRet<PTraceRegisters> get_registers() = 0;
1524

16-
private:
17-
pid_t m_attached_pid = 0;
25+
Duck::ResultRet<AddressInfo> info_at(size_t addr);
26+
Duck::ResultRet<std::vector<AddressInfo>> walk_stack();
1827
};
1928
}

libraries/libdebug/Info.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/* SPDX-License-Identifier: GPL-3.0-or-later */
2+
/* Copyright © 2016-2024 Byteduck */
3+
4+
#include "Info.h"

libraries/libdebug/Info.h

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* SPDX-License-Identifier: GPL-3.0-or-later */
2+
/* Copyright © 2016-2024 Byteduck */
3+
4+
#pragma once
5+
6+
#include <string>
7+
#include <libexec/Object.h>
8+
9+
namespace Debug {
10+
struct AddressInfo {
11+
std::string symbol_name;
12+
size_t symbol_offset;
13+
Exec::Object* object;
14+
};
15+
}

libraries/libdebug/LiveDebugger.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* SPDX-License-Identifier: GPL-3.0-or-later */
2+
/* Copyright © 2016-2024 Byteduck */
3+
4+
#include "LiveDebugger.h"
5+
#include <sys/ptrace.h>
6+
#include <fcntl.h>
7+
#include <sys/mman.h>
8+
#include <libduck/MappedBuffer.h>
9+
10+
using namespace Debug;
11+
using Duck::Result, Duck::ResultRet;
12+
13+
Result LiveDebugger::attach(pid_t pid, tid_t tid) {
14+
if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) < 0)
15+
return Result(errno);
16+
m_pid = pid;
17+
m_tid = tid;
18+
TRYRES(reload_process_info());
19+
return Result::SUCCESS;
20+
}
21+
22+
ResultRet<uintptr_t> Debug::LiveDebugger::peek(size_t addr) {
23+
if (!m_pid || !m_tid)
24+
return Result("Not attached");
25+
uintptr_t data;
26+
if (ptrace(PTRACE_PEEK, m_tid, (void*) addr, &data) < 0)
27+
return Result(errno);
28+
return data;
29+
}
30+
31+
Result LiveDebugger::poke(size_t addr, uintptr_t val) {
32+
if (!m_pid || !m_tid)
33+
return Result("Not attached");
34+
if (ptrace(PTRACE_POKE, m_tid, (void*) addr, (void*) val) < 0)
35+
return Result(errno);
36+
return Result::SUCCESS;
37+
}
38+
39+
ResultRet<Sys::Process::MemoryRegion> LiveDebugger::region_at(size_t addr) {
40+
for (auto& region : m_memory_regions) {
41+
if (region.start + region.size > addr) {
42+
// Assume they're in order
43+
if (region.start > addr)
44+
return Result("No such memory region");
45+
return region;
46+
}
47+
}
48+
return Result("No such memory region");
49+
}
50+
51+
ResultRet<Exec::Object*> LiveDebugger::object_at(size_t addr) {
52+
auto reg = TRY(region_at(addr));
53+
if (reg.type != Sys::Process::MemoryRegion::Inode)
54+
return Result("Region not an object");
55+
56+
auto loadedobj = m_objects.find(reg.name);
57+
if (loadedobj != m_objects.end()) {
58+
auto obj = loadedobj->second.get();
59+
if (!obj)
60+
return Result("No object at region"); // Previously failed to load
61+
return obj;
62+
}
63+
64+
auto do_load = [&] () -> ResultRet<Duck::Ptr<Exec::Object>> {
65+
auto objfile = TRY(Duck::File::open(reg.name, "r"));
66+
auto mappedfile = TRY(Duck::MappedBuffer::make_file(objfile, Duck::MappedBuffer::R, Duck::MappedBuffer::SharedFile));
67+
auto obj = std::make_shared<Exec::Object>();
68+
obj->fd = objfile.fd();
69+
obj->mapped_file = mappedfile->data<uint8_t>();
70+
obj->mapped_size = mappedfile->size();
71+
obj->memloc = reg.start;
72+
obj->fd = objfile.fd();
73+
obj->name = Duck::Path(reg.name).basename();
74+
TRYRES(obj->load_for_debugger());
75+
objfile.set_close_on_destroy(false);
76+
mappedfile->set_unmap_on_destroy(false);
77+
return obj;
78+
};
79+
80+
auto load_res = do_load();
81+
if (load_res.is_error()) {
82+
m_objects[reg.name] = nullptr;
83+
return load_res.result();
84+
}
85+
m_objects[reg.name] = load_res.value();
86+
return load_res.value().get();
87+
}
88+
89+
Duck::ResultRet<PTraceRegisters> LiveDebugger::get_registers() {
90+
if (!m_pid || !m_tid)
91+
return Result("Not attached");
92+
PTraceRegisters ret;
93+
if (ptrace(PTRACE_GETREGS, m_tid, nullptr, &ret) < 0)
94+
return Result(errno);
95+
return ret;
96+
}
97+
98+
Result LiveDebugger::reload_process_info() {
99+
m_process = TRY(Sys::Process::get(m_pid));
100+
m_memory_regions = TRY(m_process.memory_regions());
101+
return Result::SUCCESS;
102+
}

libraries/libdebug/LiveDebugger.h

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* SPDX-License-Identifier: GPL-3.0-or-later */
2+
/* Copyright © 2016-2024 Byteduck */
3+
4+
#pragma once
5+
6+
#include "Debugger.h"
7+
8+
namespace Debug {
9+
class LiveDebugger: public Debugger {
10+
public:
11+
Duck::Result attach(pid_t pid, tid_t tid);
12+
13+
// Debugger
14+
Duck::ResultRet<uintptr_t> peek(size_t addr) override;
15+
Duck::Result poke(size_t addr, uintptr_t val) override;
16+
Duck::ResultRet<Sys::Process::MemoryRegion> region_at(size_t addr) override;
17+
Duck::ResultRet<Exec::Object*> object_at(size_t addr) override;
18+
Duck::ResultRet<PTraceRegisters> get_registers() override;
19+
20+
private:
21+
Duck::Result reload_process_info();
22+
23+
pid_t m_pid = 0, m_tid = 0;
24+
std::vector<Sys::Process::MemoryRegion> m_memory_regions;
25+
std::map<std::string, Duck::Ptr<Exec::Object>> m_objects;
26+
Sys::Process m_process;
27+
};
28+
}

libraries/libexec/Loader.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void Loader::set_main(Exec::Loader* loader) {
3232
}
3333

3434
Duck::Result Loader::load() {
35-
m_executable = new Object(*this);
35+
m_executable = new Object();
3636
m_objects[m_main_executable] = m_executable;
3737

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

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

5858
//Read the symbols from the libraries and executable
5959
auto rev_it = m_objects.rbegin();
6060
while(rev_it != m_objects.rend()) {
6161
auto* object = rev_it->second;
62-
object->read_symbols();
62+
object->read_symbols(*this);
6363
rev_it++;
6464
}
6565

6666
//Relocate the libraries and executable
6767
rev_it = m_objects.rbegin();
6868
while(rev_it != m_objects.rend()) {
6969
auto* object = rev_it->second;
70-
object->relocate();
70+
object->relocate(*this);
7171
object->mprotect_sections();
7272
rev_it++;
7373
}
@@ -109,8 +109,8 @@ Object* Loader::main_executable() const {
109109
return m_executable;
110110
}
111111

112-
size_t Loader::get_memloc_for(Object* object, bool is_main_executable) {
113-
if(is_main_executable) {
112+
size_t Loader::get_memloc_for(Object* object) {
113+
if(object == m_executable) {
114114
size_t alloc_start = (object->calculated_base / PAGE_SIZE) * PAGE_SIZE;
115115
size_t alloc_size = ((object->memsz + (object->calculated_base - alloc_start) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
116116
m_current_brk = alloc_start + alloc_size;
@@ -147,7 +147,7 @@ Object* Loader::open_library(const char* library_name) {
147147
}
148148

149149
//Add it to the objects map
150-
auto* object = new Object(*this);
150+
auto* object = new Object();
151151
m_objects[library_name] = object;
152152
object->fd = fd;
153153
object->name = library_name;

libraries/libexec/Loader.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Exec {
1818

1919
Duck::Result load();
2020

21-
size_t get_memloc_for(Object* object, bool is_main_executable);
21+
size_t get_memloc_for(Object* object);
2222
Object* open_library(const char* library_name);
2323
std::string find_library(const char* library_name);
2424
Object* main_executable() const;

0 commit comments

Comments
 (0)