diff options
author | Vas Crabb <vas@vastheman.com> | 2021-01-06 01:25:58 +1100 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2021-01-06 02:18:04 +1100 |
commit | 4eca05fe67e6a61a30af9d168590f3f3aaf9f639 (patch) | |
tree | 8ca9d1878d84d54ffaa11d0bafe43ea28b638f33 /src/osd | |
parent | 7b22d972aea2dca442de9b8fb58687504e4444bc (diff) |
cpu: Allow recompilers to work with W^X policy
Diffstat (limited to 'src/osd')
-rw-r--r-- | src/osd/modules/lib/osdlib.h | 88 | ||||
-rw-r--r-- | src/osd/modules/lib/osdlib_macosx.cpp | 131 | ||||
-rw-r--r-- | src/osd/modules/lib/osdlib_unix.cpp | 134 | ||||
-rw-r--r-- | src/osd/modules/lib/osdlib_uwp.cpp | 85 | ||||
-rw-r--r-- | src/osd/modules/lib/osdlib_win32.cpp | 117 | ||||
-rw-r--r-- | src/osd/osdcore.h | 21 |
6 files changed, 352 insertions, 224 deletions
diff --git a/src/osd/modules/lib/osdlib.h b/src/osd/modules/lib/osdlib.h index 0095244d6fe..59b5291b0a8 100644 --- a/src/osd/modules/lib/osdlib.h +++ b/src/osd/modules/lib/osdlib.h @@ -16,6 +16,7 @@ #ifndef __OSDLIB__ #define __OSDLIB__ +#include <initializer_list> #include <string> #include <type_traits> #include <vector> @@ -58,6 +59,90 @@ int osd_setenv(const char *name, const char *value, int overwrite); -----------------------------------------------------------------------------*/ std::string osd_get_clipboard_text(void); +namespace osd { + +bool invalidate_instruction_cache(void const *start, std::size_t size); + + +class virtual_memory_allocation +{ +public: + enum : unsigned + { + NONE = 0x00, + READ = 0x01, + WRITE = 0x02, + EXECUTE = 0x04, + READ_WRITE = READ | WRITE, + READ_EXECUTE = READ | EXECUTE + }; + + virtual_memory_allocation(virtual_memory_allocation const &) = delete; + virtual_memory_allocation &operator=(virtual_memory_allocation const &) = delete; + + virtual_memory_allocation() { } + virtual_memory_allocation(std::initializer_list<std::size_t> blocks) + { + m_memory = do_alloc(blocks, m_size, m_page_size); + } + virtual_memory_allocation(virtual_memory_allocation &&that) : m_memory(that.m_memory), m_size(that.m_size), m_page_size(that.m_page_size) + { + that.m_memory = nullptr; + that.m_size = that.m_page_size = 0U; + } + ~virtual_memory_allocation() + { + if (m_memory) + do_free(m_memory, m_size); + } + + explicit operator bool() const { return bool(m_memory); } + void *get() { return m_memory; } + std::size_t size() const { return m_size; } + std::size_t page_size() const { return m_page_size; } + + bool set_access(std::size_t start, std::size_t size, unsigned access) + { + if ((start % m_page_size) || (size % m_page_size) || (start > m_size) || ((m_size - start) < size)) + return false; + else + return do_set_access(reinterpret_cast<std::uint8_t *>(m_memory) + start, size, access); + } + + virtual_memory_allocation &operator=(std::nullptr_t) + { + if (m_memory) + do_free(m_memory, m_size); + m_memory = nullptr; + m_size = m_page_size = 0U; + return *this; + } + + virtual_memory_allocation &operator=(virtual_memory_allocation &&that) + { + if (&that != this) + { + if (m_memory) + do_free(m_memory, m_size); + m_memory = that.m_memory; + m_size = that.m_size; + m_page_size = that.m_page_size; + that.m_memory = nullptr; + that.m_size = that.m_page_size = 0U; + } + return *this; + } + +private: + static void *do_alloc(std::initializer_list<std::size_t> blocks, std::size_t &size, std::size_t &page_size); + static void do_free(void *start, std::size_t size); + static bool do_set_access(void *start, std::size_t size, unsigned access); + + void *m_memory = nullptr; + std::size_t m_size = 0U, m_page_size = 0U; +}; + + /*----------------------------------------------------------------------------- dynamic_module: load functions from optional shared libraries @@ -69,7 +154,6 @@ std::string osd_get_clipboard_text(void); revisions of a same library) -----------------------------------------------------------------------------*/ -namespace osd { class dynamic_module { public: @@ -80,7 +164,7 @@ public: virtual ~dynamic_module() { }; template <typename T> - typename std::enable_if<std::is_pointer<T>::value, T>::type bind(char const *symbol) + typename std::enable_if_t<std::is_pointer_v<T>, T> bind(char const *symbol) { return reinterpret_cast<T>(get_symbol_address(symbol)); } diff --git a/src/osd/modules/lib/osdlib_macosx.cpp b/src/osd/modules/lib/osdlib_macosx.cpp index c1f0520fa9b..0b4c30bc14a 100644 --- a/src/osd/modules/lib/osdlib_macosx.cpp +++ b/src/osd/modules/lib/osdlib_macosx.cpp @@ -8,26 +8,26 @@ // //============================================================ -#include <cstdlib> -#include <unistd.h> -#include <sys/mman.h> -#include <sys/sysctl.h> -#include <sys/types.h> -#include <csignal> -#include <dlfcn.h> +// MAME headers +#include "osdcore.h" +#include "osdlib.h" +#include <csignal> #include <cstdio> +#include <cstdlib> #include <iomanip> #include <memory> +#include <dlfcn.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <Carbon/Carbon.h> -// MAME headers -#include "osdcore.h" -#include "osdlib.h" //============================================================ // osd_getenv @@ -57,42 +57,6 @@ void osd_process_kill() } //============================================================ -// osd_alloc_executable -// -// allocates "size" bytes of executable memory. this must take -// things like NX support into account. -//============================================================ - -void *osd_alloc_executable(size_t size) -{ -#if defined(SDLMAME_BSD) || defined(SDLMAME_MACOSX) - #ifdef __aarch64__ - // $$$$HACK! This assumes no DRC on Apple Silicon; making that work will be much more involved. - return (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); - #else - return (void *)mmap(0, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); - #endif -#elif defined(SDLMAME_UNIX) - return (void *)mmap(0, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, 0, 0); -#endif -} - -//============================================================ -// osd_free_executable -// -// frees memory allocated with osd_alloc_executable -//============================================================ - -void osd_free_executable(void *ptr, size_t size) -{ -#ifdef SDLMAME_SOLARIS - munmap((char *)ptr, size); -#else - munmap(ptr, size); -#endif -} - -//============================================================ // osd_break_into_debugger //============================================================ @@ -202,25 +166,23 @@ int osd_getpid(void) return getpid(); } -//============================================================ -// dynamic_module_posix_impl -//============================================================ namespace osd { + +namespace { + class dynamic_module_posix_impl : public dynamic_module { public: - dynamic_module_posix_impl(std::vector<std::string> &libraries) - : m_module(nullptr) + dynamic_module_posix_impl(std::vector<std::string> &&libraries) : m_libraries(std::move(libraries)) { - m_libraries = libraries; } virtual ~dynamic_module_posix_impl() override { - if (m_module != nullptr) + if (m_module) dlclose(m_module); - }; + } protected: virtual generic_fptr_t get_symbol_address(char const *symbol) override @@ -230,19 +192,17 @@ protected: * one of them, all additional symbols will be loaded from the same library */ if (m_module) - { return reinterpret_cast<generic_fptr_t>(dlsym(m_module, symbol)); - } for (auto const &library : m_libraries) { - void *module = dlopen(library.c_str(), RTLD_LAZY); + void *const module = dlopen(library.c_str(), RTLD_LAZY); if (module != nullptr) { - generic_fptr_t function = reinterpret_cast<generic_fptr_t>(dlsym(module, symbol)); + generic_fptr_t const function = reinterpret_cast<generic_fptr_t>(dlsym(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -259,12 +219,61 @@ protected: private: std::vector<std::string> m_libraries; - void * m_module; + void * m_module = nullptr; }; +} // anonymous namespace + + +bool invalidate_instruction_cache(void const *start, std::size_t size) +{ + char const *const begin(reinterpret_cast<char const *>(start)); + char const *const end(begin + size); + __builtin___clear_cache(const_cast<char *>(begin), const_cast<char *>(end)); + return true; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, std::size_t &size, std::size_t &page_size) +{ + long const p(sysconf(_SC_PAGE_SIZE)); + if (0 >= p) + return nullptr; + std::size_t s(0); + for (std::size_t b : blocks) + s += (b + p - 1) / p; + s *= p; + if (!s) + return nullptr; + void *const result(mmap(nullptr, s, PROT_NONE, MAP_ANON | MAP_SHARED, -1, 0)); + if (result == (void *)-1) + return nullptr; + size = s; + page_size = p; + return result; +} + +void virtual_memory_allocation::do_free(void *start, std::size_t size) +{ + munmap(start, size); +} + +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +{ + int prot((NONE == access) ? PROT_NONE : 0); + if (access & READ) + prot |= PROT_READ; + if (access & WRITE) + prot |= PROT_WRITE; + if (access & EXECUTE) + prot |= PROT_EXEC; + return mprotect(start, size, prot) == 0; +} + + dynamic_module::ptr dynamic_module::open(std::vector<std::string> &&names) { - return std::make_unique<dynamic_module_posix_impl>(names); + return std::make_unique<dynamic_module_posix_impl>(std::move(names)); } } // namespace osd diff --git a/src/osd/modules/lib/osdlib_unix.cpp b/src/osd/modules/lib/osdlib_unix.cpp index c2a34be7c98..1c51a2666c7 100644 --- a/src/osd/modules/lib/osdlib_unix.cpp +++ b/src/osd/modules/lib/osdlib_unix.cpp @@ -8,23 +8,23 @@ // //============================================================ -#include <cstdlib> -#include <unistd.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <csignal> -#include <dlfcn.h> +// MAME headers +#include "osdcore.h" +#include "osdlib.h" +#include <SDL2/SDL.h> + +#include <csignal> #include <cstdio> +#include <cstdlib> #include <iomanip> #include <memory> +#include <dlfcn.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> -// MAME headers -#include "osdcore.h" -#include "osdlib.h" - -#include <SDL2/SDL.h> //============================================================ // osd_getenv @@ -54,37 +54,6 @@ void osd_process_kill() } //============================================================ -// osd_alloc_executable -// -// allocates "size" bytes of executable memory. this must take -// things like NX support into account. -//============================================================ - -void *osd_alloc_executable(size_t size) -{ -#if defined(SDLMAME_BSD) || defined(SDLMAME_MACOSX) || defined(SDLMAME_EMSCRIPTEN) - return (void *)mmap(0, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); -#else - return (void *)mmap(0, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, 0, 0); -#endif -} - -//============================================================ -// osd_free_executable -// -// frees memory allocated with osd_alloc_executable -//============================================================ - -void osd_free_executable(void *ptr, size_t size) -{ -#ifdef SDLMAME_SOLARIS - munmap((char *)ptr, size); -#else - munmap(ptr, size); -#endif -} - -//============================================================ // osd_break_into_debugger //============================================================ @@ -133,25 +102,23 @@ int osd_getpid(void) return getpid(); } -//============================================================ -// dynamic_module_posix_impl -//============================================================ namespace osd { + +namespace { + class dynamic_module_posix_impl : public dynamic_module { public: - dynamic_module_posix_impl(std::vector<std::string> &libraries) - : m_module(nullptr) + dynamic_module_posix_impl(std::vector<std::string> &&libraries) : m_libraries(std::move(libraries)) { - m_libraries = libraries; } virtual ~dynamic_module_posix_impl() override { - if (m_module != nullptr) + if (m_module) dlclose(m_module); - }; + } protected: virtual generic_fptr_t get_symbol_address(char const *symbol) override @@ -161,19 +128,17 @@ protected: * one of them, all additional symbols will be loaded from the same library */ if (m_module) - { return reinterpret_cast<generic_fptr_t>(dlsym(m_module, symbol)); - } for (auto const &library : m_libraries) { - void *module = dlopen(library.c_str(), RTLD_LAZY); + void *const module = dlopen(library.c_str(), RTLD_LAZY); if (module != nullptr) { - generic_fptr_t function = reinterpret_cast<generic_fptr_t>(dlsym(module, symbol)); + generic_fptr_t const function = reinterpret_cast<generic_fptr_t>(dlsym(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -190,12 +155,67 @@ protected: private: std::vector<std::string> m_libraries; - void * m_module; + void * m_module = nullptr; }; +} // anonymous namespace + + +bool invalidate_instruction_cache(void const *start, std::size_t size) +{ + char const *const begin(reinterpret_cast<char const *>(start)); + char const *const end(begin + size); + __builtin___clear_cache(const_cast<char *>(begin), const_cast<char *>(end)); + return true; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, std::size_t &size, std::size_t &page_size) +{ + long const p(sysconf(_SC_PAGE_SIZE)); + if (0 >= p) + return nullptr; + std::size_t s(0); + for (std::size_t b : blocks) + s += (b + p - 1) / p; + s *= p; + if (!s) + return nullptr; +#if defined(SDLMAME_BSD) || defined(SDLMAME_MACOSX) || defined(SDLMAME_EMSCRIPTEN) + int const fd(-1); +#else + // TODO: portable applications are supposed to use -1 for anonymous mappings - detect whatever requires 0 specifically + int const fd(0); +#endif + void *const result(mmap(nullptr, s, PROT_NONE, MAP_ANON | MAP_SHARED, fd, 0)); + if (result == (void *)-1) + return nullptr; + size = s; + page_size = p; + return result; +} + +void virtual_memory_allocation::do_free(void *start, std::size_t size) +{ + munmap(reinterpret_cast<char *>(start), size); +} + +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +{ + int prot((NONE == access) ? PROT_NONE : 0); + if (access & READ) + prot |= PROT_READ; + if (access & WRITE) + prot |= PROT_WRITE; + if (access & EXECUTE) + prot |= PROT_EXEC; + return mprotect(reinterpret_cast<char *>(start), size, prot) == 0; +} + + dynamic_module::ptr dynamic_module::open(std::vector<std::string> &&names) { - return std::make_unique<dynamic_module_posix_impl>(names); + return std::make_unique<dynamic_module_posix_impl>(std::move(names)); } } // namespace osd diff --git a/src/osd/modules/lib/osdlib_uwp.cpp b/src/osd/modules/lib/osdlib_uwp.cpp index 680594d9cc8..f9a3e6a5cf9 100644 --- a/src/osd/modules/lib/osdlib_uwp.cpp +++ b/src/osd/modules/lib/osdlib_uwp.cpp @@ -8,30 +8,27 @@ // //============================================================ -#include <windows.h> -#include <mmsystem.h> - -#include <cstdlib> - -#include <cstdio> -#include <memory> - // MAME headers #include "osdlib.h" #include "osdcomm.h" #include "osdcore.h" #include "strconv.h" +#include <cstdio> +#include <cstdlib> +#include <map> +#include <memory> + #include <windows.h> +#include <memoryapi.h> + #include <wrl\client.h> -#include "strconv.h" using namespace Platform; using namespace Windows::ApplicationModel::DataTransfer; using namespace Windows::Foundation; -#include <map> //============================================================ // GLOBAL VARIABLES @@ -89,30 +86,6 @@ void osd_process_kill() } //============================================================ -// osd_alloc_executable -// -// allocates "size" bytes of executable memory. this must take -// things like NX support into account. -//============================================================ - -void *osd_alloc_executable(size_t size) -{ - return nullptr; -} - - -//============================================================ -// osd_free_executable -// -// frees memory allocated with osd_alloc_executable -//============================================================ - -void osd_free_executable(void *ptr, size_t size) -{ -} - - -//============================================================ // osd_break_into_debugger //============================================================ @@ -188,3 +161,47 @@ int osd_getpid(void) return GetCurrentProcessId(); } + +namespace osd { + +bool invalidate_instruction_cache(void const *start, std::size_t size) +{ + return FlushInstructionCache(GetCurrentProcess(), start, size) != 0; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, std::size_t &size, std::size_t &page_size) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + SIZE_T s(0); + for (std::size_t b : blocks) + s += (b + info.dwPageSize - 1) / info.dwPageSize; + s *= info.dwPageSize; + if (!s) + return nullptr; + LPVOID const result(VirtualAllocFromApp(nullptr, s, MEM_COMMIT, PAGE_NOACCESS)); + if (result) + { + size = s; + page_size = info.dwPageSize; + } + return result; +} + +void virtual_memory_allocation::do_free(void *start, std::size_t size) +{ + VirtualFree(start, 0, MEM_RELEASE); +} + +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +{ + ULONG p, o; + if (access & EXECUTE) + p = (access & WRITE) ? PAGE_EXECUTE_READWRITE : (access & READ) ? PAGE_EXECUTE_READ : PAGE_EXECUTE; + else + p = (access & WRITE) ? PAGE_READWRITE : (access & READ) ? PAGE_READONLY : PAGE_NOACCESS; + return VirtualProtectFromApp(start, size, p, &o) != 0; +} + +} // namespace osd diff --git a/src/osd/modules/lib/osdlib_win32.cpp b/src/osd/modules/lib/osdlib_win32.cpp index b1051dad7a6..768b71463d2 100644 --- a/src/osd/modules/lib/osdlib_win32.cpp +++ b/src/osd/modules/lib/osdlib_win32.cpp @@ -8,17 +8,6 @@ // //============================================================ -#include <windows.h> -#include <mmsystem.h> - -#include <cstdlib> -#ifndef _MSC_VER -#include <unistd.h> -#endif - -#include <cstdio> -#include <memory> - // MAME headers #include "osdlib.h" #include "osdcomm.h" @@ -29,6 +18,17 @@ #include "winutf8.h" #endif +#include <cstdio> +#include <cstdlib> + +#include <windows.h> +#include <memoryapi.h> + +#ifndef _MSC_VER +#include <unistd.h> +#endif + + //============================================================ // GLOBAL VARIABLES //============================================================ @@ -82,30 +82,6 @@ void osd_process_kill() TerminateProcess(GetCurrentProcess(), -1); } -//============================================================ -// osd_alloc_executable -// -// allocates "size" bytes of executable memory. this must take -// things like NX support into account. -//============================================================ - -void *osd_alloc_executable(size_t size) -{ - return VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); -} - - -//============================================================ -// osd_free_executable -// -// frees memory allocated with osd_alloc_executable -//============================================================ - -void osd_free_executable(void *ptr, size_t size) -{ - VirtualFree(ptr, 0, MEM_RELEASE); -} - //============================================================ // osd_break_into_debugger @@ -228,20 +204,21 @@ int osd_getpid() #endif namespace osd { + +namespace { + class dynamic_module_win32_impl : public dynamic_module { public: - dynamic_module_win32_impl(std::vector<std::string> &libraries) - : m_module(nullptr) + dynamic_module_win32_impl(std::vector<std::string> &&libraries) : m_libraries(std::move(libraries)) { - m_libraries = libraries; } virtual ~dynamic_module_win32_impl() override { - if (m_module != nullptr) + if (m_module) FreeLibrary(m_module); - }; + } protected: virtual generic_fptr_t get_symbol_address(char const *symbol) override @@ -251,20 +228,18 @@ protected: * one of them, all additional symbols will be loaded from the same library */ if (m_module) - { return reinterpret_cast<generic_fptr_t>(GetProcAddress(m_module, symbol)); - } for (auto const &library : m_libraries) { - osd::text::tstring tempstr = osd::text::to_tstring(library); - HMODULE module = load_library(tempstr.c_str()); + osd::text::tstring const tempstr = osd::text::to_tstring(library); + HMODULE const module = load_library(tempstr.c_str()); - if (module != nullptr) + if (module) { - auto function = reinterpret_cast<generic_fptr_t>(GetProcAddress(module, symbol)); + auto const function = reinterpret_cast<generic_fptr_t>(GetProcAddress(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -281,12 +256,56 @@ protected: private: std::vector<std::string> m_libraries; - HMODULE m_module; + HMODULE m_module = nullptr; }; +} // anonymous namespace + + +bool invalidate_instruction_cache(void const *start, std::size_t size) +{ + return FlushInstructionCache(GetCurrentProcess(), start, size) != 0; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, std::size_t &size, std::size_t &page_size) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + SIZE_T s(0); + for (std::size_t b : blocks) + s += (b + info.dwPageSize - 1) / info.dwPageSize; + s *= info.dwPageSize; + if (!s) + return nullptr; + LPVOID const result(VirtualAlloc(nullptr, s, MEM_COMMIT, PAGE_NOACCESS)); + if (result) + { + size = s; + page_size = info.dwPageSize; + } + return result; +} + +void virtual_memory_allocation::do_free(void *start, std::size_t size) +{ + VirtualFree(start, 0, MEM_RELEASE); +} + +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +{ + DWORD p, o; + if (access & EXECUTE) + p = (access & WRITE) ? PAGE_EXECUTE_READWRITE : (access & READ) ? PAGE_EXECUTE_READ : PAGE_EXECUTE; + else + p = (access & WRITE) ? PAGE_READWRITE : (access & READ) ? PAGE_READONLY : PAGE_NOACCESS; + return VirtualProtect(start, size, p, &o) != 0; +} + + dynamic_module::ptr dynamic_module::open(std::vector<std::string> &&names) { - return std::make_unique<dynamic_module_win32_impl>(names); + return std::make_unique<dynamic_module_win32_impl>(std::move(names)); } } // namespace osd diff --git a/src/osd/osdcore.h b/src/osd/osdcore.h index dd6ab5837d5..a7f652470db 100644 --- a/src/osd/osdcore.h +++ b/src/osd/osdcore.h @@ -342,27 +342,6 @@ void osd_work_item_release(osd_work_item *item); MISCELLANEOUS INTERFACES ***************************************************************************/ -/// \brief Allocate memory that can contain executable code -/// -/// Allocated memory must be both writable and executable. Allocated -/// memory must be freed by calling #osd_free_executable passing the -/// same size. -/// \param [in] size Number of bytes to allocate. -/// \return Pointer to allocated memory, or nullptr if allocation -/// failed. -/// \sa osd_free_executable -void *osd_alloc_executable(size_t size); - - -/// \brief Free memory allocated by osd_alloc_executable -/// -/// \param [in] ptr Pointer returned by #osd_alloc_executable. -/// \param [in] size Number of bytes originally requested. Must match -/// the value passed to #osd_alloc_executable. -/// \sa osd_alloc_executable -void osd_free_executable(void *ptr, size_t size); - - /// \brief Break into host debugger if attached /// /// This function is called when a fatal error occurs. If a debugger is |