From 3e9a7175d1137c481120e40100d8b98e1db9036b Mon Sep 17 00:00:00 2001 From: Itamar Date: Fri, 10 Apr 2020 17:35:49 +0300 Subject: [PATCH] Debugger: Add DebugSession The DebugSession class wraps the usage of Ptrace. It is intended to be used by cli & gui debugger programs. Also, call objdump for disassemly --- Applications/Debugger/DebugSession.cpp | 184 ++++++++++++++++ Applications/Debugger/DebugSession.h | 138 ++++++++++++ Applications/Debugger/Makefile | 3 +- Applications/Debugger/main.cpp | 287 +++++++++++++------------ Demos/Debugee/Makefile | 8 - Demos/Debugee/main.cpp | 12 -- Kernel/Process.cpp | 4 +- Kernel/Process.h | 1 + Kernel/ThreadTracer.h | 1 + Kernel/build-root-filesystem.sh | 1 - 10 files changed, 473 insertions(+), 166 deletions(-) create mode 100644 Applications/Debugger/DebugSession.cpp create mode 100644 Applications/Debugger/DebugSession.h delete mode 100644 Demos/Debugee/Makefile delete mode 100644 Demos/Debugee/main.cpp diff --git a/Applications/Debugger/DebugSession.cpp b/Applications/Debugger/DebugSession.cpp new file mode 100644 index 0000000000..8766ad60b2 --- /dev/null +++ b/Applications/Debugger/DebugSession.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2020, Itamar S. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DebugSession.h" +#include +#include + +DebugSession::DebugSession(int pid) + : m_debugee_pid(pid) + , m_executable(make(String::format("/proc/%d/exe", pid))) + , m_elf_image(make(reinterpret_cast(m_executable->data()), m_executable->size())) +{ +} + +DebugSession::~DebugSession() +{ + if (!m_is_debugee_dead) { + if (ptrace(PT_DETACH, m_debugee_pid, 0, 0) < 0) { + perror("PT_DETACH"); + } + } +} + +OwnPtr DebugSession::exec_and_attach(const String& command) +{ + int pid = fork(); + + if (!pid) { + if (ptrace(PT_TRACE_ME, 0, 0, 0) < 0) { + perror("PT_TRACE_ME"); + exit(1); + } + + auto parts = command.split(' '); + ASSERT(!parts.is_empty()); + const char** args = (const char**)calloc(parts.size() + 1, sizeof(const char*)); + for (size_t i = 0; i < parts.size(); i++) { + args[i] = parts[i].characters(); + } + int rc = execvp(args[0], const_cast(args)); + if (rc < 0) { + perror("execvp"); + } + ASSERT_NOT_REACHED(); + } + + if (waitpid(pid, nullptr, WSTOPPED) != pid) { + perror("waitpid"); + return nullptr; + } + + if (ptrace(PT_ATTACH, pid, 0, 0) < 0) { + perror("PT_ATTACH"); + return nullptr; + } + + // TOOD: do we actually need this waitpid? + if (waitpid(pid, nullptr, WSTOPPED) != pid) { + perror("waitpid"); + return nullptr; + } + + if (ptrace(PT_CONTINUE, pid, 0, 0) < 0) { + perror("continue"); + return nullptr; + } + + // We want to continue until the exit from the 'execve' sycsall. + // This ensures that when we start debugging the process + // it executes the target image, and not the forked image of the tracing process. + // NOTE: we only need to do this when we are debugging a new process (i.e not attaching to a process that's already running!) + + if (waitpid(pid, nullptr, WSTOPPED) != pid) { + perror("wait_pid"); + return nullptr; + } + + return make(pid); +} + +bool DebugSession::poke(u32* address, u32 data) +{ + if (ptrace(PT_POKE, m_debugee_pid, (void*)address, data) < 0) { + perror("PT_POKE"); + return false; + } + return true; +} + +Optional DebugSession::peek(u32* address) const +{ + Optional result; + int rc = ptrace(PT_PEEK, m_debugee_pid, (void*)address, 0); + if (errno == 0) + result = static_cast(rc); + return result; +} + +bool DebugSession::insert_breakpoint(void* address) +{ + // We insert a software breakpoint by + // patching the first byte of the instruction at 'address' + // with the breakpoint instruction (int3) + + if (m_breakpoints.contains(address)) + return false; + + auto original_bytes = peek(reinterpret_cast(address)); + + if (!original_bytes.has_value()) + return false; + + if (!poke(reinterpret_cast(address), (original_bytes.value() & ~(uint32_t)0xff) | BREAKPOINT_INSTRUCTION)) + return false; + + m_breakpoints.set(address, { address, original_bytes.value() }); + return true; +} + +void DebugSession::remove_breakpoint(const BreakPoint& breakpoint) +{ + ASSERT(m_breakpoints.contains(breakpoint.address)); + poke(reinterpret_cast(reinterpret_cast(breakpoint.address)), breakpoint.original_first_word); + m_breakpoints.remove(breakpoint.address); +} + +PtraceRegisters DebugSession::get_registers() const +{ + PtraceRegisters regs; + if (ptrace(PT_GETREGS, m_debugee_pid, ®s, 0) < 0) { + perror("PT_GETREGS"); + ASSERT_NOT_REACHED(); + } + return regs; +} + +void DebugSession::set_registers(const PtraceRegisters& regs) +{ + if (ptrace(PT_SETREGS, m_debugee_pid, reinterpret_cast(&const_cast(regs)), 0) < 0) { + perror("PT_SETREGS"); + ASSERT_NOT_REACHED(); + } +} + +Optional DebugSession::get_matching_breakpoint(const PtraceRegisters& regs) const +{ + return m_breakpoints.get(reinterpret_cast(regs.eip - 1)); +} + +void DebugSession::continue_debugee() +{ + if (ptrace(PT_CONTINUE, m_debugee_pid, 0, 0) < 0) { + perror("continue"); + ASSERT_NOT_REACHED(); + } +} + +VirtualAddress DebugSession::get_entry_point() const +{ + return m_elf_image->entry(); +} diff --git a/Applications/Debugger/DebugSession.h b/Applications/Debugger/DebugSession.h new file mode 100644 index 0000000000..9ac7774f20 --- /dev/null +++ b/Applications/Debugger/DebugSession.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2020, Itamar S. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DebugSession { +public: + static OwnPtr exec_and_attach(const String& command); + + // Has to be public for OwnPtr::make + DebugSession(int pid); + ~DebugSession(); + + int pid() const { return m_debugee_pid; } + + bool poke(u32* address, u32 data); + Optional peek(u32* address) const; + + struct BreakPoint { + void* address; + u32 original_first_word; + }; + + bool insert_breakpoint(void* address); + void remove_breakpoint(const BreakPoint&); + Optional get_matching_breakpoint(const PtraceRegisters&) const; + + PtraceRegisters get_registers() const; + void set_registers(const PtraceRegisters&); + + void continue_debugee(); + + template + void run(Callback callback); + + VirtualAddress get_entry_point() const; + + enum DebugDecision { + Continue, + Detach, + Kill, + }; + + enum DebugBreakReason { + Breakpoint, + Exited, + }; + +private: + // x86 breakpoint instruction "int3" + static constexpr u8 BREAKPOINT_INSTRUCTION + = 0xcc; + + int m_debugee_pid { -1 }; + bool m_is_debugee_dead { false }; + + NonnullOwnPtr m_executable; + NonnullOwnPtr m_elf_image; + + HashMap m_breakpoints; +}; + +template +void DebugSession::run(Callback callback) +{ + for (;;) { + continue_debugee(); + + int wstatus = 0; + if (waitpid(m_debugee_pid, &wstatus, WSTOPPED | WEXITED) != m_debugee_pid) { + perror("waitpid"); + ASSERT_NOT_REACHED(); + } + + // FIXME: This check actually only checks whether the debugee + // Is stopped because it hit a breakpoint or not + if (WSTOPSIG(wstatus) != SIGTRAP) { + callback(DebugBreakReason::Exited, Optional()); + m_is_debugee_dead = true; + break; + } + + auto regs = get_registers(); + + auto current_breakpoint = get_matching_breakpoint(regs); + if (current_breakpoint.has_value()) { + // FIXME: The current implementation removes a breakpoint + // after the first time it has been triggered. + remove_breakpoint(current_breakpoint.value()); + + // We need to re-execute the instruction we patched for the breakpoint, + // so we rollback the instruction pointer to the breakpoint's address + regs.eip = reinterpret_cast(current_breakpoint.value().address); + set_registers(regs); + DebugDecision decision = callback(DebugBreakReason::Breakpoint, regs); + if (decision != DebugDecision::Continue) { + // FIXME: implement detach & kill + ASSERT_NOT_REACHED(); + } + } + } +} diff --git a/Applications/Debugger/Makefile b/Applications/Debugger/Makefile index 2b9e6d7c50..32f18aa799 100755 --- a/Applications/Debugger/Makefile +++ b/Applications/Debugger/Makefile @@ -1,8 +1,9 @@ OBJS = \ + DebugSession.o \ main.o PROGRAM = Debugger -LIB_DEPS = Core +LIB_DEPS = Core X86 include ../../Makefile.common diff --git a/Applications/Debugger/main.cpp b/Applications/Debugger/main.cpp index 24d88dceaf..05fc498487 100644 --- a/Applications/Debugger/main.cpp +++ b/Applications/Debugger/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2020, Itamar S. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "DebugSession.h" #include #include +#include +#include +#include #include #include -#include +#include +#include #include #include #include #include -#include -#include #include static int usage() @@ -43,178 +46,176 @@ static int usage() return 1; } -static int g_pid = -1; +OwnPtr g_debug_session; static void handle_sigint(int) { - if (g_pid == -1) - return; + printf("Debugger: SIGINT\n"); - if (ptrace(PT_DETACH, g_pid, 0, 0) == -1) { - perror("detach"); - } + // The destructor of DebugSession takes care of detaching + g_debug_session = nullptr; } -void run_child_and_attach(char** argv) +String get_command() { - int pid = fork(); - - if (!pid) { - if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) { - perror("traceme"); - return exit(1); - } - - int rc = execvp(argv[1], &argv[1]); - if (rc < 0) { - perror("execvp"); + printf("(sdb) "); + fflush(stdout); + char* line = nullptr; + size_t allocated_size = 0; + ssize_t nread = getline(&line, &allocated_size, stdin); + if (nread < 0) { + if (errno == 0) { + fprintf(stderr, "\n"); + } else { + perror("getline"); exit(1); } - ASSERT_NOT_REACHED(); } - - g_pid = pid; - - if (waitpid(pid, nullptr, WSTOPPED) != pid) { - perror("waitpid"); - exit(1); - } - - if (ptrace(PT_ATTACH, g_pid, 0, 0) == -1) { - perror("attach"); - exit(1); - } - - if (waitpid(g_pid, nullptr, WSTOPPED) != g_pid) { - perror("waitpid"); - exit(1); - } - - dbg() << "debugee should continue until before execve exit"; - if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) { - perror("continue"); - } - - // we want to continue until the exit from the 'execve' sycsall - // we do this to ensure that when we start debugging the process, - // it executes the target image, and not the forked image of the debugger - // NOTE: we only need to do this when we are debugging a new process (i.e not attaching to a process that's already running!) - // if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) { - // perror("syscall"); - // exit(1); - // } - - if (waitpid(g_pid, nullptr, WSTOPPED) != g_pid) { - perror("wait_pid"); - exit(1); - } - // dbg() << "debugee should continue until after execve exit"; - // sleep(3); - - // if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) { - // perror("continue"); - // } - - // sleep(10); - - // if (waitpid(g_pid, nullptr, WSTOPPED) != g_pid) { - // perror("wait_pid"); - // exit(1); - // } - - // dbg() << "debugee should already be running"; - // if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) { - // perror("continue"); - // } + String command(line); + free(line); + if (command.ends_with('\n')) + command = command.substring(0, command.length() - 1); + return command; } -VirtualAddress get_entry_point(int pid) +void handle_print_registers(const PtraceRegisters& regs) { - auto path = String::format("/proc/%d/exe", pid); - dbg() << "path: " << path; - auto file = Core::File::construct(path); - if (!file->open(Core::File::ReadOnly)) { - fprintf(stderr, "Failed to open Debugged executable"); - exit(1); + printf("eax: 0x%x\n", regs.eax); + printf("ecx: 0x%x\n", regs.ecx); + printf("edx: 0x%x\n", regs.edx); + printf("ebx: 0x%x\n", regs.ebx); + printf("esp: 0x%x\n", regs.esp); + printf("ebp: 0x%x\n", regs.ebp); + printf("esi: 0x%x\n", regs.esi); + printf("edi: 0x%x\n", regs.edi); + printf("eip: 0x%x\n", regs.eip); + printf("eflags: 0x%x\n", regs.eflags); +} + +bool handle_disassemble_command(const String& command, void* first_instruction) +{ + auto parts = command.split(' '); + size_t number_of_instructions_to_disassemble = 5; + if (parts.size() == 2) { + bool ok; + number_of_instructions_to_disassemble = parts[1].to_uint(ok); + if (!ok) + return false; } - auto data = file->read_all(); - dbg() << "data size:" << data.size(); - ELFImage elf(data.data(), data.size()); - return elf.entry(); + + // FIXME: Instead of using a fixed "dump_size", + // we can feed instructions to the disassembler one by one + constexpr size_t dump_size = 0x100; + ByteBuffer code; + for (size_t i = 0; i < dump_size / sizeof(u32); ++i) { + auto value = g_debug_session->peek(reinterpret_cast(first_instruction) + i); + if (!value.has_value()) + break; + code.append(&value, sizeof(u32)); + } + + X86::SimpleInstructionStream stream(code.data(), code.size()); + X86::Disassembler disassembler(stream); + + for (size_t i = 0; i < number_of_instructions_to_disassemble; ++i) { + auto offset = stream.offset(); + auto insn = disassembler.next(); + if (!insn.has_value()) + break; + + printf(String::format(" %08x ", offset + reinterpret_cast(first_instruction)).characters()); + printf("<+%lu>:\t", reinterpret_cast(offset)); + printf("%s\n", insn.value().to_string(offset).characters()); + } + + return true; +} + +bool handle_breakpoint_command(const String& command) +{ + auto parts = command.split(' '); + if (parts.size() != 2) + return false; + + u32 breakpoint_address = strtoul(parts[1].characters(), nullptr, 16); + if (errno != 0) + return false; + bool success = g_debug_session->insert_breakpoint(reinterpret_cast(breakpoint_address)); + if (!success) { + fprintf(stderr, "coult not insert breakpoint at: 0x%x\n", breakpoint_address); + return false; + } + return true; +} + +void print_help() +{ + printf("Options:\n" + "cont - Continue execution\n" + "regs - Print registers\n" + "dis - Print disassembly\n" + "bp
- Insert a breakpoint\n"); } int main(int argc, char** argv) { // TODO: pledge & unveil - // TOOD: check that we didn't somehow hurt performance. boot seems slower? (or it's just laptop battey) + // TODO: check that strace still works if (argc == 1) return usage(); + StringBuilder command; + command.append(argv[1]); + for (int i = 2; i < argc; ++i) { + command.appendf("%s ", argv[i]); + } + + auto result = DebugSession::exec_and_attach(command.to_string()); + if (!result) { + fprintf(stderr, "Failed to start debugging session for: \"%s\"\n", command.to_string().characters()); + exit(1); + } + g_debug_session = result.release_nonnull(); + struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = handle_sigint; sigaction(SIGINT, &sa, nullptr); - run_child_and_attach(argv); + bool rc = g_debug_session->insert_breakpoint(g_debug_session->get_entry_point().as_ptr()); + ASSERT(rc); - dbg() << "pid:" << g_pid; - auto entry_point = get_entry_point(g_pid); - dbg() << "entry point:" << entry_point; + g_debug_session->run([&](DebugSession::DebugBreakReason reason, Optional optional_regs) { + if (reason == DebugSession::DebugBreakReason::Exited) { + printf("Program exited.\n"); + return DebugSession::DebugDecision::Detach; + } - const uint32_t original_instruction_data = ptrace(PT_PEEK, g_pid, (void*)entry_point.as_ptr(), 0); + ASSERT(optional_regs.has_value()); + const PtraceRegisters& regs = optional_regs.value(); - dbg() << "peeked data:" << (void*)original_instruction_data; + printf("Program is stopped at: 0x%x\n", regs.eip); + for (;;) { + auto command = get_command(); + bool success = false; - if (ptrace(PT_POKE, g_pid, (void*)entry_point.as_ptr(), (original_instruction_data & ~(uint32_t)0xff) | 0xcc) < 0) { - perror("poke"); - return 1; - } + if (command == "cont") { + return DebugSession::DebugDecision::Continue; + } - dbg() << "continuting"; + if (command == "regs") { + handle_print_registers(regs); + success = true; - if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) { - perror("continue"); - } - dbg() << "continued"; + } else if (command.starts_with("dis")) { + success = handle_disassemble_command(command, reinterpret_cast(regs.eip)); - // wait for breakpoint - if (waitpid(g_pid, nullptr, WSTOPPED) != g_pid) { - perror("waitpid"); - return 1; - } + } else if (command.starts_with("bp")) { + success = handle_breakpoint_command(command); + } - printf("hit breakpoint\n"); - - if (ptrace(PT_POKE, g_pid, (void*)entry_point.as_ptr(), original_instruction_data) < 0) { - perror("poke"); - return 1; - } - - PtraceRegisters regs; - if (ptrace(PT_GETREGS, g_pid, ®s, 0) < 0) { - perror("getregs"); - return 1; - } - - dbg() << "eip after breakpoint: " << (void*)regs.eip; - - regs.eip = reinterpret_cast(entry_point.as_ptr()); - dbg() << "settings eip back to:" << (void*)regs.eip; - if (ptrace(PT_SETREGS, g_pid, ®s, 0) < 0) { - perror("setregs"); - return 1; - } - - dbg() << "continuig"; - - if (ptrace(PT_CONTINUE, g_pid, 0, 0) == -1) { - perror("continue"); - } - - // wait for end - - if (waitpid(g_pid, nullptr, WSTOPPED) != g_pid) { - perror("waitpid"); - return 1; - } + if (!success) + print_help(); + } + }); } diff --git a/Demos/Debugee/Makefile b/Demos/Debugee/Makefile deleted file mode 100644 index 2c67a05707..0000000000 --- a/Demos/Debugee/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -OBJS = \ - main.o - -PROGRAM = Debugee - -# LIB_DEPS = Core - -include ../../Makefile.common diff --git a/Demos/Debugee/main.cpp b/Demos/Debugee/main.cpp deleted file mode 100644 index 6922f27a6f..0000000000 --- a/Demos/Debugee/main.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -int main(int, char**) -{ - printf("Debuggee main\n"); - int s = 0; - for (int i = 0; i < 10; ++i) { - s++; - } - printf("s: %d\n", s); - // asm("int3"); - return 0; -} diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index e6df853499..13054d8f86 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -4935,6 +4935,7 @@ bool Process::has_tracee_thread(int tracer_pid) const KResultOr Process::peek_user_data(u32* address) { if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) { + dbg() << "Invalid address for peek_user_data: " << address; return KResult(-EFAULT); } uint32_t result; @@ -4951,8 +4952,9 @@ KResultOr Process::peek_user_data(u32* address) KResult Process::poke_user_data(u32* address, u32 data) { // We validate for read (rather than write) because PT_POKE can write to readonly pages. - // So we wffectively only care that the poke operation only writes to user pages + // So we effectively only care that the poke operation is trying to write to user pages. if (!MM.validate_user_read(*this, VirtualAddress(address), sizeof(u32))) { + dbg() << "Invalid address for poke_user_data: " << address; return KResult(-EFAULT); } ProcessPagingScope scope(*this); diff --git a/Kernel/Process.h b/Kernel/Process.h index 98699701cd..05f9cbe94b 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -39,6 +39,7 @@ #include #include #include +#include #include namespace ELF { diff --git a/Kernel/ThreadTracer.h b/Kernel/ThreadTracer.h index fae20afcae..1ac492d868 100644 --- a/Kernel/ThreadTracer.h +++ b/Kernel/ThreadTracer.h @@ -46,6 +46,7 @@ public: void set_trace_syscalls(bool val) { m_trace_syscalls = val; } void set_regs(const RegisterState& regs); + void set_regs(const PtraceRegisters& regs) { m_regs = regs; } bool has_regs() const { return m_regs.has_value(); } const PtraceRegisters& regs() const { diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index ddc2ee86e3..38950b8a4e 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -146,7 +146,6 @@ cp ../Applications/Browser/Browser mnt/bin/Browser cp ../Applications/Debugger/Debugger mnt/bin/sdb cp ../Games/Solitaire/Solitaire mnt/bin/Solitaire cp ../Demos/HelloWorld/HelloWorld mnt/bin/HelloWorld -cp ../Demos/Debugee/Debugee mnt/bin/Debugee cp ../Demos/WidgetGallery/WidgetGallery mnt/bin/WidgetGallery cp ../Demos/Fire/Fire mnt/bin/Fire cp ../Demos/DynamicLink/LinkDemo/LinkDemo mnt/bin/LinkDemo