From 2a705d0393220e69b32318bb41fc6aa2d51a0da7 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Wed, 22 Aug 2018 13:16:26 +0100 Subject: [PATCH] backend/x64: Support W^X systems Closes #176. --- src/backend/x64/a32_emit_x64.cpp | 4 +++ src/backend/x64/a64_emit_x64.cpp | 4 +++ src/backend/x64/block_of_code.cpp | 43 ++++++++++++++++++++++++++++++- src/backend/x64/block_of_code.h | 5 ++++ src/backend/x64/emit_x64.cpp | 4 +++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/backend/x64/a32_emit_x64.cpp b/src/backend/x64/a32_emit_x64.cpp index 62320873..46f7e944 100644 --- a/src/backend/x64/a32_emit_x64.cpp +++ b/src/backend/x64/a32_emit_x64.cpp @@ -22,6 +22,7 @@ #include "common/assert.h" #include "common/bit_util.h" #include "common/common_types.h" +#include "common/scope_exit.h" #include "common/variant_util.h" #include "frontend/A32/location_descriptor.h" #include "frontend/A32/types.h" @@ -85,6 +86,9 @@ A32EmitX64::A32EmitX64(BlockOfCode& code, A32::UserConfig config, A32::Jit* jit_ A32EmitX64::~A32EmitX64() = default; A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { + code.EnableWriting(); + SCOPE_EXIT { code.DisableWriting(); }; + code.align(); const u8* const entrypoint = code.getCurr(); diff --git a/src/backend/x64/a64_emit_x64.cpp b/src/backend/x64/a64_emit_x64.cpp index 909de239..bc711e9d 100644 --- a/src/backend/x64/a64_emit_x64.cpp +++ b/src/backend/x64/a64_emit_x64.cpp @@ -19,6 +19,7 @@ #include "common/assert.h" #include "common/bit_util.h" #include "common/common_types.h" +#include "common/scope_exit.h" #include "common/variant_util.h" #include "frontend/A64/location_descriptor.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::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { + code.EnableWriting(); + SCOPE_EXIT { code.DisableWriting(); }; + code.align(); const u8* const entrypoint = code.getCurr(); diff --git a/src/backend/x64/block_of_code.cpp b/src/backend/x64/block_of_code.cpp index 8dbd08c2..bb78beeb 100644 --- a/src/backend/x64/block_of_code.cpp +++ b/src/backend/x64/block_of_code.cpp @@ -15,6 +15,12 @@ #include "backend/x64/block_of_code.h" #include "common/assert.h" +#ifdef _WIN32 + #include +#else + #include +#endif + namespace Dynarmic::BackendX64 { #ifdef _WIN32 @@ -36,16 +42,42 @@ const Xbyak::Reg64 BlockOfCode::ABI_PARAM6 = Xbyak::util::r9; const std::array BlockOfCode::ABI_PARAMS = {BlockOfCode::ABI_PARAM1, BlockOfCode::ABI_PARAM2, BlockOfCode::ABI_PARAM3, BlockOfCode::ABI_PARAM4, BlockOfCode::ABI_PARAM5, BlockOfCode::ABI_PARAM6}; #endif +namespace { + constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024; constexpr size_t FAR_CODE_OFFSET = 100 * 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(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(base); + const size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + const int mode = is_executable ? (PROT_READ | PROT_EXEC) : (PROT_READ | PROT_WRITE); + mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode); +#endif +} + +} // anonymous namespace + BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi) - : Xbyak::CodeGenerator(TOTAL_CODE_SIZE) + : Xbyak::CodeGenerator(TOTAL_CODE_SIZE, nullptr, &s_allocator) , cb(std::move(cb)) , jsi(jsi) , constant_pool(*this, CONSTANT_POOL_SIZE) { + EnableWriting(); GenRunCode(); exception_handler.Register(*this); } @@ -55,6 +87,15 @@ void BlockOfCode::PreludeComplete() { near_code_begin = getCurr(); far_code_begin = getCurr() + FAR_CODE_OFFSET; ClearCache(); + DisableWriting(); +} + +void BlockOfCode::EnableWriting() { + ProtectMemory(getCode(), maxSize_, false); +} + +void BlockOfCode::DisableWriting() { + ProtectMemory(getCode(), maxSize_, true); } void BlockOfCode::ClearCache() { diff --git a/src/backend/x64/block_of_code.h b/src/backend/x64/block_of_code.h index 32627396..a99db2a6 100644 --- a/src/backend/x64/block_of_code.h +++ b/src/backend/x64/block_of_code.h @@ -34,6 +34,11 @@ public: /// Call when external emitters have finished emitting their preludes. 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. void ClearCache(); /// Calculates how much space is remaining to use. This is the minimum of near code and far code. diff --git a/src/backend/x64/emit_x64.cpp b/src/backend/x64/emit_x64.cpp index 8fae3d99..41f3316a 100644 --- a/src/backend/x64/emit_x64.cpp +++ b/src/backend/x64/emit_x64.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/bit_util.h" #include "common/common_types.h" +#include "common/scope_exit.h" #include "common/variant_util.h" #include "frontend/ir/basic_block.h" #include "frontend/ir/microinstruction.h" @@ -328,6 +329,9 @@ void EmitX64::ClearCache() { } void EmitX64::InvalidateBasicBlocks(const std::unordered_set& locations) { + code.EnableWriting(); + SCOPE_EXIT { code.DisableWriting(); }; + for (const auto &descriptor : locations) { auto it = block_descriptors.find(descriptor); if (it == block_descriptors.end()) {