Browse Source

[port] NetBSD and improper ctor for SpinLock fixes (#3092)

So when libc starts it has to start at an entry point located into crt0, now most OSes will do "enough" setup to allow mprotect() and mmap() to be called in static ctors (remember they're called BEFORE main)
By some stupid miracle, NetBSD doesn't; this means that using those functions on NetBSD will result in spurious results
The reason why is still unknown to me, but this is also combined with the fact that allocating a big chunk of memory for the JIT will make NetBSD refuse to mprotect()/mmap() it in low memory situations (even when space is available); so I take the same approach as with solaris
Also I now make it so fastmem handlers are NOT registered for OSes that disabled fastmem, this is because they pollute sigsegv and makes debugging stupidier

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3092
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
pull/3124/head
lizzie 3 weeks ago
committed by crueter
parent
commit
941caf31ce
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 2
      externals/CMakeLists.txt
  2. 4
      src/core/arm/dynarmic/arm_dynarmic_32.cpp
  3. 2
      src/core/arm/dynarmic/arm_dynarmic_64.cpp
  4. 7
      src/dynarmic/CMakeLists.txt
  5. 2
      src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp
  6. 2
      src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp
  7. 14
      src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp
  8. 35
      src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp
  9. 12
      src/video_core/macro/macro_jit_x64.cpp

2
externals/CMakeLists.txt

@ -27,7 +27,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# Xbyak (also used by Dynarmic, so needs to be added first) # Xbyak (also used by Dynarmic, so needs to be added first)
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
if (PLATFORM_SUN OR PLATFORM_OPENBSD)
if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_NETBSD OR PLATFORM_DRAGONFLY)
AddJsonPackage(xbyak_sun) AddJsonPackage(xbyak_sun)
else() else()
AddJsonPackage(xbyak) AddJsonPackage(xbyak)

4
src/core/arm/dynarmic/arm_dynarmic_32.cpp

@ -211,7 +211,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
config.enable_cycle_counting = !m_uses_wall_clock; config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size // Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
config.code_cache_size = std::uint32_t(128_MiB); config.code_cache_size = std::uint32_t(128_MiB);
#else #else
config.code_cache_size = std::uint32_t(512_MiB); config.code_cache_size = std::uint32_t(512_MiB);
@ -295,7 +295,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
// Curated optimizations // Curated optimizations
case Settings::CpuAccuracy::Auto: case Settings::CpuAccuracy::Auto:
config.unsafe_optimizations = true; config.unsafe_optimizations = true;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__)
config.fastmem_pointer = std::nullopt; config.fastmem_pointer = std::nullopt;
config.fastmem_exclusive_access = false; config.fastmem_exclusive_access = false;
#endif #endif

2
src/core/arm/dynarmic/arm_dynarmic_64.cpp

@ -270,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
config.enable_cycle_counting = !m_uses_wall_clock; config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size // Code cache size
#if defined(ARCHITECTURE_arm64) || defined(__sun__)
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
config.code_cache_size = std::uint32_t(128_MiB); config.code_cache_size = std::uint32_t(128_MiB);
#else #else
config.code_cache_size = std::uint32_t(512_MiB); config.code_cache_size = std::uint32_t(512_MiB);

7
src/dynarmic/CMakeLists.txt

@ -18,7 +18,12 @@ endif()
# Dynarmic project options # Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON) option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${PLATFORM_OPENBSD})
if (PLATFORM_OPENBSD OR PLATFORM_DRAGONFLY OR PLATFORM_NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
endif()
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF) option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF) option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)

2
src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp

@ -87,10 +87,12 @@ A32EmitX64::A32EmitX64(BlockOfCode& code, A32::UserConfig conf, A32::Jit* jit_in
code.PreludeComplete(); code.PreludeComplete();
ClearFastDispatchTable(); ClearFastDispatchTable();
if (conf.fastmem_pointer.has_value()) {
exception_handler.SetFastmemCallback([this](u64 rip_) { exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_); return FastmemCallback(rip_);
}); });
} }
}
A32EmitX64::~A32EmitX64() = default; A32EmitX64::~A32EmitX64() = default;

2
src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp

@ -61,10 +61,12 @@ A64EmitX64::A64EmitX64(BlockOfCode& code, A64::UserConfig conf, A64::Jit* jit_in
code.PreludeComplete(); code.PreludeComplete();
ClearFastDispatchTable(); ClearFastDispatchTable();
if (conf.fastmem_pointer.has_value()) {
exception_handler.SetFastmemCallback([this](u64 rip_) { exception_handler.SetFastmemCallback([this](u64 rip_) {
return FastmemCallback(rip_); return FastmemCallback(rip_);
}); });
} }
}
A64EmitX64::~A64EmitX64() = default; A64EmitX64::~A64EmitX64() = default;

14
src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp

