From b29242862bb650bd3c0d3dbad0466a30d2d015e5 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Fri, 8 Apr 2022 23:53:42 -0400
Subject: [PATCH] core: extract symbol reading

---
 src/core/CMakeLists.txt        |   2 +
 src/core/arm/arm_interface.cpp | 141 +++---------------------
 src/core/arm/symbols.cpp       | 190 +++++++++++++++++++++++++++++++++
 src/core/arm/symbols.h         |  27 +++++
 4 files changed, 231 insertions(+), 129 deletions(-)
 create mode 100644 src/core/arm/symbols.cpp
 create mode 100644 src/core/arm/symbols.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 81eaf0942f..ffbda79256 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -13,6 +13,8 @@ add_library(core STATIC
     arm/dynarmic/arm_exclusive_monitor.h
     arm/exclusive_monitor.cpp
     arm/exclusive_monitor.h
+    arm/symbols.cpp
+    arm/symbols.h
     constants.cpp
     constants.h
     core.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e19768..08bf1201d4 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -8,134 +8,13 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "core/arm/arm_interface.h"
+#include "core/arm/symbols.h"
 #include "core/core.h"
+#include "core/hle/kernel/k_process.h"
 #include "core/loader/loader.h"
 #include "core/memory.h"
 
 namespace Core {
-namespace {
-
-constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
-constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
-constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
-constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
-
-enum class ELFSymbolType : u8 {
-    None = 0,
-    Object = 1,
-    Function = 2,
-    Section = 3,
-    File = 4,
-    Common = 5,
-    TLS = 6,
-};
-
-enum class ELFSymbolBinding : u8 {
-    Local = 0,
-    Global = 1,
-    Weak = 2,
-};
-
-enum class ELFSymbolVisibility : u8 {
-    Default = 0,
-    Internal = 1,
-    Hidden = 2,
-    Protected = 3,
-};
-
-struct ELFSymbol {
-    u32 name_index;
-    union {
-        u8 info;
-
-        BitField<0, 4, ELFSymbolType> type;
-        BitField<4, 4, ELFSymbolBinding> binding;
-    };
-    ELFSymbolVisibility visibility;
-    u16 sh_index;
-    u64 value;
-    u64 size;
-};
-static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
-
-using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
-
-Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
-    const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
-
-    if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
-        memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
-        return {};
-    }
-
-    const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
-
-    VAddr string_table_offset{};
-    VAddr symbol_table_offset{};
-    u64 symbol_entry_size{};
-
-    VAddr dynamic_index = dynamic_offset;
-    while (true) {
-        const u64 tag = memory.Read64(dynamic_index);
-        const u64 value = memory.Read64(dynamic_index + 0x8);
-        dynamic_index += 0x10;
-
-        if (tag == ELF_DYNAMIC_TAG_NULL) {
-            break;
-        }
-
-        if (tag == ELF_DYNAMIC_TAG_STRTAB) {
-            string_table_offset = value;
-        } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
-            symbol_table_offset = value;
-        } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
-            symbol_entry_size = value;
-        }
-    }
-
-    if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
-        return {};
-    }
-
-    const auto string_table_address = text_offset + string_table_offset;
-    const auto symbol_table_address = text_offset + symbol_table_offset;
-
-    Symbols out;
-
-    VAddr symbol_index = symbol_table_address;
-    while (symbol_index < string_table_address) {
-        ELFSymbol symbol{};
-        memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
-
-        VAddr string_offset = string_table_address + symbol.name_index;
-        std::string name;
-        for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
-            name += static_cast<char>(c);
-        }
-
-        symbol_index += symbol_entry_size;
-        out.push_back({symbol, name});
-    }
-
-    return out;
-}
-
-std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
-    const auto iter =
-        std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
-            const auto& symbol = pair.first;
-            const auto end_address = symbol.value + symbol.size;
-            return func_address >= symbol.value && func_address < end_address;
-        });
-
-    if (iter == symbols.end()) {
-        return std::nullopt;
-    }
-
-    return iter->second;
-}
-
-} // Anonymous namespace
 
 constexpr u64 SEGMENT_BASE = 0x7100000000ull;
 
@@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
         return {};
     }
 
