Browse Source

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.
Itamar 3 years ago
parent
commit
7950f5cb51

+ 1 - 0
Userland/Libraries/LibDebug/CMakeLists.txt

@@ -9,6 +9,7 @@ set(SOURCES
     Dwarf/DwarfInfo.cpp
     Dwarf/Expression.cpp
     Dwarf/LineProgram.cpp
+    ProcessInspector.cpp
     StackFrameUtils.cpp
 )
 

+ 26 - 66
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);
-}
-
 }

+ 14 - 51
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;
 };
 

+ 32 - 0
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)
+    {
+    }
+};
+
+}

+ 62 - 0
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);
+}
+
+}

+ 37 - 0
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;
+};
+
+};

+ 1 - 1
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;