backend/x64: Support W^X systems

Closes #176.
This commit is contained in:
MerryMage 2018-08-22 13:16:26 +01:00
parent 2a65442933
commit 3432a08e0a
5 changed files with 59 additions and 1 deletions

View file

@ -22,6 +22,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scope_exit.h"
#include "common/variant_util.h" #include "common/variant_util.h"
#include "frontend/A32/location_descriptor.h" #include "frontend/A32/location_descriptor.h"
#include "frontend/A32/types.h" #include "frontend/A32/types.h"
@ -85,6 +86,9 @@ A32EmitX64::A32EmitX64(BlockOfCode& code, A32::UserConfig config, A32::Jit* jit_
A32EmitX64::~A32EmitX64() = default; A32EmitX64::~A32EmitX64() = default;
A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); };
code.align(); code.align();
const u8* const entrypoint = code.getCurr(); const u8* const entrypoint = code.getCurr();

View file

@ -19,6 +19,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scope_exit.h"
#include "common/variant_util.h" #include "common/variant_util.h"
#include "frontend/A64/location_descriptor.h" #include "frontend/A64/location_descriptor.h"
#include "frontend/A64/types.h" #include "frontend/A64/types.h"
@ -71,6 +72,9 @@ A64EmitX64::A64EmitX64(BlockOfCode& code, A64::UserConfig conf, A64::Jit* jit_in
A64EmitX64::~A64EmitX64() = default; A64EmitX64::~A64EmitX64() = default;
A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); };
code.align(); code.align();
const u8* const entrypoint = code.getCurr(); const u8* const entrypoint = code.getCurr();

View file

@ -15,6 +15,12 @@
#include "backend/x64/block_of_code.h" #include "backend/x64/block_of_code.h"
#include "common/assert.h" #include "common/assert.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif
namespace Dynarmic::BackendX64 { namespace Dynarmic::BackendX64 {
#ifdef _WIN32 #ifdef _WIN32
@ -36,16 +42,42 @@ const Xbyak::Reg64 BlockOfCode::ABI_PARAM6 = Xbyak::util::r9;
const std::array<Xbyak::Reg64, 6> BlockOfCode::ABI_PARAMS = {BlockOfCode::ABI_PARAM1, BlockOfCode::ABI_PARAM2, BlockOfCode::ABI_PARAM3, BlockOfCode::ABI_PARAM4, BlockOfCode::ABI_PARAM5, BlockOfCode::ABI_PARAM6}; const std::array<Xbyak::Reg64, 6> BlockOfCode::ABI_PARAMS = {BlockOfCode::ABI_PARAM1, BlockOfCode::ABI_PARAM2, BlockOfCode::ABI_PARAM3, BlockOfCode::ABI_PARAM4, BlockOfCode::ABI_PARAM5, BlockOfCode::ABI_PARAM6};
#endif #endif
namespace {
constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024; constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024;
constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024; constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024;
constexpr size_t CONSTANT_POOL_SIZE = 2 * 1024 * 1024; constexpr size_t CONSTANT_POOL_SIZE = 2 * 1024 * 1024;
class CustomXbyakAllocator : public Xbyak::Allocator {
public:
bool useProtect() const override { return false; }
};
// This is threadsafe as Xbyak::Allocator does not contain any state; it is a pure interface.
CustomXbyakAllocator s_allocator;
void ProtectMemory(const void* base, size_t size, bool is_executable) {
#ifdef _WIN32
DWORD oldProtect = 0;
VirtualProtect(const_cast<void*>(base), size, is_executable ? PAGE_EXECUTE_READ : PAGE_READWRITE, &oldProtect);
#else
static const size_t pageSize = sysconf(_SC_PAGESIZE);
const size_t iaddr = reinterpret_cast<size_t>(base);
const size_t roundAddr = iaddr & ~(pageSize - static_cast<size_t>(1));
const int mode = is_executable ? (PROT_READ | PROT_EXEC) : (PROT_READ | PROT_WRITE);
mprotect(reinterpret_cast<void*>(roundAddr), size + (iaddr - roundAddr), mode);
#endif
}
} // anonymous namespace
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi) BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi)
: Xbyak::CodeGenerator(TOTAL_CODE_SIZE) : Xbyak::CodeGenerator(TOTAL_CODE_SIZE, nullptr, &s_allocator)
, cb(std::move(cb)) , cb(std::move(cb))
, jsi(jsi) , jsi(jsi)
, constant_pool(*this, CONSTANT_POOL_SIZE) , constant_pool(*this, CONSTANT_POOL_SIZE)
{ {
EnableWriting();
GenRunCode(); GenRunCode();
exception_handler.Register(*this); exception_handler.Register(*this);
} }
@ -55,6 +87,15 @@ void BlockOfCode::PreludeComplete() {
near_code_begin = getCurr(); near_code_begin = getCurr();
far_code_begin = getCurr() + FAR_CODE_OFFSET; far_code_begin = getCurr() + FAR_CODE_OFFSET;
ClearCache(); ClearCache();
DisableWriting();
}
void BlockOfCode::EnableWriting() {
ProtectMemory(getCode(), maxSize_, false);
}
void BlockOfCode::DisableWriting() {
ProtectMemory(getCode(), maxSize_, true);
} }
void BlockOfCode::ClearCache() { void BlockOfCode::ClearCache() {

View file

@ -34,6 +34,11 @@ public:
/// Call when external emitters have finished emitting their preludes. /// Call when external emitters have finished emitting their preludes.
void PreludeComplete(); void PreludeComplete();
/// Change permissions to RW. This is required to support systems with W^X enforced.
void EnableWriting();
/// Change permissions to RX. This is required to support systems with W^X enforced.
void DisableWriting();
/// Clears this block of code and resets code pointer to beginning. /// Clears this block of code and resets code pointer to beginning.
void ClearCache(); void ClearCache();
/// Calculates how much space is remaining to use. This is the minimum of near code and far code. /// Calculates how much space is remaining to use. This is the minimum of near code and far code.

View file

@ -11,6 +11,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scope_exit.h"
#include "common/variant_util.h" #include "common/variant_util.h"
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/microinstruction.h" #include "frontend/ir/microinstruction.h"
@ -327,6 +328,9 @@ void EmitX64::ClearCache() {
} }
void EmitX64::InvalidateBasicBlocks(const std::unordered_set<IR::LocationDescriptor>& locations) { void EmitX64::InvalidateBasicBlocks(const std::unordered_set<IR::LocationDescriptor>& locations) {
code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); };
for (const auto &descriptor : locations) { for (const auto &descriptor : locations) {
auto it = block_descriptors.find(descriptor); auto it = block_descriptors.find(descriptor);
if (it == block_descriptors.end()) { if (it == block_descriptors.end()) {