Skip to content

Commit bf57aea

Browse files
committed
Implemented linux debugger.
1 parent 1aea626 commit bf57aea

File tree

5 files changed

+357
-31
lines changed

5 files changed

+357
-31
lines changed

NativeCore/Unix/Debugger.cpp

Lines changed: 278 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,300 @@
1+
#include <sys/ptrace.h>
2+
#include <sys/types.h>
3+
#include <sys/wait.h>
4+
#include <sys/time.h>
5+
#include <sys/user.h>
6+
#include <experimental/filesystem>
7+
#include <cstddef>
8+
19
#include "NativeCore.hpp"
210

11+
namespace fs = std::experimental::filesystem;
12+
13+
int ualarm(unsigned int milliseconds)
14+
{
15+
struct itimerval nval = { 0 };
16+
nval.it_value.tv_sec = milliseconds / 1000;
17+
nval.it_value.tv_usec = (long int)(milliseconds % 1000) * 1000;
18+
struct itimerval oval;
19+
if (setitimer(ITIMER_REAL, &nval, &oval) < 0)
20+
return 0;
21+
else
22+
return oval.it_value.tv_sec;
23+
}
24+
25+
pid_t waitpid_timeout(pid_t pid, int* status, int options, int timeoutInMilliseconds, bool& timedOut)
26+
{
27+
struct sigaction sig = {};
28+
sig.sa_flags = 0;
29+
sig.sa_handler = [](int) {};
30+
sigfillset(&sig.sa_mask);
31+
sigaction(SIGALRM, &sig, nullptr);
32+
33+
ualarm(timeoutInMilliseconds);
34+
35+
auto res = waitpid(pid, status, options);
36+
if (res == -1 && errno == EINTR)
37+
{
38+
timedOut = true;
39+
}
40+
else
41+
{
42+
ualarm(0); // Cancel the alarm.
43+
44+
timedOut = false;
45+
}
46+
return res;
47+
}
48+
49+
pid_t waitpid_timeout(int* status, int timeoutInMilliseconds, bool& timedOut)
50+
{
51+
return waitpid_timeout(-1, status, 0, timeoutInMilliseconds, timedOut);
52+
}
53+
354
extern "C" bool AttachDebuggerToProcess(RC_Pointer id)
455
{
56+
//TODO: Attach to all threads.
57+
58+
ptrace(PTRACE_ATTACH, (pid_t)(intptr_t)id, nullptr, nullptr);
59+
60+
waitpid(-1, nullptr, 0);
61+
62+
ptrace(PTRACE_CONT, (pid_t)(intptr_t)id, nullptr, nullptr);
63+
564
return false;
665
}
766

867
extern "C" void DetachDebuggerFromProcess(RC_Pointer id)
968
{
10-
69+
//TODO: Detach to all threads.
70+
71+
ptrace(PTRACE_DETACH, (pid_t)(intptr_t)id, nullptr, nullptr);
1172
}
1273

