mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[A64/CodeCache] Add POSIX code cache and JIT-safe copy hook
- Add CopyMachineCode virtual to centralize code writes - Use MAP_JIT write-protect toggles around JIT writes on macOS ARM64 - Add POSIX A64 code cache with unwind table + icache flush
This commit is contained in:
@@ -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<size_t>(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<size_t>(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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef XE_PLATFORM_MAC
|
||||
#include <libkern/OSCacheControl.h>
|
||||
#include <pthread.h>
|
||||
#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<UnwindInfo> unwind_table_;
|
||||
// Current number of entries in the table.
|
||||
std::atomic<uint32_t> unwind_table_count_ = {0};
|
||||
};
|
||||
|
||||
std::unique_ptr<A64CodeCache> A64CodeCache::Create() {
|
||||
return std::make_unique<PosixA64CodeCache>();
|
||||
}
|
||||
|
||||
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<uint8_t*>(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<uintptr_t>(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<char*>(code_execute_address),
|
||||
static_cast<char*>(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
|
||||
Reference in New Issue
Block a user