[jit] fix Super Mario 64 in SM3D: All-Stars (#3950)

jit service had wrong check for module versions
missing handlers for some funcs
the page cache i added interfered with jit (gee who would've tought)

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3950
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
lizzie 2026-05-13 19:02:33 +02:00 committed by crueter
parent 7e84f9ef59
commit b89cd6903c
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
7 changed files with 198 additions and 213 deletions

View file

@ -36,7 +36,7 @@ public:
u64 MemoryRead64(u32 vaddr) override; u64 MemoryRead64(u32 vaddr) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override; std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = 0; //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite8(u32 vaddr, u8 value) override; void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override; void MemoryWrite16(u32 vaddr, u16 value) override;
@ -54,7 +54,7 @@ public:
void ReturnException(u32 pc, Dynarmic::HaltReason hr); void ReturnException(u32 pc, Dynarmic::HaltReason hr);
// //
Dynarmic::CodePage cached_code_page; Dynarmic::CodePage cached_code_page;
u64 last_code_addr = 0; u64 last_code_addr = u64(-1);
ArmDynarmic32& m_parent; ArmDynarmic32& m_parent;
Core::Memory::Memory& m_memory; Core::Memory::Memory& m_memory;
Kernel::KProcess* m_process{}; Kernel::KProcess* m_process{};

View file

@ -45,7 +45,6 @@ Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) { std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
return std::nullopt; return std::nullopt;
// return m_memory.Read32(vaddr);
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK; auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
if (last_code_addr != aligned_vaddr) { if (last_code_addr != aligned_vaddr) {
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page)); m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
@ -103,6 +102,7 @@ bool DynarmicCallbacks64::MemoryWriteExclusive128(u64 vaddr, Dynarmic::A64::Vect
} }
void DynarmicCallbacks64::InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, u64 value) { void DynarmicCallbacks64::InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op, u64 value) {
last_code_addr = u64(-1); //invalidate cached page
switch (op) { switch (op) {
case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: { case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
static constexpr u64 ICACHE_LINE_SIZE = 64; static constexpr u64 ICACHE_LINE_SIZE = 64;
@ -128,7 +128,7 @@ void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception excep
case Dynarmic::A64::Exception::SendEvent: case Dynarmic::A64::Exception::SendEvent:
case Dynarmic::A64::Exception::SendEventLocal: case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield: case Dynarmic::A64::Exception::Yield:
LOG_TRACE(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, m_memory.Read32(pc)); LOG_TRACE(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, cached = {:08X})", std::size_t(exception), pc, m_memory.Read32(pc), MemoryReadCode(pc).value_or(0));
return; return;
case Dynarmic::A64::Exception::NoExecuteFault: case Dynarmic::A64::Exception::NoExecuteFault:
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc); LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);

View file

@ -43,7 +43,7 @@ public:
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override; Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override; std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = 0; //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite8(u64 vaddr, u8 value) override; void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override; void MemoryWrite16(u64 vaddr, u16 value) override;
@ -65,7 +65,7 @@ public:
void ReturnException(u64 pc, Dynarmic::HaltReason hr); void ReturnException(u64 pc, Dynarmic::HaltReason hr);
Dynarmic::CodePage cached_code_page; Dynarmic::CodePage cached_code_page;
u64 last_code_addr = 0; u64 last_code_addr = u64(-1);
ArmDynarmic64& m_parent; ArmDynarmic64& m_parent;
Core::Memory::Memory& m_memory; Core::Memory::Memory& m_memory;
u64 m_tpidrro_el0{}; u64 m_tpidrro_el0{};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -13,117 +16,111 @@ using namespace Common::ELF;
namespace Core { namespace Core {
namespace Symbols { namespace Symbols {
template <typename Word, typename ELFSymbol, typename ByteReader> struct ModuleHeaderLocation {
static Symbols GetSymbols(ByteReader ReadBytes) { u32 version;
const auto Read8{[&](u64 index) { u32 header_offset;
u32 version_offset;
};
static_assert(sizeof(ModuleHeaderLocation) == 0x0C);
struct ModuleHeader {
u32 signature;
u32 dynamic_offset;
u32 bss_start_offset;
u32 bss_end_offset;
u32 exception_info_start_offset;
u32 exception_info_end_offset;
u32 module_offset;
u32 relro_start_offset;
u32 full_relro_end_offset;
u32 nx_debug_link_start_offset;
u32 nx_debug_link_end_offset;
u32 note_gnu_build_id_start_offset;
u32 note_gnu_build_id_end_offset;
};
static_assert(sizeof(ModuleHeader) == 0x34);
struct Mod32 {
using Sym = Elf32_Sym;
using Dyn = Elf32_Dyn;
};
struct Mod64 {
using Sym = Elf64_Sym;
using Dyn = Elf64_Dyn;
};
template <typename M, typename F>
static Symbols GetSymbols(F&& ReadBytes) {
const auto Read8 = [&](u64 index) {
u8 ret; u8 ret;
ReadBytes(&ret, index, sizeof(u8)); ReadBytes(&ret, index, sizeof(u8));
return ret; return ret;
}}; };
ModuleHeaderLocation loc{};
const auto Read32{[&](u64 index) { ReadBytes(&loc, 0, sizeof(ModuleHeaderLocation));
u32 ret; ModuleHeader hdr;
ReadBytes(&ret, index, sizeof(u32)); ReadBytes(&hdr, loc.header_offset, sizeof(ModuleHeader));
return ret; if (hdr.signature != Common::MakeMagic('M', 'O', 'D', '0')) {
}};
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 {}; return {};
} }
VAddr string_table_offset{}; VAddr strtab_offs{};
VAddr symbol_table_offset{}; VAddr symtab_offs{};
u64 symbol_entry_size{}; u64 syment_size = sizeof(typename M::Sym);
VAddr dynamic_offset = loc.header_offset + hdr.dynamic_offset;
const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
VAddr dynamic_index = dynamic_offset;
while (true) { while (true) {
const Word tag = ReadWord(dynamic_index); typename M::Dyn dyn;
const Word value = ReadWord(dynamic_index + sizeof(Word)); ReadBytes(&dyn, dynamic_offset, sizeof(typename M::Dyn));
dynamic_index += 2 * sizeof(Word); dynamic_offset += sizeof(typename M::Dyn);
if (dyn.d_tag == ElfDtNull) {
if (tag == ElfDtNull) {
break; break;
} }
if (dyn.d_tag == ElfDtStrtab) {
if (tag == ElfDtStrtab) { strtab_offs = dyn.d_un.d_ptr;
string_table_offset = value; } else if (dyn.d_tag == ElfDtSymtab) {
} else if (tag == ElfDtSymtab) { symtab_offs = dyn.d_un.d_ptr;
symbol_table_offset = value; } else if (dyn.d_tag == ElfDtSyment) {
} else if (tag == ElfDtSyment) { syment_size = dyn.d_un.d_val;
symbol_entry_size = value;
} }
} }
if (strtab_offs > 0 && symtab_offs > 0) {
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { Symbols out;
return {}; VAddr symbol_index = symtab_offs;
} while (symbol_index < strtab_offs) {
typename M::Sym symbol{};
Symbols out; ReadBytes(&symbol, symbol_index, sizeof(typename M::Sym));
VAddr offs = strtab_offs + symbol.st_name;
VAddr symbol_index = symbol_table_offset; std::string name{};
while (symbol_index < string_table_offset) { for (u8 c = Read8(offs); c != 0; c = Read8(++offs))
ELFSymbol symbol{}; name += char(c);
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); symbol_index += syment_size;
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
VAddr string_offset = string_table_offset + symbol.st_name;
std::string name;
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
name += static_cast<char>(c);
} }
return out;
symbol_index += symbol_entry_size;
out[name] = std::make_pair(symbol.st_value, symbol.st_size);
} }
return {};
return out;
} }
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) { Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
const auto ReadBytes{ const auto f = [base, &memory](void* ptr, size_t offset, size_t size) {
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; memory.ReadBlock(base + offset, ptr, size);
};
if (is_64) { return is_64 ? GetSymbols<Mod64>(f) : GetSymbols<Mod32>(f);
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
} }
Symbols GetSymbols(std::span<const u8> data, bool is_64) { Symbols GetSymbols(std::span<const u8> data, bool is_64) {
const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) { const auto f = [data](void* ptr, size_t offset, size_t size) {
std::memcpy(ptr, data.data() + offset, size); std::memcpy(ptr, data.data() + offset, size);
}}; };
return is_64 ? GetSymbols<Mod64>(f) : GetSymbols<Mod32>(f);
if (is_64) {
return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
} }
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) { 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 it = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& e) {
const auto& [name, sym_info] = pair; auto const [start, size] = e.second;
const auto& [start_address, size] = sym_info; auto const end = start + size;
const auto end_address = start_address + size; return addr >= start && addr < end;
return addr >= start_address && addr < end_address;
}); });
return it != symbols.cend() ? std::optional<std::string>{it->first} : std::nullopt;
if (iter == symbols.cend()) {
return std::nullopt;
}
return iter->first;
} }
} // namespace Symbols } // namespace Symbols