@ -87,18 +87,24 @@ public:
// Waste a page to store the size // Waste a page to store the size
size += DYNARMIC_PAGE_SIZE; size += DYNARMIC_PAGE_SIZE;
int mode = MAP_PRIVATE;
#if defined(MAP_ANONYMOUS) #if defined(MAP_ANONYMOUS)
int mode = MAP_PRIVATE | MAP_ANONYMOUS;
mode |= MAP_ANONYMOUS;
#elif defined(MAP_ANON) #elif defined(MAP_ANON)
int mode = MAP_PRIVATE | MAP_ANON;
mode |= MAP_ANON;
#else #else
# error "not supported" # error "not supported"
#endif #endif
#ifdef MAP_JIT #ifdef MAP_JIT
mode |= MAP_JIT; mode |= MAP_JIT;
#endif #endif
void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, mode, -1, 0);
int prot = PROT_READ | PROT_WRITE;
#ifdef PROT_MPROTECT
// https://man.netbsd.org/mprotect.2 specifies that an mprotect() that is LESS
// restrictive than the original mapping MUST fail
prot |= PROT_MPROTECT(PROT_READ) | PROT_MPROTECT(PROT_WRITE) | PROT_MPROTECT(PROT_EXEC);
#endif
void* p = mmap(nullptr, size, prot, mode, -1, 0);
if (p == MAP_FAILED) { if (p == MAP_FAILED) {
using Xbyak::Error; using Xbyak::Error;
XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); XBYAK_THROW(Xbyak::ERR_CANT_ALLOC);

35
src/dynarmic/src/dynarmic/common/spin_lock_x64.cpp

@ -7,7 +7,7 @@
*/ */
#include <mutex> #include <mutex>
#include <optional>
#include <xbyak/xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/abi.h"
@ -42,43 +42,46 @@ void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg
} }
namespace { namespace {
struct SpinLockImpl { struct SpinLockImpl {
void Initialize();
void Initialize() noexcept;
static void GlobalInitialize() noexcept;
Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode); Xbyak::CodeGenerator code = Xbyak::CodeGenerator(4096, default_cg_mode);
void (*lock)(volatile int*);
void (*unlock)(volatile int*);
void (*lock)(volatile int*) = nullptr;
void (*unlock)(volatile int*) = nullptr;
}; };
std::once_flag flag; std::once_flag flag;
SpinLockImpl impl;
void SpinLockImpl::Initialize() {
const Xbyak::Reg64 ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
/// @brief Bear in mind that initializing the variable as-is on ctor time will trigger bugs
/// because some OSes do not prepare mprotect() properly at static ctor time
/// We can't really do anything about it, so just live with this fact
std::optional<SpinLockImpl> impl;
void SpinLockImpl::Initialize() noexcept {
Xbyak::Reg64 const ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
code.align(); code.align();
lock = code.getCurr<void (*)(volatile int*)>(); lock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockLock(code, ABI_PARAM1, code.eax); EmitSpinLockLock(code, ABI_PARAM1, code.eax);
code.ret(); code.ret();
code.align(); code.align();
unlock = code.getCurr<void (*)(volatile int*)>(); unlock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockUnlock(code, ABI_PARAM1, code.eax); EmitSpinLockUnlock(code, ABI_PARAM1, code.eax);
code.ret(); code.ret();
} }
void SpinLockImpl::GlobalInitialize() noexcept {
impl.emplace();
impl->Initialize();
}
} // namespace } // namespace
void SpinLock::Lock() noexcept { void SpinLock::Lock() noexcept {
std::call_once(flag, &SpinLockImpl::Initialize, impl);
impl.lock(&storage);
std::call_once(flag, &SpinLockImpl::GlobalInitialize);
impl->lock(&storage);
} }
void SpinLock::Unlock() noexcept { void SpinLock::Unlock() noexcept {
std::call_once(flag, &SpinLockImpl::Initialize, impl);
impl.unlock(&storage);
std::call_once(flag, &SpinLockImpl::GlobalInitialize);
impl->unlock(&storage);
} }
} // namespace Dynarmic } // namespace Dynarmic

12
src/video_core/macro/macro_jit_x64.cpp

@ -44,10 +44,20 @@ std::bitset<32> PersistentCallerSavedRegs() {
return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED; return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED;
} }
/// @brief Must enforce W^X constraints, as we yet don't havea global "NO_EXECUTE" support flag
/// the speed loss is minimal, and in fact may be negligible, however for your peace of mind
/// I simply included known OSes whom had W^X issues
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
#else
static const auto default_cg_mode = nullptr; //Allow RWE
#endif
class MacroJITx64Impl final : public Xbyak::CodeGenerator, public CachedMacro { class MacroJITx64Impl final : public Xbyak::CodeGenerator, public CachedMacro {
public: public:
explicit MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_) explicit MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_)
: CodeGenerator{MAX_CODE_SIZE}, code{code_}, maxwell3d{maxwell3d_} {
: Xbyak::CodeGenerator(MAX_CODE_SIZE, default_cg_mode)
, code{code_}, maxwell3d{maxwell3d_} {
Compile(); Compile();
} }

Loading…
Cancel
Save