diff --git a/Userland/Libraries/LibDebug/CMakeLists.txt b/Userland/Libraries/LibDebug/CMakeLists.txt index f823a550d5..e2cc975fd8 100644 --- a/Userland/Libraries/LibDebug/CMakeLists.txt +++ b/Userland/Libraries/LibDebug/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES Dwarf/DwarfInfo.cpp Dwarf/Expression.cpp Dwarf/LineProgram.cpp + ProcessInspector.cpp StackFrameUtils.cpp ) diff --git a/Userland/Libraries/LibDebug/DebugSession.cpp b/Userland/Libraries/LibDebug/DebugSession.cpp index b91b0b801d..41b579dacf 100644 --- a/Userland/Libraries/LibDebug/DebugSession.cpp +++ b/Userland/Libraries/LibDebug/DebugSession.cpp @@ -44,6 +44,15 @@ DebugSession::~DebugSession() } } +void DebugSession::for_each_loaded_library(Function func) const +{ + for (const auto& lib_name : m_loaded_libraries.keys()) { + const auto& lib = *m_loaded_libraries.get(lib_name).value(); + if (func(lib) == IterationDecision::Break) + break; + } +} + OwnPtr DebugSession::exec_and_attach(String const& command, String source_root) { auto pid = fork(); @@ -110,39 +119,39 @@ OwnPtr DebugSession::exec_and_attach(String const& command, String return debug_session; } -bool DebugSession::poke(u32* address, u32 data) +bool DebugSession::poke(void* address, FlatPtr data) { - if (ptrace(PT_POKE, m_debuggee_pid, (void*)address, data) < 0) { + if (ptrace(PT_POKE, m_debuggee_pid, (void*)address, (void*)data) < 0) { perror("PT_POKE"); return false; } return true; } -Optional DebugSession::peek(u32* address) const +Optional DebugSession::peek(void* address) const { - Optional result; - int rc = ptrace(PT_PEEK, m_debuggee_pid, (void*)address, 0); + Optional result; + auto rc = ptrace(PT_PEEK, m_debuggee_pid, address, nullptr); if (errno == 0) - result = static_cast(rc); + result = static_cast(rc); return result; } -bool DebugSession::poke_debug(u32 register_index, u32 data) +bool DebugSession::poke_debug(u32 register_index, FlatPtr data) { - if (ptrace(PT_POKEDEBUG, m_debuggee_pid, reinterpret_cast(register_index), data) < 0) { + if (ptrace(PT_POKEDEBUG, m_debuggee_pid, reinterpret_cast(register_index), (void*)data) < 0) { perror("PT_POKEDEBUG"); return false; } return true; } -Optional DebugSession::peek_debug(u32 register_index) const +Optional DebugSession::peek_debug(u32 register_index) const { - Optional result; - int rc = ptrace(PT_PEEKDEBUG, m_debuggee_pid, reinterpret_cast(register_index), 0); + Optional result; + int rc = ptrace(PT_PEEKDEBUG, m_debuggee_pid, reinterpret_cast(register_index), nullptr); if (errno == 0) - result = static_cast(rc); + result = static_cast(rc); return result; } @@ -155,7 +164,7 @@ bool DebugSession::insert_breakpoint(void* address) if (m_breakpoints.contains(address)) return false; - auto original_bytes = peek(reinterpret_cast(address)); + auto original_bytes = peek(reinterpret_cast(address)); if (!original_bytes.has_value()) return false; @@ -175,7 +184,7 @@ bool DebugSession::disable_breakpoint(void* address) { auto breakpoint = m_breakpoints.get(address); VERIFY(breakpoint.has_value()); - if (!poke(reinterpret_cast(reinterpret_cast(breakpoint.value().address)), breakpoint.value().original_first_word)) + if (!poke(reinterpret_cast(reinterpret_cast(breakpoint.value().address)), breakpoint.value().original_first_word)) return false; auto bp = m_breakpoints.get(breakpoint.value().address).value(); @@ -191,7 +200,7 @@ bool DebugSession::enable_breakpoint(void* address) VERIFY(breakpoint.value().state == BreakPointState::Disabled); - if (!poke(reinterpret_cast(breakpoint.value().address), (breakpoint.value().original_first_word & ~(uint32_t)0xff) | BREAKPOINT_INSTRUCTION)) + if (!poke(reinterpret_cast(breakpoint.value().address), (breakpoint.value().original_first_word & ~(FlatPtr)0xff) | BREAKPOINT_INSTRUCTION)) return false; auto bp = m_breakpoints.get(breakpoint.value().address).value(); @@ -219,7 +228,8 @@ bool DebugSession::insert_watchpoint(void* address, u32 ebp) auto current_register_status = peek_debug(DEBUG_CONTROL_REGISTER); if (!current_register_status.has_value()) return false; - u32 dr7_value = current_register_status.value(); + // FIXME: 64 bit support + u32 dr7_value = static_cast(current_register_status.value()); u32 next_available_index; for (next_available_index = 0; next_available_index < 4; next_available_index++) { auto bitmask = 1 << (next_available_index * 2); @@ -455,54 +465,4 @@ void DebugSession::update_loaded_libs() }); } -const DebugSession::LoadedLibrary* DebugSession::library_at(FlatPtr address) const -{ - const LoadedLibrary* result = nullptr; - for_each_loaded_library([&result, address](const auto& lib) { - if (address >= lib.base_address && address < lib.base_address + lib.debug_info->elf().size()) { - result = &lib; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return result; -} - -Optional DebugSession::symbolicate(FlatPtr address) const -{ - auto* lib = library_at(address); - if (!lib) - return {}; - //FIXME: ELF::Image symlicate() API should return String::empty() if symbol is not found (It currently returns ??) - auto symbol = lib->debug_info->elf().symbolicate(address - lib->base_address); - return { { lib->name, symbol } }; -} - -Optional DebugSession::get_address_from_source_position(String const& file, size_t line) const -{ - Optional result; - for_each_loaded_library([file, line, &result](auto& lib) { - // The loader contains its own definitions for LibC symbols, so we don't want to include it in the search. - if (lib.name == "Loader.so") - return IterationDecision::Continue; - - auto source_position_and_address = lib.debug_info->get_address_from_source_position(file, line); - if (!source_position_and_address.has_value()) - return IterationDecision::Continue; - - result = source_position_and_address; - result.value().address += lib.base_address; - return IterationDecision::Break; - }); - return result; -} - -Optional DebugSession::get_source_position(FlatPtr address) const -{ - auto* lib = library_at(address); - if (!lib) - return {}; - return lib->debug_info->get_source_position(address - lib->base_address); -} - } diff --git a/Userland/Libraries/LibDebug/DebugSession.h b/Userland/Libraries/LibDebug/DebugSession.h index c678917770..43f05fb67f 100644 --- a/Userland/Libraries/LibDebug/DebugSession.h +++ b/Userland/Libraries/LibDebug/DebugSession.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,19 +24,23 @@ namespace Debug { -class DebugSession { +class DebugSession : public ProcessInspector { public: static OwnPtr exec_and_attach(String const& command, String source_root = {}); - ~DebugSession(); + virtual ~DebugSession() override; + + // ^Debug::ProcessInspector + virtual bool poke(void* address, FlatPtr data) override; + virtual Optional peek(void* address) const override; + virtual PtraceRegisters get_registers() const override; + virtual void set_registers(PtraceRegisters const&) override; + virtual void for_each_loaded_library(Function) const override; int pid() const { return m_debuggee_pid; } - bool poke(u32* address, u32 data); - Optional peek(u32* address) const; - - bool poke_debug(u32 register_index, u32 data); - Optional peek_debug(u32 register_index) const; + bool poke_debug(u32 register_index, FlatPtr data); + Optional peek_debug(u32 register_index) const; enum class BreakPointState { Enabled, @@ -44,7 +49,7 @@ public: struct BreakPoint { void* address { nullptr }; - u32 original_first_word { 0 }; + FlatPtr original_first_word { 0 }; BreakPointState state { BreakPointState::Disabled }; }; @@ -88,9 +93,6 @@ public: } } - PtraceRegisters get_registers() const; - void set_registers(PtraceRegisters const&); - enum class ContinueType { FreeRun, Syscall, @@ -126,45 +128,6 @@ public: Exited, }; - struct LoadedLibrary { - String name; - NonnullRefPtr file; - NonnullOwnPtr image; - NonnullOwnPtr debug_info; - FlatPtr base_address; - - LoadedLibrary(String const& name, NonnullRefPtr file, NonnullOwnPtr image, NonnullOwnPtr&& debug_info, FlatPtr base_address) - : name(name) - , file(move(file)) - , image(move(image)) - , debug_info(move(debug_info)) - , base_address(base_address) - { - } - }; - - template - void for_each_loaded_library(Func f) const - { - for (const auto& lib_name : m_loaded_libraries.keys()) { - const auto& lib = *m_loaded_libraries.get(lib_name).value(); - if (f(lib) == IterationDecision::Break) - break; - } - } - - const LoadedLibrary* library_at(FlatPtr address) const; - - struct SymbolicationResult { - String library_name; - String symbol; - }; - Optional symbolicate(FlatPtr address) const; - - Optional get_address_from_source_position(String const& file, size_t line) const; - - Optional get_source_position(FlatPtr address) const; - private: explicit DebugSession(pid_t, String source_root); @@ -180,7 +143,7 @@ private: HashMap m_breakpoints; HashMap m_watchpoints; - // Maps from base address to loaded library + // Maps from library name to LoadedLibrary obect HashMap> m_loaded_libraries; }; diff --git a/Userland/Libraries/LibDebug/LoadedLibrary.h b/Userland/Libraries/LibDebug/LoadedLibrary.h new file mode 100644 index 0000000000..15141bae88 --- /dev/null +++ b/Userland/Libraries/LibDebug/LoadedLibrary.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Itamar S. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "DebugInfo.h" +#include +#include +#include + +namespace Debug { +struct LoadedLibrary { + String name; + NonnullRefPtr file; + NonnullOwnPtr image; + NonnullOwnPtr debug_info; + FlatPtr base_address {}; + + LoadedLibrary(String const& name, NonnullRefPtr file, NonnullOwnPtr image, NonnullOwnPtr&& debug_info, FlatPtr base_address) + : name(name) + , file(move(file)) + , image(move(image)) + , debug_info(move(debug_info)) + , base_address(base_address) + { + } +}; + +} diff --git a/Userland/Libraries/LibDebug/ProcessInspector.cpp b/Userland/Libraries/LibDebug/ProcessInspector.cpp new file mode 100644 index 0000000000..c13f78cd58 --- /dev/null +++ b/Userland/Libraries/LibDebug/ProcessInspector.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021, Itamar S. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ProcessInspector.h" +#include "DebugInfo.h" + +namespace Debug { + +const LoadedLibrary* ProcessInspector::library_at(FlatPtr address) const +{ + const LoadedLibrary* result = nullptr; + for_each_loaded_library([&result, address](const auto& lib) { + if (address >= lib.base_address && address < lib.base_address + lib.debug_info->elf().size()) { + result = &lib; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return result; +} + +Optional ProcessInspector::symbolicate(FlatPtr address) const +{ + auto* lib = library_at(address); + if (!lib) + return {}; + // FIXME: ELF::Image symlicate() API should return String::empty() if symbol is not found (It currently returns ??) + auto symbol = lib->debug_info->elf().symbolicate(address - lib->base_address); + return { { lib->name, symbol } }; +} + +Optional ProcessInspector::get_address_from_source_position(String const& file, size_t line) const +{ + Optional result; + for_each_loaded_library([file, line, &result](auto& lib) { + // The loader contains its own definitions for LibC symbols, so we don't want to include it in the search. + if (lib.name == "Loader.so") + return IterationDecision::Continue; + + auto source_position_and_address = lib.debug_info->get_address_from_source_position(file, line); + if (!source_position_and_address.has_value()) + return IterationDecision::Continue; + + result = source_position_and_address; + result.value().address += lib.base_address; + return IterationDecision::Break; + }); + return result; +} + +Optional ProcessInspector::get_source_position(FlatPtr address) const +{ + auto* lib = library_at(address); + if (!lib) + return {}; + return lib->debug_info->get_source_position(address - lib->base_address); +} + +} diff --git a/Userland/Libraries/LibDebug/ProcessInspector.h b/Userland/Libraries/LibDebug/ProcessInspector.h new file mode 100644 index 0000000000..fd7731e9d8 --- /dev/null +++ b/Userland/Libraries/LibDebug/ProcessInspector.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Itamar S. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "LoadedLibrary.h" +#include +#include + +namespace Debug { + +class ProcessInspector { +public: + virtual ~ProcessInspector() { } + virtual bool poke(void* address, FlatPtr data) = 0; + virtual Optional peek(void* address) const = 0; + virtual PtraceRegisters get_registers() const = 0; + virtual void set_registers(PtraceRegisters const&) = 0; + virtual void for_each_loaded_library(Function) const = 0; + + const LoadedLibrary* library_at(FlatPtr address) const; + struct SymbolicationResult { + String library_name; + String symbol; + }; + Optional symbolicate(FlatPtr address) const; + Optional get_address_from_source_position(String const& file, size_t line) const; + Optional get_source_position(FlatPtr address) const; + +protected: + ProcessInspector() = default; +}; + +}; diff --git a/Userland/Utilities/functrace.cpp b/Userland/Utilities/functrace.cpp index 2ce14f0682..d81f3bf829 100644 --- a/Userland/Utilities/functrace.cpp +++ b/Userland/Utilities/functrace.cpp @@ -70,7 +70,7 @@ static void print_syscall(PtraceRegisters& regs, size_t depth) static NonnullOwnPtr> instrument_code() { auto instrumented = make>(); - g_debug_session->for_each_loaded_library([&](const Debug::DebugSession::LoadedLibrary& lib) { + g_debug_session->for_each_loaded_library([&](const Debug::LoadedLibrary& lib) { lib.debug_info->elf().for_each_section_of_type(SHT_PROGBITS, [&](const ELF::Image::Section& section) { if (section.name() != ".text") return IterationDecision::Continue;