diff options
author | Itamar <itamar8910@gmail.com> | 2021-11-19 16:13:07 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-20 21:22:24 +0000 |
commit | 7950f5cb51549dc304e51616597b96dc4c273b51 (patch) | |
tree | bb6a05c3ceac4f44f378cbbe6d1b84615665468b | |
parent | 38ddf301f63011992209baeb4bafd8f8047a2b9d (diff) | |
download | serenity-7950f5cb51549dc304e51616597b96dc4c273b51.zip |
LibDebug: Add ProcessInspector base class
ProcessInspector is an abstract base class for an object that can
inspect the address space of a process.
Concrete sub classes need to implement methods for peeking & poking
memory and walking the loaded libraries.
It is currently only implemented by DebugSession.
-rw-r--r-- | Userland/Libraries/LibDebug/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibDebug/DebugSession.cpp | 92 | ||||
-rw-r--r-- | Userland/Libraries/LibDebug/DebugSession.h | 65 | ||||
-rw-r--r-- | Userland/Libraries/LibDebug/LoadedLibrary.h | 32 | ||||
-rw-r--r-- | Userland/Libraries/LibDebug/ProcessInspector.cpp | 62 | ||||
-rw-r--r-- | Userland/Libraries/LibDebug/ProcessInspector.h | 37 | ||||
-rw-r--r-- | Userland/Utilities/functrace.cpp | 2 |
7 files changed, 173 insertions, 118 deletions
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<IterationDecision(LoadedLibrary const&)> 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> DebugSession::exec_and_attach(String const& command, String source_root) { auto pid = fork(); @@ -110,39 +119,39 @@ OwnPtr<DebugSession> 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<u32> DebugSession::peek(u32* address) const +Optional<FlatPtr> DebugSession::peek(void* address) const { - Optional<u32> result; - int rc = ptrace(PT_PEEK, m_debuggee_pid, (void*)address, 0); + Optional<FlatPtr> result; + auto rc = ptrace(PT_PEEK, m_debuggee_pid, address, nullptr); if (errno == 0) - result = static_cast<u32>(rc); + result = static_cast<FlatPtr>(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<u32*>(register_index), data) < 0) { + if (ptrace(PT_POKEDEBUG, m_debuggee_pid, reinterpret_cast<void*>(register_index), (void*)data) < 0) { perror("PT_POKEDEBUG"); return false; } return true; } -Optional<u32> DebugSession::peek_debug(u32 register_index) const +Optional<FlatPtr> DebugSession::peek_debug(u32 register_index) const { - Optional<u32> result; - int rc = ptrace(PT_PEEKDEBUG, m_debuggee_pid, reinterpret_cast<u32*>(register_index), 0); + Optional<FlatPtr> result; + int rc = ptrace(PT_PEEKDEBUG, m_debuggee_pid, reinterpret_cast<FlatPtr*>(register_index), nullptr); if (errno == 0) - result = static_cast<u32>(rc); + result = static_cast<FlatPtr>(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<u32*>(address)); + auto original_bytes = peek(reinterpret_cast<FlatPtr*>(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<u32*>(reinterpret_cast<char*>(breakpoint.value().address)), breakpoint.value().original_first_word)) + if (!poke(reinterpret_cast<FlatPtr*>(reinterpret_cast<char*>(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<u32*>(breakpoint.value().address), (breakpoint.value().original_first_word & ~(uint32_t)0xff) | BREAKPOINT_INSTRUCTION)) + if (!poke(reinterpret_cast<FlatPtr*>(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<u32>(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::SymbolicationResult> 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<DebugInfo::SourcePositionAndAddress> DebugSession::get_address_from_source_position(String const& file, size_t line) const -{ - Optional<DebugInfo::SourcePositionAndAddress> 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<DebugInfo::SourcePosition> 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 <AK/String.h> #include <LibC/sys/arch/i386/regs.h> #include <LibDebug/DebugInfo.h> +#include <LibDebug/ProcessInspector.h> #include <signal.h> #include <stdio.h> #include <sys/ptrace.h> @@ -23,19 +24,23 @@ namespace Debug { -class DebugSession { +class DebugSession : public ProcessInspector { public: static OwnPtr<DebugSession> exec_and_attach(String const& command, String source_root = {}); - ~DebugSession(); + virtual ~DebugSession() override; - int pid() const { return m_debuggee_pid; } + // ^Debug::ProcessInspector + virtual bool poke(void* address, FlatPtr data) override; + virtual Optional<FlatPtr> 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<IterationDecision(LoadedLibrary const&)>) const override; - bool poke(u32* address, u32 data); - Optional<u32> peek(u32* address) const; + int pid() const { return m_debuggee_pid; } - bool poke_debug(u32 register_index, u32 data); - Optional<u32> peek_debug(u32 register_index) const; + bool poke_debug(u32 register_index, FlatPtr data); + Optional<FlatPtr> 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<MappedFile> file; - NonnullOwnPtr<ELF::Image> image; - NonnullOwnPtr<DebugInfo> debug_info; - FlatPtr base_address; - - LoadedLibrary(String const& name, NonnullRefPtr<MappedFile> file, NonnullOwnPtr<ELF::Image> image, NonnullOwnPtr<DebugInfo>&& debug_info, FlatPtr base_address) - : name(name) - , file(move(file)) - , image(move(image)) - , debug_info(move(debug_info)) - , base_address(base_address) - { - } - }; - - template<typename Func> - 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<SymbolicationResult> symbolicate(FlatPtr address) const; - - Optional<DebugInfo::SourcePositionAndAddress> get_address_from_source_position(String const& file, size_t line) const; - - Optional<DebugInfo::SourcePosition> get_source_position(FlatPtr address) const; - private: explicit DebugSession(pid_t, String source_root); @@ -180,7 +143,7 @@ private: HashMap<void*, BreakPoint> m_breakpoints; HashMap<void*, WatchPoint> m_watchpoints; - // Maps from base address to loaded library + // Maps from library name to LoadedLibrary obect HashMap<String, NonnullOwnPtr<LoadedLibrary>> 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. <itamar8910@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "DebugInfo.h" +#include <AK/MappedFile.h> +#include <AK/Types.h> +#include <LibELF/Image.h> + +namespace Debug { +struct LoadedLibrary { + String name; + NonnullRefPtr<MappedFile> file; + NonnullOwnPtr<ELF::Image> image; + NonnullOwnPtr<DebugInfo> debug_info; + FlatPtr base_address {}; + + LoadedLibrary(String const& name, NonnullRefPtr<MappedFile> file, NonnullOwnPtr<ELF::Image> image, NonnullOwnPtr<DebugInfo>&& 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. <itamar8910@gmail.com> + * + * 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::SymbolicationResult> 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<DebugInfo::SourcePositionAndAddress> ProcessInspector::get_address_from_source_position(String const& file, size_t line) const +{ + Optional<DebugInfo::SourcePositionAndAddress> 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<DebugInfo::SourcePosition> 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. <itamar8910@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "LoadedLibrary.h" +#include <AK/Types.h> +#include <LibC/sys/arch/i386/regs.h> + +namespace Debug { + +class ProcessInspector { +public: + virtual ~ProcessInspector() { } + virtual bool poke(void* address, FlatPtr data) = 0; + virtual Optional<FlatPtr> 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<IterationDecision(LoadedLibrary const&)>) const = 0; + + const LoadedLibrary* library_at(FlatPtr address) const; + struct SymbolicationResult { + String library_name; + String symbol; + }; + Optional<SymbolicationResult> symbolicate(FlatPtr address) const; + Optional<DebugInfo::SourcePositionAndAddress> get_address_from_source_position(String const& file, size_t line) const; + Optional<DebugInfo::SourcePosition> 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<HashMap<void*, X86::Instruction>> instrument_code() { auto instrumented = make<HashMap<void*, X86::Instruction>>(); - 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; |