1374
extern "C" bool AwaitDebugEvent(DebugEvent* evt, int timeoutInMilliseconds)
1475
{
15-
return false;
76+
int status;
77+
bool timedOut;
78+
79+
auto tid = waitpid_timeout(&status, timeoutInMilliseconds, timedOut);
80+
81+
if (timedOut)
82+
{
83+
return false;
84+
}
85+
86+
auto result = false;
87+
88+
if (tid > 0)
89+
{
90+
evt->ThreadId = (RC_Pointer)(intptr_t)tid;
91+
92+
siginfo_t si;
93+
if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, &si) == 0)
94+
{
95+
if (si.si_signo == SIGTRAP)
96+
{
97+
struct user_regs_struct regs;
98+
if (ptrace(PTRACE_GETREGS, tid, nullptr, &regs) == 0)
99+
{
100+
DebugRegister6 dr6;
101+
dr6.Value = ptrace(PTRACE_PEEKUSER, tid, offsetof(struct user, u_debugreg[6]), nullptr);
102+
103+
// Check if breakpoint was a hardware breakpoint.
104+
if (dr6.DR0)
105+
{
106+
evt->ExceptionInfo.CausedBy = HardwareBreakpointRegister::Dr0;
107+
}
108+
else if (dr6.DR1)
109+
{
110+
evt->ExceptionInfo.CausedBy = HardwareBreakpointRegister::Dr1;
111+
}
112+
else if (dr6.DR2)
113+
{
114+
evt->ExceptionInfo.CausedBy = HardwareBreakpointRegister::Dr2;
115+
}
116+
else if (dr6.DR3)
117+
{
118+
evt->ExceptionInfo.CausedBy = HardwareBreakpointRegister::Dr3;
119+
}
120+
else
121+
{
122+
evt->ExceptionInfo.CausedBy = HardwareBreakpointRegister::InvalidRegister;
123+
}
124+
125+
// Copy registers.
126+
auto& reg = evt->ExceptionInfo.Registers;
127+
#ifdef __x86_64__
128+
reg.Rax = (RC_Pointer)regs.rax;
129+
reg.Rbx = (RC_Pointer)regs.rbx;
130+
reg.Rcx = (RC_Pointer)regs.rcx;
131+
reg.Rdx = (RC_Pointer)regs.rdx;
132+
reg.Rdi = (RC_Pointer)regs.rdi;
133+
reg.Rsi = (RC_Pointer)regs.rsi;
134+
reg.Rsp = (RC_Pointer)regs.rsp;
135+
reg.Rbp = (RC_Pointer)regs.rbp;
136+
reg.Rip = (RC_Pointer)regs.rip;
137+
138+
reg.R8 = (RC_Pointer)regs.r8;
139+
reg.R9 = (RC_Pointer)regs.r9;
140+
reg.R10 = (RC_Pointer)regs.r10;
141+
reg.R11 = (RC_Pointer)regs.r11;
142+
reg.R12 = (RC_Pointer)regs.r12;
143+
reg.R13 = (RC_Pointer)regs.r13;
144+
reg.R14 = (RC_Pointer)regs.r14;
145+
reg.R15 = (RC_Pointer)regs.r15;
146+
#else
147+
reg.Eax = (RC_Pointer)regs.eax;
148+
reg.Ebx = (RC_Pointer)regs.ebx;
149+
reg.Ecx = (RC_Pointer)regs.ecx;
150+
reg.Edx = (RC_Pointer)regs.edx;
151+
reg.Edi = (RC_Pointer)regs.edi;
152+
reg.Esi = (RC_Pointer)regs.esi;
153+
reg.Esp = (RC_Pointer)regs.esp;
154+
reg.Ebp = (RC_Pointer)regs.ebp;
155+
reg.Eip = (RC_Pointer)regs.eip;
156+
#endif
157+
158+
result = true;
159+
}
160+
}
161+
162+
if (result == false)
163+
{
164+
ptrace(PTRACE_CONT, tid, nullptr, si.si_signo);
165+
}
166+
}
167+
}
168+
169+
return result;
16170
}
17171

18172
extern "C" void HandleDebugEvent(DebugEvent* evt)
19173
{
20-
174+
auto tid = (pid_t)(intptr_t)evt->ThreadId;
175+
176+
siginfo_t si;
177+
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == 0)
178+
{
179+
auto signal = 0;
180+
switch (evt->ContinueStatus)
181+
{
182+
case DebugContinueStatus::Handled:
183+
signal = 0;
184+
break;
185+
case DebugContinueStatus::NotHandled:
186+
signal = si.si_signo;
187+
break;
188+
}
189+
190+
if (signal == SIGSTOP)
191+
{
192+
signal = 0;
193+
}
194+
195+
ptrace(PTRACE_CONT, tid, nullptr, signal);
196+
}
21197
}
22198

23199
extern "C" bool SetHardwareBreakpoint(RC_Pointer id, RC_Pointer address, HardwareBreakpointRegister reg, HardwareBreakpointTrigger type, HardwareBreakpointSize size, bool set)
24200
{
25-
return false;
201+
if (reg == HardwareBreakpointRegister::InvalidRegister)
202+
{
203+
return false;
204+
}
205+
206+
intptr_t addressValue = 0;
207+
int accessValue = 0;
208+
int lengthValue = 0;
209+
210+
if (set)
211+
{
212+
addressValue = (intptr_t)address;
213+
214+
if (type == HardwareBreakpointTrigger::Execute)
215+
accessValue = 0;
216+
else if (type == HardwareBreakpointTrigger::Access)
217+
accessValue = 3;
218+
else if (type == HardwareBreakpointTrigger::Write)
219+
accessValue = 1;
220+
221+
if (size == HardwareBreakpointSize::Size1)
222+
lengthValue = 0;
223+
else if (size == HardwareBreakpointSize::Size2)
224+
lengthValue = 1;
225+
else if (size == HardwareBreakpointSize::Size4)
226+
lengthValue = 3;
227+
else if (size == HardwareBreakpointSize::Size8)
228+
lengthValue = 2;
229+
}
230+
231+
auto tasksPath = fs::path("/proc") / std::to_string((intptr_t)id) / "task";
232+
if (fs::is_directory(tasksPath))
233+
{
234+
for (auto& d : fs::directory_iterator(tasksPath))
235+
{
236+
if (fs::is_directory(d))
237+
{
238+
auto taskPath = d.path();
239+
240+
auto name = taskPath.filename().string();
241+
if (is_number(name))
242+
{
243+
auto tid = parse_type<size_t>(name);
244+
245+
// Stop the thread. TODO: Check if the thread was already paused.
246+
for (int i = 0; i < 10; ++i)
247+
{
248+
kill(tid, SIGSTOP);
249+
250+
bool timedOut;
251+
waitpid_timeout(tid, nullptr, 0, 100, timedOut);
252+
if (!timedOut)
253+
{
254+
break;
255+
}
256+
}
257+
258+
DebugRegister7 dr7;
259+
dr7.Value = ptrace(PTRACE_PEEKUSER, tid, offsetof(struct user, u_debugreg[7]), nullptr);
260+
261+
intptr_t registerAddress;
262+
switch (reg)
263+
{
264+
case HardwareBreakpointRegister::Dr0:
265+
registerAddress = offsetof(struct user, u_debugreg[0]);
266+
dr7.G0 = true;
267+
dr7.RW0 = accessValue;
268+
dr7.Len0 = lengthValue;
269+
break;
270+
case HardwareBreakpointRegister::Dr1:
271+
registerAddress = offsetof(struct user, u_debugreg[1]);
272+
dr7.G1 = true;
273+
dr7.RW1 = accessValue;
274+
dr7.Len1 = lengthValue;
275+
break;
276+
case HardwareBreakpointRegister::Dr2:
277+
registerAddress = offsetof(struct user, u_debugreg[2]);
278+
dr7.G2 = true;
279+
dr7.RW2 = accessValue;
280+
dr7.Len2 = lengthValue;
281+
break;
282+
case HardwareBreakpointRegister::Dr3:
283+
registerAddress = offsetof(struct user, u_debugreg[3]);
284+
dr7.G3 = true;
285+
dr7.RW3 = accessValue;
286+
dr7.Len3 = lengthValue;
287+
break;
288+
}
289+
290+
ptrace(PTRACE_POKEUSER, tid, registerAddress, addressValue);
291+
ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg[7]), dr7.Value);
292+
293+
ptrace(PTRACE_CONT, tid, nullptr, nullptr);
294+
}
295+
}
296+
}
297+
}
298+
299+
return true;
26300
}