View file

@ -33,8 +33,7 @@ IScreenShotApplicationService::IScreenShotApplicationService(
IScreenShotApplicationService::~IScreenShotApplicationService() = default; IScreenShotApplicationService::~IScreenShotApplicationService() = default;
Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, ClientAppletResourceUserId aruid) {
ClientAppletResourceUserId aruid) {
LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
library_version, aruid.pid); library_version, aruid.pid);
R_SUCCEED(); R_SUCCEED();

View file

@ -1,6 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging.h"
#include "core/arm/debug.h" #include "core/arm/debug.h"
#include "core/arm/symbols.h" #include "core/arm/symbols.h"
#include "core/core.h" #include "core/core.h"
@ -111,10 +115,7 @@ public:
const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())}; const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())}; const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, const u64 wrapper_value = context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, command, input_ptr, in_data.size(), output_ptr, out_data.size());
command, input_ptr, in_data.size(), output_ptr,
out_data.size())};
*out_return_value = context.GetHeap<s32>(ret_ptr); *out_return_value = context.GetHeap<s32>(ret_ptr);
context.GetHeap(output_ptr, out_data.data(), out_data.size()); context.GetHeap(output_ptr, out_data.data(), out_data.size());
@ -126,9 +127,7 @@ public:
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem, Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem, InBuffer<BufferAttr_HipcMapAlias> nrr, InBuffer<BufferAttr_HipcMapAlias> nro) {
InBuffer<BufferAttr_HipcMapAlias> nrr,
InBuffer<BufferAttr_HipcMapAlias> nro) {
if (!tmem) { if (!tmem) {
LOG_ERROR(Service_JIT, "Invalid transfer memory handle!"); LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
@ -139,9 +138,10 @@ public:
configuration.transfer_memory.size = tmem_size; configuration.transfer_memory.size = tmem_size;
// Gather up all the callbacks from the loaded plugin // Gather up all the callbacks from the loaded plugin
auto symbols{Core::Symbols::GetSymbols(nro, true)}; auto symbols = Core::Symbols::GetSymbols(nro, true);
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; const auto GetSymbol = [&](const std::string& name) {
return symbols[name].first;
};
callbacks.rtld_fini = GetSymbol("_fini"); callbacks.rtld_fini = GetSymbol("_fini");
callbacks.rtld_init = GetSymbol("_init"); callbacks.rtld_init = GetSymbol("_init");
callbacks.Control = GetSymbol("nnjitpluginControl"); callbacks.Control = GetSymbol("nnjitpluginControl");
@ -153,8 +153,7 @@ public:
callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared"); callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared");
callbacks.Keeper = GetSymbol("nnjitpluginKeeper"); callbacks.Keeper = GetSymbol("nnjitpluginKeeper");
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0 || callbacks.Control == 0) {
callbacks.OnPrepared == 0) {
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@ -164,12 +163,9 @@ public:
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
context.MapProcessMemory(configuration.sys_ro_memory.offset, context.MapProcessMemory(configuration.sys_ro_memory.offset, configuration.sys_ro_memory.size);
configuration.sys_ro_memory.size); context.MapProcessMemory(configuration.sys_rx_memory.offset, configuration.sys_rx_memory.size);
context.MapProcessMemory(configuration.sys_rx_memory.offset, context.MapProcessMemory(configuration.transfer_memory.offset, configuration.transfer_memory.size);
configuration.sys_rx_memory.size);
context.MapProcessMemory(configuration.transfer_memory.offset,
configuration.transfer_memory.size);
// Run ELF constructors, if needed // Run ELF constructors, if needed
if (callbacks.rtld_init != 0) { if (callbacks.rtld_init != 0) {
@ -179,7 +175,7 @@ public:
// Function prototype: // Function prototype:
// u64 GetVersion(); // u64 GetVersion();
const auto version{context.CallFunction(callbacks.GetVersion)}; const auto version{context.CallFunction(callbacks.GetVersion)};
if (version != 1) { if (version > 1) {
LOG_ERROR(Service_JIT, "unknown plugin version {}", version); LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }

View file

@ -24,15 +24,25 @@ using namespace Common::ELF;
namespace Service::JIT { namespace Service::JIT {
enum HelperFn {
None,
Stop,
Resolve,
Panic,
Memcpy,
Memmove,
Memset,
// sm64
PanicForPlugin,
AbortImpl,
UnexpectedImpl,
Count
};
constexpr std::array<u8, 8> SVC0_ARM64 = { constexpr std::array<u8, 8> SVC0_ARM64 = {
0x01, 0x00, 0x00, 0xd4, // svc #0 0x01, 0x00, 0x00, 0xd4, // svc #0
0xc0, 0x03, 0x5f, 0xd6, // ret 0xc0, 0x03, 0x5f, 0xd6, // ret
}; };
constexpr std::array HELPER_FUNCTIONS{
"_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
};
constexpr size_t STACK_ALIGN = 16; constexpr size_t STACK_ALIGN = 16;
class JITContextImpl; class JITContextImpl;
@ -42,10 +52,12 @@ using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public: public:
explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, IntervalSet& mapped_ranges_, JITContextImpl& parent_)
IntervalSet& mapped_ranges_, JITContextImpl& parent_) : memory{memory_}
: memory{memory_}, local_memory{local_memory_}, , local_memory{local_memory_}
mapped_ranges{mapped_ranges_}, parent{parent_} {} , mapped_ranges{mapped_ranges_}
, parent{parent_}
{}
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE); static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
@ -57,7 +69,7 @@ public:
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
} }
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = 0; //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
u8 MemoryRead8(u64 vaddr) override { u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr); return ReadMemory<u8>(vaddr);
@ -75,13 +87,10 @@ public:
return ReadMemory<u128>(vaddr); return ReadMemory<u128>(vaddr);
} }
std::string MemoryReadCString(u64 vaddr) { std::string MemoryReadCString(u64 vaddr) {
std::string result; std::string result{};
u8 next; u8 next;
while ((next = MemoryRead8(vaddr++)) != 0)
while ((next = MemoryRead8(vaddr++)) != 0) { result += char(next);
result += next;
}
return result; return result;
} }
@ -157,32 +166,26 @@ private:
std::vector<u8>& local_memory; std::vector<u8>& local_memory;
IntervalSet& mapped_ranges; IntervalSet& mapped_ranges;
JITContextImpl& parent; JITContextImpl& parent;
Dynarmic::CodePage cached_code_page; Dynarmic::CodePage cached_code_page;
u64 last_code_addr = 0; u64 last_code_addr = u64(-1);
}; };
class JITContextImpl { class JITContextImpl {
public: public:
explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} { explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
callbacks = callbacks.emplace(memory, local_memory, mapped_ranges, *this);
std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this); user_config.callbacks = std::addressof(callbacks.value());
user_config.callbacks = callbacks.get(); jit.emplace(user_config);
jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
} }
bool LoadNRO(std::span<const u8> data) { bool LoadNRO(std::span<const u8> data) {
local_memory.clear();
relocbase = local_memory.size(); relocbase = local_memory.size();
local_memory.insert(local_memory.end(), data.begin(), data.end()); local_memory.insert(local_memory.end(), data.begin(), data.end());
if (FixupRelocations()) { if (FixupRelocations()) {
InsertHelperFunctions(); InsertHelperFunctions();
InsertStack(); InsertStack();
return true; return true;
} }
return false; return false;
} }
@ -190,11 +193,9 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run. // The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have // Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves. // the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead32(4)}; const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
return false; return false;
}
// For more info about dynamic entries, see the ELF ABI specification: // For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
@ -205,40 +206,33 @@ public:
while (true) { while (true) {
const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
dynamic_offset += sizeof(Elf64_Dyn); dynamic_offset += sizeof(Elf64_Dyn);
if (!dyn.d_tag) { if (!dyn.d_tag) {
break; break;
} } else if (dyn.d_tag == ElfDtRela) {
if (dyn.d_tag == ElfDtRela) {
rela_dyn = dyn.d_un.d_ptr; rela_dyn = dyn.d_un.d_ptr;
} } else if (dyn.d_tag == ElfDtRelasz) {
if (dyn.d_tag == ElfDtRelasz) {
num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
} } else if (dyn.d_tag == ElfDtRelr) {
if (dyn.d_tag == ElfDtRelr) {
relr_dyn = dyn.d_un.d_ptr; relr_dyn = dyn.d_un.d_ptr;
} } else if (dyn.d_tag == ElfDtRelrsz) {
if (dyn.d_tag == ElfDtRelrsz) {
num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr); num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr);
} }
} }
for (size_t i = 0; i < num_rela; i++) { for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) != ElfAArch64Relative) { if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
continue; const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
} }
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
} }
VAddr relr_where = 0; VAddr relr_where = 0;
for (size_t i = 0; i < num_relr; i++) { for (size_t i = 0; i < num_relr; i++) {
const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))}; const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr{[&](VAddr where) { const auto incr = [&](VAddr where) {
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
}}; };
if ((relr & 1) == 0) { if ((relr & 1) == 0) {
// where pointer // where pointer
relr_where = relocbase + relr; relr_where = relocbase + relr;
@ -254,13 +248,12 @@ public:
relr_where += 63 * sizeof(Elf64_Addr); relr_where += 63 * sizeof(Elf64_Addr);
} }
} }
return true; return true;
} }
void InsertHelperFunctions() { void InsertHelperFunctions() {
for (const auto& name : HELPER_FUNCTIONS) { for (size_t i = 0; i < size_t(HelperFn::Count); ++i) {
helpers[name] = local_memory.size(); helpers[i] = local_memory.size();
local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end()); local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end());
} }
} }
@ -268,9 +261,8 @@ public:
void InsertStack() { void InsertStack() {
// Allocate enough space to avoid any reasonable risk of // Allocate enough space to avoid any reasonable risk of
// overflowing the stack during plugin execution // overflowing the stack during plugin execution
const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) - const u64 pad_amount = Common::AlignUp(local_memory.size(), STACK_ALIGN) - local_memory.size();
local_memory.size()}; local_memory.insert(local_memory.end(), (4096 * 32) + pad_amount, 0);
local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
top_of_stack = local_memory.size(); top_of_stack = local_memory.size();
heap_pointer = top_of_stack; heap_pointer = top_of_stack;
} }
@ -297,27 +289,21 @@ public:
// //
// For more info, see the AArch64 ABI PCS: // For more info, see the AArch64 ABI PCS:
// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
for (size_t i = 0; i < 8 && i < argument_stack.size(); i++)
for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
jit->SetRegister(i, argument_stack[i]); jit->SetRegister(i, argument_stack[i]);
}
if (argument_stack.size() > 8) { if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown( const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); for (size_t i = 8; i < argument_stack.size(); i++)
for (size_t i = 8; i < argument_stack.size(); i++) {
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
}
jit->SetSP(new_sp); jit->SetSP(new_sp);
} }
// Reset the call state for the next invocation // Reset the call state for the next invocation
argument_stack.clear(); argument_stack.clear();
heap_pointer = top_of_stack; heap_pointer = top_of_stack;
} }
u64 CallFunction(VAddr func) { u64 CallFunction(VAddr func) {
jit->SetRegister(30, helpers["_stop"]); jit->SetRegister(30, helpers[size_t(HelperFn::Stop)]);
jit->SetSP(top_of_stack); jit->SetSP(top_of_stack);
SetupArguments(); SetupArguments();
@ -326,21 +312,14 @@ public:
return jit->GetRegister(0); return jit->GetRegister(0);
} }
VAddr GetHelper(const std::string& name) {
return helpers[name];
}
VAddr AddHeap(const void* data, size_t size) { VAddr AddHeap(const void* data, size_t size) {
// Require all heap data types to have the same alignment as the // Require all heap data types to have the same alignment as the
// stack pointer, for compatibility // stack pointer, for compatibility
const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)}; const size_t num_bytes = Common::AlignUp(size, STACK_ALIGN);
// Make additional memory space if required // Make additional memory space if required
if (heap_pointer + num_bytes > local_memory.size()) { if (heap_pointer + num_bytes > local_memory.size()) {
local_memory.insert(local_memory.end(), local_memory.insert(local_memory.end(), (heap_pointer + num_bytes) - local_memory.size(), 0);
(heap_pointer + num_bytes) - local_memory.size(), 0);
} }
const VAddr location{heap_pointer}; const VAddr location{heap_pointer};
std::memcpy(local_memory.data() + location, data, size); std::memcpy(local_memory.data() + location, data, size);
heap_pointer += num_bytes; heap_pointer += num_bytes;
@ -351,13 +330,29 @@ public:
std::memcpy(data, local_memory.data() + location, size); std::memcpy(data, local_memory.data() + location, size);
} }
std::unique_ptr<DynarmicCallbacks64> callbacks; VAddr GetHelper(const std::string& name) {
if (name == "_resolve") return helpers[HelperFn::Resolve];
else if (name == "_panic") return helpers[HelperFn::Panic];
else if (name == "_stop") return helpers[HelperFn::Stop];
else if (name == "memset") return helpers[HelperFn::Memset];
else if (name == "memcpy") return helpers[HelperFn::Memcpy];
else if (name == "memmove") return helpers[HelperFn::Memmove];
else if (name == "PanicForPlugin") return helpers[HelperFn::PanicForPlugin];
else if (name == "_ZN2nn4diag6detail9AbortImplEPKcS3_S3_i") return helpers[HelperFn::AbortImpl];
else if (name == "_ZN2nn6detail21UnexpectedDefaultImplEPKcS2_i") return helpers[HelperFn::UnexpectedImpl];
else {
LOG_CRITICAL(Service_JIT, "unresolved {}", name);
return helpers[HelperFn::Panic];
}
}
std::optional<DynarmicCallbacks64> callbacks;
std::optional<Dynarmic::A64::Jit> jit;
std::vector<u8> local_memory; std::vector<u8> local_memory;
std::vector<u64> argument_stack; std::vector<VAddr> argument_stack;
IntervalSet mapped_ranges; IntervalSet mapped_ranges;
Dynarmic::A64::UserConfig user_config; Dynarmic::A64::UserConfig user_config;
std::unique_ptr<Dynarmic::A64::Jit> jit; std::array<VAddr, size_t(HelperFn::Count)> helpers;
std::map<std::string, VAddr, std::less<>> helpers;
Core::Memory::Memory& memory; Core::Memory::Memory& memory;
VAddr top_of_stack; VAddr top_of_stack;
VAddr heap_pointer; VAddr heap_pointer;
@ -383,44 +378,41 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
} }
u64 pc{parent.jit->GetPC() - 4}; u64 pc{parent.jit->GetPC() - 4};
auto& helpers{parent.helpers}; if (pc == parent.helpers[size_t(HelperFn::Memcpy)] || pc == parent.helpers[size_t(HelperFn::Memmove)]) {
if (pc == helpers["memcpy"] || pc == helpers["memmove"]) {
const VAddr dest{parent.jit->GetRegister(0)}; const VAddr dest{parent.jit->GetRegister(0)};
const VAddr src{parent.jit->GetRegister(1)}; const VAddr src{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)}; const size_t n{parent.jit->GetRegister(2)};
if (dest < src) { if (dest < src) {
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, MemoryRead8(src + i)); MemoryWrite8(dest + i, MemoryRead8(src + i));
}
} else { } else {
for (size_t i = n; i > 0; i--) { for (size_t i = n; i > 0; i--)
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
}
} }
} else if (pc == helpers["memset"]) { } else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)}; const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)}; const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)}; const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++)
for (size_t i = 0; i < n; i++) { MemoryWrite8(dest + i, u8(c));
MemoryWrite8(dest + i, static_cast<u8>(c)); } else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
}
} else if (pc == helpers["_resolve"]) {
// X0 contains a char* for a symbol to resolve // X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
const auto helper{helpers[name]}; parent.jit->SetRegister(0, u64(parent.GetHelper(name)));
} else if (pc == parent.helpers[size_t(HelperFn::Stop)]) {
if (helper != 0) {
parent.jit->SetRegister(0, helper);
} else {
LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
parent.jit->SetRegister(0, helpers["_panic"]);
}
} else if (pc == helpers["_stop"]) {
parent.jit->HaltExecution(); parent.jit->HaltExecution();
} else if (pc == helpers["_panic"]) { } else if (pc == parent.helpers[size_t(HelperFn::Panic)]) {
LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution();
// SM64
} else if (pc == parent.helpers[size_t(HelperFn::PanicForPlugin)]) {
LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution();
} else if (pc == parent.helpers[size_t(HelperFn::AbortImpl)]) {
LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution();
} else if (pc == parent.helpers[size_t(HelperFn::UnexpectedImpl)]) {
LOG_CRITICAL(Service_JIT, "plugin panicked!"); LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution(); parent.jit->HaltExecution();
} else { } else {
@ -430,7 +422,8 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
} }
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc); auto const inst = MemoryRead32(pc);
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution(); parent.jit->HaltExecution();
} }