parent
edfa3f3fc0
commit
352bae30cb
|
@ -42,7 +42,6 @@ X64Backend::~X64Backend() {
|
|||
processor()->memory()->SystemHeapFree(emitter_data_);
|
||||
emitter_data_ = 0;
|
||||
}
|
||||
delete code_cache_;
|
||||
}
|
||||
|
||||
bool X64Backend::Initialize() {
|
||||
|
@ -70,8 +69,8 @@ bool X64Backend::Initialize() {
|
|||
X64Emitter::XMM_COUNT,
|
||||
};
|
||||
|
||||
code_cache_ = new X64CodeCache();
|
||||
Backend::code_cache_ = code_cache_;
|
||||
code_cache_ = X64CodeCache::Create();
|
||||
Backend::code_cache_ = code_cache_.get();
|
||||
if (!code_cache_->Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/cpu/backend/backend.h"
|
||||
|
||||
DECLARE_bool(enable_haswell_instructions);
|
||||
|
@ -36,7 +38,7 @@ class X64Backend : public Backend {
|
|||
X64Backend(Processor* processor);
|
||||
~X64Backend() override;
|
||||
|
||||
X64CodeCache* code_cache() const { return code_cache_; }
|
||||
X64CodeCache* code_cache() const { return code_cache_.get(); }
|
||||
uint32_t emitter_data() const { return emitter_data_; }
|
||||
|
||||
// Call a generated function, saving all stack parameters.
|
||||
|
@ -55,7 +57,7 @@ class X64Backend : public Backend {
|
|||
std::unique_ptr<Assembler> CreateAssembler() override;
|
||||
|
||||
private:
|
||||
X64CodeCache* code_cache_;
|
||||
std::unique_ptr<X64CodeCache> code_cache_;
|
||||
|
||||
uint32_t emitter_data_;
|
||||
|
||||
|
|
|
@ -18,19 +18,11 @@
|
|||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
|
||||
// When enabled, this will use Windows 8 APIs to get unwind info.
|
||||
// TODO(benvanik): figure out why the callback variant doesn't work.
|
||||
#define USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// Size of unwind info per function.
|
||||
// TODO(benvanik): move this to emitter.
|
||||
const static uint32_t kUnwindInfoSize = 4 + (2 * 1 + 2 + 2);
|
||||
|
||||
X64CodeCache::X64CodeCache() = default;
|
||||
|
||||
X64CodeCache::~X64CodeCache() {
|
||||
|
@ -39,17 +31,6 @@ X64CodeCache::~X64CodeCache() {
|
|||
xe::memory::DeallocationType::kRelease);
|
||||
}
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
if (unwind_table_handle_) {
|
||||
RtlDeleteGrowableFunctionTable(unwind_table_handle_);
|
||||
}
|
||||
#else
|
||||
if (generated_code_base_) {
|
||||
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3));
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
// Unmap all views and close mapping.
|
||||
if (mapping_) {
|
||||
xe::memory::UnmapFileView(mapping_, generated_code_base_,
|
||||
|
@ -97,38 +78,6 @@ bool X64CodeCache::Initialize() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Compute total number of unwind entries we should allocate.
|
||||
// We don't support reallocing right now, so this should be high.
|
||||
unwind_table_.resize(30000);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Create table and register with the system. It's empty now, but we'll grow
|
||||
// it as functions are added.
|
||||
if (RtlAddGrowableFunctionTable(
|
||||
&unwind_table_handle_, unwind_table_.data(), unwind_table_count_,
|
||||
DWORD(unwind_table_.size()),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_ +
|
||||
kGeneratedCodeSize))) {
|
||||
XELOGE("Unable to create unwind function table");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Install a callback that the debugger will use to lookup unwind info on
|
||||
// demand.
|
||||
if (!RtlInstallFunctionTableCallback(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3,
|
||||
reinterpret_cast<DWORD64>(generated_code_base_),
|
||||
kGeneratedCodeSize, [](uintptr_t control_pc, void* context) {
|
||||
auto code_cache = reinterpret_cast<X64CodeCache*>(context);
|
||||
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
code_cache->LookupUnwindEntry(control_pc));
|
||||
}, this, nullptr)) {
|
||||
XELOGE("Unable to install function table callback");
|
||||
return false;
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -165,8 +114,7 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
|||
size_t low_mark;
|
||||
size_t high_mark;
|
||||
uint8_t* code_address = nullptr;
|
||||
uint8_t* unwind_entry_address = nullptr;
|
||||
size_t unwind_table_slot = 0;
|
||||
UnwindReservation unwind_reservation;
|
||||
{
|
||||
std::lock_guard<xe::mutex> allocation_lock(allocation_mutex_);
|
||||
|
||||
|
@ -180,9 +128,9 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
|||
// Reserve unwind info.
|
||||
// We go on the high size of the unwind info as we don't know how big we
|
||||
// need it, and a few extra bytes of padding isn't the worst thing.
|
||||
unwind_entry_address = generated_code_base_ + generated_code_offset_;
|
||||
generated_code_offset_ += xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_table_slot = ++unwind_table_count_;
|
||||
unwind_reservation =
|
||||
RequestUnwindReservation(generated_code_base_ + generated_code_offset_);
|
||||
generated_code_offset_ += unwind_reservation.data_size;
|
||||
|
||||
high_mark = generated_code_offset_;
|
||||
}
|
||||
|
@ -203,18 +151,9 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
|||
// Copy code.
|
||||
std::memcpy(code_address, machine_code, code_size);
|
||||
|
||||
// Add unwind info.
|
||||
InitializeUnwindEntry(unwind_entry_address, unwind_table_slot, code_address,
|
||||
code_size, stack_size);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Notify that the unwind table has grown.
|
||||
// We do this outside of the lock, but with the latest total count.
|
||||
RtlGrowFunctionTable(unwind_table_handle_, unwind_table_count_);
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address, code_size);
|
||||
// Notify subclasses of placed code.
|
||||
PlaceCode(guest_address, machine_code, code_size, stack_size, code_address,
|
||||
unwind_reservation);
|
||||
|
||||
// Now that everything is ready, fix up the indirection table.
|
||||
// Note that we do support code that doesn't have an indirection fixup, so
|
||||
|
@ -228,146 +167,6 @@ void* X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
|||
return code_address;
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ssa62fwe.aspx
|
||||
typedef enum _UNWIND_OP_CODES {
|
||||
UWOP_PUSH_NONVOL = 0, /* info == register number */
|
||||
UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
|
||||
UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
|
||||
UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
|
||||
UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
|
||||
UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
|
||||
UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
|
||||
UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
|
||||
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
|
||||
} UNWIND_CODE_OPS;
|
||||
class UNWIND_REGISTER {
|
||||
public:
|
||||
enum _ {
|
||||
RAX = 0,
|
||||
RCX = 1,
|
||||
RDX = 2,
|
||||
RBX = 3,
|
||||
RSP = 4,
|
||||
RBP = 5,
|
||||
RSI = 6,
|
||||
RDI = 7,
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R11 = 11,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15,
|
||||
};
|
||||
};
|
||||
|
||||
typedef union _UNWIND_CODE {
|
||||
struct {
|
||||
uint8_t CodeOffset;
|
||||
uint8_t UnwindOp : 4;
|
||||
uint8_t OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE, *PUNWIND_CODE;
|
||||
|
||||
typedef struct _UNWIND_INFO {
|
||||
uint8_t Version : 3;
|
||||
uint8_t Flags : 5;
|
||||
uint8_t SizeOfProlog;
|
||||
uint8_t CountOfCodes;
|
||||
uint8_t FrameRegister : 4;
|
||||
uint8_t FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode[1];
|
||||
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
|
||||
* union {
|
||||
* OPTIONAL ULONG ExceptionHandler;
|
||||
* OPTIONAL ULONG FunctionEntry;
|
||||
* };
|
||||
* OPTIONAL ULONG ExceptionData[]; */
|
||||
} UNWIND_INFO, *PUNWIND_INFO;
|
||||
|
||||
void X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot,
|
||||
uint8_t* code_address,
|
||||
size_t code_size, size_t stack_size) {
|
||||
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
||||
|
||||
if (!stack_size) {
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = 0;
|
||||
unwind_info->CountOfCodes = 0;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
} else if (stack_size <= 128) {
|
||||
uint8_t prolog_size = 4;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 1;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
14; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_SMALL;
|
||||
unwind_code.OpInfo = stack_size / 8 - 1;
|
||||
} else {
|
||||
// TODO(benvanik): take as parameters?
|
||||
uint8_t prolog_size = 7;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 2;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code.OpInfo = 0;
|
||||
unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.FrameOffset = (USHORT)(stack_size) / 8;
|
||||
}
|
||||
|
||||
// Add entry.
|
||||
auto& fn_entry = unwind_table_[unwind_table_slot];
|
||||
fn_entry.BeginAddress = (DWORD)(code_address - generated_code_base_);
|
||||
fn_entry.EndAddress = (DWORD)(fn_entry.BeginAddress + code_size);
|
||||
fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_);
|
||||
}
|
||||
|
||||
void* X64CodeCache::LookupUnwindEntry(uintptr_t host_address) {
|
||||
void* fn_entry = std::bsearch(
|
||||
&host_address, unwind_table_.data(), unwind_table_count_ + 1,
|
||||
sizeof(RUNTIME_FUNCTION),
|
||||
[](const void* key_ptr, const void* element_ptr) {
|
||||
auto key =
|
||||
*reinterpret_cast<const uintptr_t*>(key_ptr) - kGeneratedCodeBase;
|
||||
auto element = reinterpret_cast<const RUNTIME_FUNCTION*>(element_ptr);
|
||||
if (key < element->BeginAddress) {
|
||||
return -1;
|
||||
} else if (key > element->EndAddress) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return reinterpret_cast<RUNTIME_FUNCTION*>(fn_entry);
|
||||
}
|
||||
|
||||
uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||
// Hold a lock while we bump the pointers up.
|
||||
size_t high_mark;
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
#define XENIA_BACKEND_X64_X64_CODE_CACHE_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/cpu/backend/code_cache.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -27,10 +27,11 @@ namespace x64 {
|
|||
|
||||
class X64CodeCache : public CodeCache {
|
||||
public:
|
||||
X64CodeCache();
|
||||
~X64CodeCache() override;
|
||||
|
||||
bool Initialize();
|
||||
static std::unique_ptr<X64CodeCache> Create();
|
||||
|
||||
virtual bool Initialize();
|
||||
|
||||
std::wstring file_name() const override { return file_name_; }
|
||||
uint32_t base_address() const override { return kGeneratedCodeBase; }
|
||||
|
@ -50,16 +51,27 @@ class X64CodeCache : public CodeCache {
|
|||
|
||||
uint32_t PlaceData(const void* data, size_t length);
|
||||
|
||||
private:
|
||||
protected:
|
||||
const static uint64_t kIndirectionTableBase = 0x80000000;
|
||||
const static uint64_t kIndirectionTableSize = 0x1FFFFFFF;
|
||||
const static uint64_t kGeneratedCodeBase = 0xA0000000;
|
||||
const static uint64_t kGeneratedCodeSize = 0x0FFFFFFF;
|
||||
|
||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot, uint8_t* code_address,
|
||||
size_t code_size, size_t stack_size);
|
||||
void* LookupUnwindEntry(uintptr_t host_address);
|
||||
struct UnwindReservation {
|
||||
size_t data_size = 0;
|
||||
size_t table_slot = 0;
|
||||
uint8_t* entry_address = 0;
|
||||
};
|
||||
|
||||
X64CodeCache();
|
||||
|
||||
virtual UnwindReservation RequestUnwindReservation(uint8_t* entry_address) {
|
||||
return UnwindReservation();
|
||||
}
|
||||
virtual void PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
void* code_address,
|
||||
UnwindReservation unwind_reservation) {}
|
||||
|
||||
std::wstring file_name_;
|
||||
xe::memory::FileMappingHandle mapping_ = nullptr;
|
||||
|
@ -82,13 +94,6 @@ class X64CodeCache : public CodeCache {
|
|||
size_t generated_code_offset_ = 0;
|
||||
// Current high water mark of COMMITTED code.
|
||||
std::atomic<size_t> generated_code_commit_mark_ = {0};
|
||||
|
||||
// Growable function table system handle.
|
||||
void* unwind_table_handle_ = nullptr;
|
||||
// Actual unwind table entries.
|
||||
std::vector<RUNTIME_FUNCTION> unwind_table_;
|
||||
// Current number of entries in the table.
|
||||
std::atomic<uint32_t> unwind_table_count_ = {0};
|
||||
};
|
||||
|
||||
} // namespace x64
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_code_cache.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#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/base/platform_win.h"
|
||||
|
||||
// When enabled, this will use Windows 8 APIs to get unwind info.
|
||||
// TODO(benvanik): figure out why the callback variant doesn't work.
|
||||
#define USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// Size of unwind info per function.
|
||||
// TODO(benvanik): move this to emitter.
|
||||
const static uint32_t kUnwindInfoSize = 4 + (2 * 1 + 2 + 2);
|
||||
|
||||
class Win32X64CodeCache : public X64CodeCache {
|
||||
public:
|
||||
Win32X64CodeCache();
|
||||
~Win32X64CodeCache() override;
|
||||
|
||||
bool Initialize() override;
|
||||
|
||||
private:
|
||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
||||
size_t stack_size, void* code_address,
|
||||
UnwindReservation unwind_reservation) override;
|
||||
|
||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot, void* code_address,
|
||||
size_t code_size, size_t stack_size);
|
||||
void* LookupUnwindEntry(uintptr_t host_address);
|
||||
|
||||
// Growable function table system handle.
|
||||
void* unwind_table_handle_ = nullptr;
|
||||
// Actual unwind table entries.
|
||||
std::vector<RUNTIME_FUNCTION> unwind_table_;
|
||||
// Current number of entries in the table.
|
||||
std::atomic<uint32_t> unwind_table_count_ = {0};
|
||||
};
|
||||
|
||||
std::unique_ptr<X64CodeCache> X64CodeCache::Create() {
|
||||
return std::make_unique<Win32X64CodeCache>();
|
||||
}
|
||||
|
||||
Win32X64CodeCache::Win32X64CodeCache() = default;
|
||||
|
||||
Win32X64CodeCache::~Win32X64CodeCache() {
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
if (unwind_table_handle_) {
|
||||
RtlDeleteGrowableFunctionTable(unwind_table_handle_);
|
||||
}
|
||||
#else
|
||||
if (generated_code_base_) {
|
||||
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3));
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
}
|
||||
|
||||
bool Win32X64CodeCache::Initialize() {
|
||||
if (!X64CodeCache::Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute total number of unwind entries we should allocate.
|
||||
// We don't support reallocing right now, so this should be high.
|
||||
unwind_table_.resize(30000);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Create table and register with the system. It's empty now, but we'll grow
|
||||
// it as functions are added.
|
||||
if (RtlAddGrowableFunctionTable(
|
||||
&unwind_table_handle_, unwind_table_.data(), unwind_table_count_,
|
||||
DWORD(unwind_table_.size()),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_ +
|
||||
kGeneratedCodeSize))) {
|
||||
XELOGE("Unable to create unwind function table");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Install a callback that the debugger will use to lookup unwind info on
|
||||
// demand.
|
||||
if (!RtlInstallFunctionTableCallback(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3,
|
||||
reinterpret_cast<DWORD64>(generated_code_base_),
|
||||
kGeneratedCodeSize, [](uintptr_t control_pc, void* context) {
|
||||
auto code_cache = reinterpret_cast<X64CodeCache*>(context);
|
||||
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
code_cache->LookupUnwindEntry(control_pc));
|
||||
}, this, nullptr)) {
|
||||
XELOGE("Unable to install function table callback");
|
||||
return false;
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Win32X64CodeCache::UnwindReservation
|
||||
Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||
UnwindReservation unwind_reservation;
|
||||
unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_reservation.table_slot = ++unwind_table_count_;
|
||||
unwind_reservation.entry_address = entry_address;
|
||||
return unwind_reservation;
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
void* code_address,
|
||||
UnwindReservation unwind_reservation) {
|
||||
// Add unwind info.
|
||||
InitializeUnwindEntry(unwind_reservation.entry_address,
|
||||
unwind_reservation.table_slot, code_address, code_size,
|
||||
stack_size);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Notify that the unwind table has grown.
|
||||
// We do this outside of the lock, but with the latest total count.
|
||||
RtlGrowFunctionTable(unwind_table_handle_, unwind_table_count_);
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address, code_size);
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ssa62fwe.aspx
|
||||
typedef enum _UNWIND_OP_CODES {
|
||||
UWOP_PUSH_NONVOL = 0, /* info == register number */
|
||||
UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
|
||||
UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
|
||||
UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
|
||||
UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
|
||||
UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
|
||||
UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
|
||||
UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
|
||||
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
|
||||
} UNWIND_CODE_OPS;
|
||||
class UNWIND_REGISTER {
|
||||
public:
|
||||
enum _ {
|
||||
RAX = 0,
|
||||
RCX = 1,
|
||||
RDX = 2,
|
||||
RBX = 3,
|
||||
RSP = 4,
|
||||
RBP = 5,
|
||||
RSI = 6,
|
||||
RDI = 7,
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R11 = 11,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15,
|
||||
};
|
||||
};
|
||||
|
||||
typedef union _UNWIND_CODE {
|
||||
struct {
|
||||
uint8_t CodeOffset;
|
||||
uint8_t UnwindOp : 4;
|
||||
uint8_t OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE, *PUNWIND_CODE;
|
||||
|
||||
typedef struct _UNWIND_INFO {
|
||||
uint8_t Version : 3;
|
||||
uint8_t Flags : 5;
|
||||
uint8_t SizeOfProlog;
|
||||
uint8_t CountOfCodes;
|
||||
uint8_t FrameRegister : 4;
|
||||
uint8_t FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode[1];
|
||||
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
|
||||
* union {
|
||||
* OPTIONAL ULONG ExceptionHandler;
|
||||
* OPTIONAL ULONG FunctionEntry;
|
||||
* };
|
||||
* OPTIONAL ULONG ExceptionData[]; */
|
||||
} UNWIND_INFO, *PUNWIND_INFO;
|
||||
|
||||
void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot,
|
||||
void* code_address,
|
||||
size_t code_size,
|
||||
size_t stack_size) {
|
||||
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
||||
|
||||
if (!stack_size) {
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = 0;
|
||||
unwind_info->CountOfCodes = 0;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
} else if (stack_size <= 128) {
|
||||
uint8_t prolog_size = 4;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 1;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
14; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_SMALL;
|
||||
unwind_code.OpInfo = stack_size / 8 - 1;
|
||||
} else {
|
||||
// TODO(benvanik): take as parameters?
|
||||
uint8_t prolog_size = 7;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 2;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code.OpInfo = 0;
|
||||
unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.FrameOffset = (USHORT)(stack_size) / 8;
|
||||
}
|
||||
|
||||
// Add entry.
|
||||
auto& fn_entry = unwind_table_[unwind_table_slot];
|
||||
fn_entry.BeginAddress =
|
||||
(DWORD)(reinterpret_cast<uint8_t*>(code_address) - generated_code_base_);
|
||||
fn_entry.EndAddress = (DWORD)(fn_entry.BeginAddress + code_size);
|
||||
fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_);
|
||||
}
|
||||
|
||||
void* Win32X64CodeCache::LookupUnwindEntry(uintptr_t host_address) {
|
||||
void* fn_entry = std::bsearch(
|
||||
&host_address, unwind_table_.data(), unwind_table_count_ + 1,
|
||||
sizeof(RUNTIME_FUNCTION),
|
||||
[](const void* key_ptr, const void* element_ptr) {
|
||||
auto key =
|
||||
*reinterpret_cast<const uintptr_t*>(key_ptr) - kGeneratedCodeBase;
|
||||
auto element = reinterpret_cast<const RUNTIME_FUNCTION*>(element_ptr);
|
||||
if (key < element->BeginAddress) {
|
||||
return -1;
|
||||
} else if (key > element->EndAddress) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return reinterpret_cast<RUNTIME_FUNCTION*>(fn_entry);
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
Loading…
Reference in New Issue