NativeCore/Unix/EnumerateProcesses.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,6 @@ fs::path my_read_symlink(const fs::path& p, std::error_code& ec)
4545

4646
#endif
4747

48-
bool is_number(const std::string& s)
49-
{
50-
auto it = s.begin();
51-
for (; it != s.end() && std::isdigit(*it); ++it);
52-
return !s.empty() && it == s.end();
53-
}
54-
55-
template<typename T>
56-
T parse_type(const std::string& s)
57-
{
58-
std::stringstream ss(s);
59-
60-
T val;
61-
ss >> val;
62-
return val;
63-
}
64-
6548
enum class Platform
6649
{
6750
Unknown,

NativeCore/Unix/NativeCore.Unix.vcxproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
<PositionIndependentCode>true</PositionIndependentCode>
150150
<CppLanguageStandard>c++1y</CppLanguageStandard>
151151
<AdditionalIncludeDirectories>../Dependencies/beaengine/include;$(Sysroot)\usr\include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
152-
<PreprocessorDefinitions>NATIVE_CORE_64</PreprocessorDefinitions>
152+
<PreprocessorDefinitions>RECLASSNET64</PreprocessorDefinitions>
153153
<AdditionalOptions>-m64 %(AdditionalOptions)</AdditionalOptions>
154154
</ClCompile>
155155
<Link>
@@ -162,7 +162,7 @@
162162
<PositionIndependentCode>true</PositionIndependentCode>
163163
<CppLanguageStandard>c++1y</CppLanguageStandard>
164164
<AdditionalIncludeDirectories>../Dependencies/beaengine/include;$(Sysroot)\usr\include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
165-
<PreprocessorDefinitions>NATIVE_CORE_64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
165+
<PreprocessorDefinitions>RECLASSNET64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
166166
<AdditionalOptions>-m64 %(AdditionalOptions)</AdditionalOptions>
167167
</ClCompile>
168168
<Link>
@@ -176,6 +176,7 @@
176176
<CppLanguageStandard>c++1y</CppLanguageStandard>
177177
<AdditionalIncludeDirectories>../Dependencies/beaengine/include;$(Sysroot)\usr\include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
178178
<AdditionalOptions>-m32 %(AdditionalOptions)</AdditionalOptions>
179+
<PreprocessorDefinitions>RECLASSNET32</PreprocessorDefinitions>
179180
</ClCompile>
180181
<Link>
181182
<AdditionalOptions>-m32 %(AdditionalOptions)</AdditionalOptions>
@@ -188,6 +189,7 @@
188189
<CppLanguageStandard>c++1y</CppLanguageStandard>
189190
<AdditionalIncludeDirectories>../Dependencies/beaengine/include;$(Sysroot)\usr\include;$(StlIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
190191
<AdditionalOptions>-m32 %(AdditionalOptions)</AdditionalOptions>
192+
<PreprocessorDefinitions>RECLASSNET32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
191193
</ClCompile>
192194
<Link>
193195
<AdditionalOptions>-m32 %(AdditionalOptions)</AdditionalOptions>

0 commit comments

Comments
 (0)