diff options
Diffstat (limited to 'src/core/arm')
-rw-r--r-- | src/core/arm/arm_interface.cpp | 141 | ||||
-rw-r--r-- | src/core/arm/arm_interface.h | 3 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.cpp | 89 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_32.h | 4 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.cpp | 90 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic_64.h | 4 | ||||
-rw-r--r-- | src/core/arm/symbols.cpp | 190 | ||||
-rw-r--r-- | src/core/arm/symbols.h | 27 |
8 files changed, 330 insertions, 218 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0951e1976..08bf1201d 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/arm_interface.h b/src/core/arm/arm_interface.h index c60322442..dce2f4195 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -171,6 +171,9 @@ public: /// Prepare core for thread reschedule (if needed to correctly handle state) virtual void PrepareReschedule() = 0; + /// Signal an interrupt and ask the core to halt as soon as possible. + virtual void SignalInterrupt() = 0; + struct BacktraceEntry { std::string module; u64 address; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 286976623..ab3210d84 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -25,6 +25,9 @@ namespace Core { using namespace Common::Literals; +constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; +constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) @@ -70,11 +73,13 @@ public: } void InterpreterFallback(u32 pc, std::size_t num_instructions) override { + parent.LogBacktrace(); UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, MemoryReadCode(pc)); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { + parent.LogBacktrace(); LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); @@ -82,15 +87,13 @@ public: } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit->HaltExecution(svc_call); } void AddTicks(u64 ticks) override { - if (parent.uses_wall_clock) { - return; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off // if not all cores are doing a similar amount of work. Instead of doing this, we should @@ -106,12 +109,8 @@ public: } u64 GetTicksRemaining() override { - if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return minimum_run_cycles; - } - return 0U; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -146,6 +145,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = !uses_wall_clock; // Code cache size config.code_cache_size = 512_MiB; @@ -186,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } - } + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } + } - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; - } - if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; - } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - } - if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_unique<Dynarmic::A32::Jit>(config); @@ -222,13 +228,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* void ARM_Dynarmic_32::Run() { while (true) { - jit->Run(); - if (!svc_called) { - break; + const auto hr = jit->Run(); + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, svc_swi); } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { + if (Has(hr, break_loop)) { break; } } @@ -314,8 +318,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { } void ARM_Dynarmic_32::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; + jit->HaltExecution(break_loop); +} + +void ARM_Dynarmic_32::SignalInterrupt() { + jit->HaltExecution(break_loop); } void ARM_Dynarmic_32::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 5d47b600d..3f68a4ff1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -57,6 +57,7 @@ public: void LoadContext(const ThreadContext64& ctx) override {} void PrepareReschedule() override; + void SignalInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -83,9 +84,6 @@ private: // SVC callback u32 svc_swi{}; - bool svc_called{}; - - bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 24107f9f6..68822a1fc 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -26,6 +26,9 @@ namespace Core { using Vector = Dynarmic::A64::Vector; using namespace Common::Literals; +constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; +constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) @@ -81,6 +84,7 @@ public: } void InterpreterFallback(u64 pc, std::size_t num_instructions) override { + parent.LogBacktrace(); LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, MemoryReadCode(pc)); @@ -105,7 +109,7 @@ public: break; } - parent.jit->HaltExecution(); + parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); } void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { @@ -118,21 +122,19 @@ public: return; case Dynarmic::A64::Exception::Breakpoint: default: + parent.LogBacktrace(); ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); } } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit->HaltExecution(svc_call); } void AddTicks(u64 ticks) override { - if (parent.uses_wall_clock) { - return; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off @@ -147,12 +149,8 @@ public: } u64 GetTicksRemaining() override { - if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return minimum_run_cycles; - } - return 0U; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -208,6 +206,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = !uses_wall_clock; // Code cache size config.code_cache_size = 512_MiB; @@ -248,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } - } + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_fastmem_check) { + config.fastmem_address_space_bits = 64; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } + } - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; - } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - } - if (Settings::values.cpuopt_unsafe_fastmem_check) { config.fastmem_address_space_bits = 64; - } - if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - config.fastmem_address_space_bits = 64; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_shared<Dynarmic::A64::Jit>(config); @@ -284,13 +289,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* void ARM_Dynarmic_64::Run() { while (true) { - jit->Run(); - if (!svc_called) { - break; + const auto hr = jit->Run(); + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, svc_swi); } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { + if (Has(hr, break_loop)) { break; } } @@ -381,8 +384,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { } void ARM_Dynarmic_64::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; + jit->HaltExecution(break_loop); +} + +void ARM_Dynarmic_64::SignalInterrupt() { + jit->HaltExecution(break_loop); } void ARM_Dynarmic_64::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 0c4e46c64..58bc7fbec 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -51,6 +51,7 @@ public: void LoadContext(const ThreadContext64& ctx) override; void PrepareReschedule() override; + void SignalInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -77,9 +78,6 @@ private: // SVC callback u32 svc_swi{}; - bool svc_called{}; - - bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp new file mode 100644 index 000000000..26c44f0c7 --- /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 000000000..99e6a9f8e --- /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 |