diff --git a/src/xenia/cpu/backend/a64/a64_code_cache.cc b/src/xenia/cpu/backend/a64/a64_code_cache.cc index f484967ac..72d80aa47 100644 --- a/src/xenia/cpu/backend/a64/a64_code_cache.cc +++ b/src/xenia/cpu/backend/a64/a64_code_cache.cc @@ -241,12 +241,25 @@ void A64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code, } while (generated_code_commit_mark_.compare_exchange_weak( old_commit_mark, new_commit_mark)); - // Copy code. - std::memcpy(code_write_address, machine_code, func_info.code_size.total); - - // Fill unused slots with 0x00 - std::memset(tail_write_address, 0x00, - static_cast(end_write_address - tail_write_address)); + // Copy code and fill padding while in write mode on MAP_JIT. +#if XE_PLATFORM_MAC && defined(__aarch64__) + const bool jit_write = + (generated_code_execute_base_ == generated_code_write_base_); + if (jit_write) { + pthread_jit_write_protect_np(0); + } +#endif + CopyMachineCode(code_write_address, machine_code, + func_info.code_size.total); + if (end_write_address > tail_write_address) { + std::memset(tail_write_address, 0x00, + static_cast(end_write_address - tail_write_address)); + } +#if XE_PLATFORM_MAC && defined(__aarch64__) + if (jit_write) { + pthread_jit_write_protect_np(1); + } +#endif // Notify subclasses of placed code. PlaceCode(guest_address, machine_code, func_info, code_execute_address, diff --git a/src/xenia/cpu/backend/a64/a64_code_cache.h b/src/xenia/cpu/backend/a64/a64_code_cache.h index 2bc9ed59f..0ff678c9a 100644 --- a/src/xenia/cpu/backend/a64/a64_code_cache.h +++ b/src/xenia/cpu/backend/a64/a64_code_cache.h @@ -111,6 +111,11 @@ class A64CodeCache : public CodeCache { void* code_execute_address, UnwindReservation unwind_reservation) {} + // Platform-specific code copying with JIT protection handling + virtual void CopyMachineCode(void* dest, const void* src, size_t size) { + std::memcpy(dest, src, size); + } + std::filesystem::path file_name_; xe::memory::FileMappingHandle mapping_ = xe::memory::kFileMappingHandleInvalid; diff --git a/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc b/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc new file mode 100644 index 000000000..c2c8b5df7 --- /dev/null +++ b/src/xenia/cpu/backend/a64/a64_code_cache_posix.cc @@ -0,0 +1,201 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2025 Ben Vanik. All rights reserved. + * Released under the BSD license - see LICENSE in the root for more details. + ****************************************************************************** + */ + +#include "xenia/cpu/backend/a64/a64_code_cache.h" + +#include +#include +#include +#include + +#ifdef XE_PLATFORM_MAC +#include +#include +#endif + +#include "xenia/base/assert.h" +#include "xenia/base/clock.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/memory.h" +#include "xenia/cpu/function.h" + +namespace xe { +namespace cpu { +namespace backend { +namespace a64 { + +// ARM64 unwind-op codes for POSIX (simplified) +typedef enum _UNWIND_OP_CODES_POSIX { + UWOP_POSIX_NOP = 0x00, + UWOP_POSIX_ALLOC_STACK = 0x01, + UWOP_POSIX_SAVE_FP_LR = 0x02, + UWOP_POSIX_SET_FP = 0x03, + UWOP_POSIX_END = 0xFF, +} UNWIND_CODE_OPS_POSIX; + +using UNWIND_CODE_POSIX = uint8_t; + +// Size of unwind info per function. +static const size_t kUnwindInfoSize = 16; + +class PosixA64CodeCache : public A64CodeCache { + public: + PosixA64CodeCache(); + ~PosixA64CodeCache() override; + + bool Initialize() override; + + void* LookupUnwindInfo(uint64_t host_pc) override; + + protected: + void CopyMachineCode(void* dest, const void* src, size_t size) override; + + private: + struct UnwindInfo { + uint64_t begin_address; + uint64_t end_address; + // Additional unwind information can be added here + }; + + UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override; + void PlaceCode(uint32_t guest_address, void* machine_code, + const EmitFunctionInfo& func_info, void* code_execute_address, + UnwindReservation unwind_reservation) override; + + void InitializeUnwindEntry(uint8_t* unwind_entry_address, + size_t unwind_table_slot, + void* code_execute_address, + const EmitFunctionInfo& func_info); + + // Unwind table entries. + std::vector unwind_table_; + // Current number of entries in the table. + std::atomic unwind_table_count_ = {0}; +}; + +std::unique_ptr A64CodeCache::Create() { + return std::make_unique(); +} + +PosixA64CodeCache::PosixA64CodeCache() = default; + +PosixA64CodeCache::~PosixA64CodeCache() { + // Cleanup if necessary +} + +bool PosixA64CodeCache::Initialize() { + if (!A64CodeCache::Initialize()) { + return false; + } + + // Resize (not reserve) space for unwind table entries to ensure vector has + // actual elements + unwind_table_.resize(kMaximumFunctionCount); + + // Additional POSIX-specific initialization can be done here + + return true; +} + +void PosixA64CodeCache::CopyMachineCode(void* dest, const void* src, + size_t size) { + std::memcpy(dest, src, size); +} + +PosixA64CodeCache::UnwindReservation +PosixA64CodeCache::RequestUnwindReservation(uint8_t* entry_address) { + uint32_t current_count = unwind_table_count_.fetch_add(1); + assert_false(current_count >= kMaximumFunctionCount); + UnwindReservation unwind_reservation; + unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16); + unwind_reservation.table_slot = current_count; + unwind_reservation.entry_address = entry_address; + return unwind_reservation; +} + +void PosixA64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code, + const EmitFunctionInfo& func_info, + void* code_execute_address, + UnwindReservation unwind_reservation) { + // Add unwind info. + InitializeUnwindEntry( + reinterpret_cast(unwind_reservation.entry_address), + unwind_reservation.table_slot, code_execute_address, func_info); + + // Add entry to unwind table at the reserved slot only + UnwindInfo unwind_info; + unwind_info.begin_address = reinterpret_cast(code_execute_address); + unwind_info.end_address = + unwind_info.begin_address + func_info.code_size.total; + + // Store in the reserved slot + unwind_table_[unwind_reservation.table_slot] = unwind_info; + + // Validate address alignment before cache flushing + if ((uintptr_t)code_execute_address % 4 != 0) { + XELOGW( + "PosixA64CodeCache::PlaceCode: WARNING - code address 0x{:016X} is not " + "4-byte aligned", + (uintptr_t)code_execute_address); + } + + if (func_info.code_size.total % 4 != 0) { + XELOGW( + "PosixA64CodeCache::PlaceCode: WARNING - code size {} is not 4-byte " + "aligned", + func_info.code_size.total); + } + + // Flush instruction cache +#ifdef XE_PLATFORM_MAC + // On macOS, use sys_icache_invalidate + sys_icache_invalidate(code_execute_address, func_info.code_size.total); +#else + // On Linux and other POSIX systems, use GCC builtin + __builtin___clear_cache( + static_cast(code_execute_address), + static_cast(code_execute_address) + func_info.code_size.total); +#endif +} + +void PosixA64CodeCache::InitializeUnwindEntry( + uint8_t* unwind_entry_address, size_t unwind_table_slot, + void* code_execute_address, const EmitFunctionInfo& func_info) { + // Initialize unwind information for POSIX (simplified example) + // In practice, you would populate this with proper unwind info + // based on the function prologue and epilogue. + + // NOTE: Unwind info is already stored in PlaceCode, so we don't store it + // again here to avoid the double-storage bug that was causing memory + // corruption. +} + +void* PosixA64CodeCache::LookupUnwindInfo(uint64_t host_pc) { + // Binary search the unwind table for the given program counter + size_t left = 0; + size_t right = unwind_table_count_.load(); + while (left < right) { + size_t mid = left + (right - left) / 2; + const UnwindInfo& info = unwind_table_[mid]; + if (host_pc < info.begin_address) { + right = mid; + } else if (host_pc >= info.end_address) { + left = mid + 1; + } else { + return &unwind_table_[mid]; + } + } + return nullptr; +} + +} // namespace a64 +} // namespace backend +} // namespace cpu +} // namespace xe