-    std::map<std::string, Symbols> symbols;
+    std::map<std::string, Symbols::Symbols> symbols;
     for (const auto& module : modules) {
-        symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
+        symbols.insert_or_assign(module.second,
+                                 Symbols::GetSymbols(module.first, system.Memory(),
+                                                     system.CurrentProcess()->Is64BitProcess()));
     }
 
     for (auto& entry : out) {
@@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
 
         const auto symbol_set = symbols.find(entry.module);
         if (symbol_set != symbols.end()) {
-            const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+            const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
             if (symbol.has_value()) {
                 // TODO(DarkLordZach): Add demangling of symbol names.
                 entry.name = *symbol;
@@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
         return {};
     }
 
-    std::map<std::string, Symbols> symbols;
+    std::map<std::string, Symbols::Symbols> symbols;
     for (const auto& module : modules) {
-        symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
+        symbols.insert_or_assign(module.second,
+                                 Symbols::GetSymbols(module.first, system.Memory(),
+                                                     system.CurrentProcess()->Is64BitProcess()));
     }
 
     for (auto& entry : out) {
@@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
 
         const auto symbol_set = symbols.find(entry.module);
         if (symbol_set != symbols.end()) {
-            const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+            const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
             if (symbol.has_value()) {
                 // TODO(DarkLordZach): Add demangling of symbol names.
                 entry.name = *symbol;
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 0000000000..26c44f0c78
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,190 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/memory.h"
+
+namespace Core {
+namespace {
+
+constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
+constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
+constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
+constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
+
+enum class ELFSymbolType : u8 {
+    None = 0,
+    Object = 1,
+    Function = 2,
+    Section = 3,
+    File = 4,
+    Common = 5,
+    TLS = 6,
+};
+
+enum class ELFSymbolBinding : u8 {
+    Local = 0,
+    Global = 1,
+    Weak = 2,
+};
+
+enum class ELFSymbolVisibility : u8 {
+    Default = 0,
+    Internal = 1,
+    Hidden = 2,
+    Protected = 3,
+};
+
+struct ELF64Symbol {
+    u32 name_index;
+    union {
+        u8 info;
+
+        BitField<0, 4, ELFSymbolType> type;
+        BitField<4, 4, ELFSymbolBinding> binding;
+    };
+    ELFSymbolVisibility visibility;
+    u16 sh_index;
+    u64 value;
+    u64 size;
+};
+static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
+
+struct ELF32Symbol {
+    u32 name_index;
+    u32 value;
+    u32 size;
+    union {
+        u8 info;
+
+        BitField<0, 4, ELFSymbolType> type;
+        BitField<4, 4, ELFSymbolBinding> binding;
+    };
+    ELFSymbolVisibility visibility;
+    u16 sh_index;
+};
+static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
+
+} // Anonymous namespace
+
+namespace Symbols {
+
+template <typename Word, typename ELFSymbol, typename ByteReader>
+static Symbols GetSymbols(ByteReader ReadBytes) {
+    const auto Read8{[&](u64 index) {
+        u8 ret;
+        ReadBytes(&ret, index, sizeof(u8));
+        return ret;
+    }};
+
+    const auto Read32{[&](u64 index) {
+        u32 ret;
+        ReadBytes(&ret, index, sizeof(u32));
+        return ret;
+    }};
+
+    const auto ReadWord{[&](u64 index) {
+        Word ret;
+        ReadBytes(&ret, index, sizeof(Word));
+        return ret;
+    }};
+
+    const u32 mod_offset = Read32(4);
+
+    if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+        return {};
+    }
+
+    VAddr string_table_offset{};
+    VAddr symbol_table_offset{};
+    u64 symbol_entry_size{};
+
+    const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
+
+    VAddr dynamic_index = dynamic_offset;
+    while (true) {
+        const Word tag = ReadWord(dynamic_index);
+        const Word value = ReadWord(dynamic_index + sizeof(Word));
+        dynamic_index += 2 * sizeof(Word);
+
+        if (tag == ELF_DYNAMIC_TAG_NULL) {
+            break;
+        }
+
+        if (tag == ELF_DYNAMIC_TAG_STRTAB) {
+            string_table_offset = value;
+        } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
+            symbol_table_offset = value;
+        } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
+            symbol_entry_size = value;
+        }
+    }
+
+    if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
+        return {};
+    }
+
+    Symbols out;
+
+    VAddr symbol_index = symbol_table_offset;
+    while (symbol_index < string_table_offset) {
+        ELFSymbol symbol{};
+        ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
+
+        VAddr string_offset = string_table_offset + symbol.name_index;
+        std::string name;
+        for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
+            name += static_cast<char>(c);
+        }
+
+        symbol_index += symbol_entry_size;
+        out[name] = std::make_pair(symbol.value, symbol.size);
+    }
+
+    return out;
+}
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
+    const auto ReadBytes{
+        [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
+
+    if (is_64) {
+        return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+    } else {
+        return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+    }
+}
+
+Symbols GetSymbols(std::span<const u8> data, bool is_64) {
+    const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
+        std::memcpy(ptr, data.data() + offset, size);
+    }};
+
+    if (is_64) {
+        return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+    } else {
+        return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+    }
+}
+
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
+    const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
+        const auto& [name, sym_info] = pair;
+        const auto& [start_address, size] = sym_info;
+        const auto end_address = start_address + size;
+        return addr >= start_address && addr < end_address;
+    });
+
+    if (iter == symbols.cend()) {
+        return std::nullopt;
+    }
+
+    return iter->first;
+}
+
+} // namespace Symbols
+} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 0000000000..99e6a9f8e7
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,27 @@
+// Copyright 2022 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <optional>
+#include <span>
+#include <string>
+#include <utility>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+} // namespace Core::Memory
+
+namespace Core::Symbols {
+
+using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
+Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
+
+} // namespace Core::Symbols