From 4eca05fe67e6a61a30af9d168590f3f3aaf9f639 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Wed, 6 Jan 2021 01:25:58 +1100 Subject: cpu: Allow recompilers to work with W^X policy --- scripts/resources/windows/mame/mame.man | 2 +- scripts/resources/windows/mess/mess.man | 2 +- src/devices/cpu/drcbeut.cpp | 2 +- src/devices/cpu/drccache.cpp | 155 +++++++++++++++++++------------- src/devices/cpu/drccache.h | 50 ++++++----- src/devices/cpu/drcuml.cpp | 2 + src/osd/modules/lib/osdlib.h | 88 +++++++++++++++++- src/osd/modules/lib/osdlib_macosx.cpp | 131 ++++++++++++++------------- src/osd/modules/lib/osdlib_unix.cpp | 134 +++++++++++++++------------ src/osd/modules/lib/osdlib_uwp.cpp | 85 +++++++++++------- src/osd/modules/lib/osdlib_win32.cpp | 117 ++++++++++++++---------- src/osd/osdcore.h | 21 ----- 12 files changed, 476 insertions(+), 313 deletions(-) diff --git a/scripts/resources/windows/mame/mame.man b/scripts/resources/windows/mame/mame.man index 85e0f31644e..cd2705c8886 100644 --- a/scripts/resources/windows/mame/mame.man +++ b/scripts/resources/windows/mame/mame.man @@ -1,5 +1,5 @@ - + MAME diff --git a/scripts/resources/windows/mess/mess.man b/scripts/resources/windows/mess/mess.man index 4856b147be1..d03a874f8fb 100644 --- a/scripts/resources/windows/mess/mess.man +++ b/scripts/resources/windows/mess/mess.man @@ -1,5 +1,5 @@ - + MESS diff --git a/src/devices/cpu/drcbeut.cpp b/src/devices/cpu/drcbeut.cpp index bf49969ca06..ce7cf527e3c 100644 --- a/src/devices/cpu/drcbeut.cpp +++ b/src/devices/cpu/drcbeut.cpp @@ -501,7 +501,7 @@ drccodeptr drc_label_list::get_codeptr(uml::code_label label, drc_label_fixup_de label_fixup *fixup = reinterpret_cast(m_cache.alloc(sizeof(*fixup))); new (fixup) label_fixup{ nullptr, curlabel, callback }; m_fixup_list.append(*fixup); - m_cache.request_oob_codegen(m_oob_callback_delegate, fixup, param); + m_cache.request_oob_codegen(drc_oob_delegate(m_oob_callback_delegate), fixup, param); } return curlabel->m_codeptr; diff --git a/src/devices/cpu/drccache.cpp b/src/devices/cpu/drccache.cpp index fc86fd37cc6..4a41cdcddb0 100644 --- a/src/devices/cpu/drccache.cpp +++ b/src/devices/cpu/drccache.cpp @@ -11,15 +11,22 @@ #include "emu.h" #include "drccache.h" +#include -//************************************************************************** -// MACROS -//************************************************************************** +namespace { -// ensure that all memory allocated is aligned to an 8-byte boundary -#define ALIGN_PTR_UP(p) ((void *)(((uintptr_t)(p) + (CACHE_ALIGNMENT - 1)) & ~(CACHE_ALIGNMENT - 1))) -#define ALIGN_PTR_DOWN(p) ((void *)((uintptr_t)(p) & ~(CACHE_ALIGNMENT - 1))) +template constexpr T *ALIGN_PTR_UP(T *p, U align) +{ + return reinterpret_cast((uintptr_t(p) + (align - 1)) & ~uintptr_t(align - 1)); +} + +template constexpr T *ALIGN_PTR_DOWN(T *p, U align) +{ + return reinterpret_cast(uintptr_t(p) & ~uintptr_t(align - 1)); +} + +} // anonymous namespace @@ -31,17 +38,27 @@ // drc_cache - constructor //------------------------------------------------- -drc_cache::drc_cache(size_t bytes) - : m_near((drccodeptr)osd_alloc_executable(bytes)), - m_neartop(m_near), - m_base(m_near + NEAR_CACHE_SIZE), - m_top(m_base), - m_end(m_near + bytes), - m_codegen(nullptr), - m_size(bytes) +drc_cache::drc_cache(size_t bytes) : + m_cache({ NEAR_CACHE_SIZE, bytes - NEAR_CACHE_SIZE }), + m_near(reinterpret_cast(m_cache.get())), + m_neartop(m_near), + m_base(ALIGN_PTR_UP(m_near + NEAR_CACHE_SIZE, m_cache.page_size())), + m_top(m_base), + m_limit(m_near + m_cache.size()), + m_end(m_limit), + m_codegen(nullptr), + m_size(m_cache.size()) { - memset(m_free, 0, sizeof(m_free)); - memset(m_nearfree, 0, sizeof(m_nearfree)); + // alignment and page size must be powers of two, cache must be page-aligned + assert(!(CACHE_ALIGNMENT & (CACHE_ALIGNMENT - 1))); + assert(!(m_cache.page_size() & (m_cache.page_size() - 1))); + assert(!(uintptr_t(m_near) & (m_cache.page_size() - 1))); + assert(m_cache.page_size() >= CACHE_ALIGNMENT); + + std::fill(std::begin(m_free), std::end(m_free), nullptr); + std::fill(std::begin(m_nearfree), std::end(m_nearfree), nullptr); + + m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE); } @@ -51,8 +68,6 @@ drc_cache::drc_cache(size_t bytes) drc_cache::~drc_cache() { - // release the memory - osd_free_executable(m_near, m_size); } @@ -64,10 +79,11 @@ drc_cache::~drc_cache() void drc_cache::flush() { // can't flush in the middle of codegen - assert(m_codegen == nullptr); + assert(!m_codegen); // just reset the top back to the base and re-seed m_top = m_base; + m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE); } @@ -83,9 +99,9 @@ void *drc_cache::alloc(size_t bytes) // pick first from the free list if (bytes < MAX_PERMANENT_ALLOC) { - free_link **linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; - free_link *link = *linkptr; - if (link != nullptr) + free_link **const linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + free_link *const link = *linkptr; + if (link) { *linkptr = link->m_next; return link; @@ -93,11 +109,13 @@ void *drc_cache::alloc(size_t bytes) } // if no space, we just fail - drccodeptr ptr = (drccodeptr)ALIGN_PTR_DOWN(m_end - bytes); - if (m_top > ptr) + drccodeptr const ptr = ALIGN_PTR_DOWN(m_end - bytes, CACHE_ALIGNMENT); + drccodeptr const limit = ALIGN_PTR_DOWN(ptr, m_cache.page_size()); + if (m_top > limit) return nullptr; // otherwise update the end of the cache + m_limit = limit; m_end = ptr; return ptr; } @@ -115,9 +133,9 @@ void *drc_cache::alloc_near(size_t bytes) // pick first from the free list if (bytes < MAX_PERMANENT_ALLOC) { - free_link **linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; - free_link *link = *linkptr; - if (link != nullptr) + free_link **const linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + free_link *const link = *linkptr; + if (link) { *linkptr = link->m_next; return link; @@ -125,8 +143,8 @@ void *drc_cache::alloc_near(size_t bytes) } // if no space, we just fail - drccodeptr ptr = (drccodeptr)ALIGN_PTR_UP(m_neartop); - if (ptr + bytes > m_base) + drccodeptr const ptr = ALIGN_PTR_UP(m_neartop, CACHE_ALIGNMENT); + if ((ptr + bytes) > m_base) return nullptr; // otherwise update the top of the near part of the cache @@ -143,15 +161,16 @@ void *drc_cache::alloc_near(size_t bytes) void *drc_cache::alloc_temporary(size_t bytes) { // can't allocate in the middle of codegen - assert(m_codegen == nullptr); + assert(!m_codegen); // if no space, we just fail - drccodeptr ptr = m_top; - if (ptr + bytes >= m_end) + drccodeptr const ptr = m_top; + if ((ptr + bytes) > m_limit) return nullptr; // otherwise, update the cache top - m_top = (drccodeptr)ALIGN_PTR_UP(ptr + bytes); + m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE); + m_top = ALIGN_PTR_UP(ptr + bytes, CACHE_ALIGNMENT); return ptr; } @@ -163,23 +182,32 @@ void *drc_cache::alloc_temporary(size_t bytes) void drc_cache::dealloc(void *memory, size_t bytes) { + drccodeptr const mem = reinterpret_cast(memory); assert(bytes < MAX_PERMANENT_ALLOC); - assert(((drccodeptr)memory >= m_near && (drccodeptr)memory < m_base) || ((drccodeptr)memory >= m_end && (drccodeptr)memory < m_near + m_size)); + assert(((mem >= m_near) && (mem < m_base)) || ((mem >= m_end) && (mem < (m_near + m_size)))); // determine which free list to add to - free_link **linkptr; - if ((drccodeptr)memory < m_base) - linkptr = &m_nearfree[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; - else - linkptr = &m_free[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; + free_link **const linkptr = &((mem < m_base) ? m_nearfree : m_free)[(bytes + CACHE_ALIGNMENT - 1) / CACHE_ALIGNMENT]; // link is into the free list for our size - free_link *link = (free_link *)memory; + free_link *const link = reinterpret_cast(memory); link->m_next = *linkptr; *linkptr = link; } +void drc_cache::codegen_init() +{ + m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE); +} + + +void drc_cache::codegen_complete() +{ + m_cache.set_access(m_base - m_near, ALIGN_PTR_UP(m_top, m_cache.page_size()) - m_base, osd::virtual_memory_allocation::READ_EXECUTE); +} + + //------------------------------------------------- // begin_codegen - begin code generation //------------------------------------------------- @@ -187,15 +215,15 @@ void drc_cache::dealloc(void *memory, size_t bytes) drccodeptr *drc_cache::begin_codegen(uint32_t reserve_bytes) { // can't restart in the middle of codegen - assert(m_codegen == nullptr); - assert(m_ooblist.first() == nullptr); + assert(!m_codegen); + assert(m_oob_list.empty()); - // if still no space, we just fail - drccodeptr ptr = m_top; - if (ptr + reserve_bytes >= m_end) + // if no space, we just fail + if ((m_top + reserve_bytes) > m_limit) return nullptr; // otherwise, return a pointer to the cache top + m_cache.set_access(0, m_size, osd::virtual_memory_allocation::READ_WRITE); m_codegen = m_top; return &m_top; } @@ -207,23 +235,22 @@ drccodeptr *drc_cache::begin_codegen(uint32_t reserve_bytes) drccodeptr drc_cache::end_codegen() { - drccodeptr result = m_codegen; + drccodeptr const result = m_codegen; // run the OOB handlers - oob_handler *oob; - while ((oob = m_ooblist.detach_head()) != nullptr) + while (!m_oob_list.empty()) { // call the callback - oob->m_callback(&m_top, oob->m_param1, oob->m_param2); - assert(m_top - m_codegen < CODEGEN_MAX_BYTES); + m_oob_list.front().m_callback(&m_top, m_oob_list.front().m_param1, m_oob_list.front().m_param2); + assert((m_top - m_codegen) < CODEGEN_MAX_BYTES); - // release our memory - oob->~oob_handler(); - dealloc(oob, sizeof(*oob)); + // add it to the free list + m_oob_free.splice(m_oob_free.begin(), m_oob_list, m_oob_list.begin()); } // update the cache top - m_top = (drccodeptr)ALIGN_PTR_UP(m_top); + osd::invalidate_instruction_cache(m_codegen, m_top - m_codegen); + m_top = ALIGN_PTR_UP(m_top, CACHE_ALIGNMENT); m_codegen = nullptr; return result; @@ -235,20 +262,24 @@ drccodeptr drc_cache::end_codegen() // out-of-band codegen //------------------------------------------------- -void drc_cache::request_oob_codegen(drc_oob_delegate callback, void *param1, void *param2) +void drc_cache::request_oob_codegen(drc_oob_delegate &&callback, void *param1, void *param2) { - assert(m_codegen != nullptr); + assert(m_codegen); // pull an item from the free list - oob_handler *oob = (oob_handler *)alloc(sizeof(*oob)); - assert(oob != nullptr); - new (oob) oob_handler(); + std::list::iterator oob; + if (m_oob_free.empty()) + { + oob = m_oob_list.emplace(m_oob_list.end()); + } + else + { + oob = m_oob_free.begin(); + m_oob_list.splice(m_oob_list.end(), m_oob_free, oob); + } // fill it in oob->m_callback = std::move(callback); oob->m_param1 = param1; oob->m_param2 = param2; - - // add to the tail - m_ooblist.append(*oob); } diff --git a/src/devices/cpu/drccache.h b/src/devices/cpu/drccache.h index bbbcf62af63..07b0f847b81 100644 --- a/src/devices/cpu/drccache.h +++ b/src/devices/cpu/drccache.h @@ -7,12 +7,12 @@ Universal dynamic recompiler cache management. ***************************************************************************/ +#ifndef MAME_CPU_DRCCACHE_H +#define MAME_CPU_DRCCACHE_H #pragma once -#ifndef __DRCCACHE_H__ -#define __DRCCACHE_H__ - +#include "modules/lib/osdlib.h" //************************************************************************** @@ -63,43 +63,46 @@ public: void dealloc(void *memory, size_t bytes); // codegen helpers + void codegen_init(); + void codegen_complete(); drccodeptr *begin_codegen(uint32_t reserve_bytes); drccodeptr end_codegen(); - void request_oob_codegen(drc_oob_delegate callback, void *param1 = nullptr, void *param2 = nullptr); + void request_oob_codegen(drc_oob_delegate &&callback, void *param1 = nullptr, void *param2 = nullptr); private: // largest block of code that can be generated at once - static const size_t CODEGEN_MAX_BYTES = 131072; + static constexpr size_t CODEGEN_MAX_BYTES = 131072; // minimum alignment, in bytes (must be power of 2) - static const size_t CACHE_ALIGNMENT = alignof(std::max_align_t); + static constexpr size_t CACHE_ALIGNMENT = alignof(std::max_align_t); // largest permanent allocation we allow - static const size_t MAX_PERMANENT_ALLOC = 1024; + static constexpr size_t MAX_PERMANENT_ALLOC = 1024; // size of "near" area at the base of the cache - static const size_t NEAR_CACHE_SIZE = 131072; + static constexpr size_t NEAR_CACHE_SIZE = 131072; + + osd::virtual_memory_allocation m_cache; // core parameters - drccodeptr m_near; // pointer to the near part of the cache - drccodeptr m_neartop; // top of the near part of the cache - drccodeptr m_base; // base pointer to the compiler cache - drccodeptr m_top; // current top of cache - drccodeptr m_end; // end of cache memory - drccodeptr m_codegen; // start of generated code - size_t m_size; // size of the cache in bytes + drccodeptr const m_near; // pointer to the near part of the cache + drccodeptr m_neartop; // unallocated area of near cache + drccodeptr const m_base; // end of near cache + drccodeptr m_top; // end of temporary allocations and code + drccodeptr m_limit; // limit for temporary allocations and code (page-aligned) + drccodeptr m_end; // first allocated byte in cache + drccodeptr m_codegen; // start of current generated code block + size_t const m_size; // size of the cache in bytes // oob management struct oob_handler { - oob_handler *next() const { return m_next; } - - oob_handler * m_next; // next handler - drc_oob_delegate m_callback; // callback function - void * m_param1; // 1st pointer parameter - void * m_param2; // 2nd pointer parameter + drc_oob_delegate m_callback; // callback function + void * m_param1; // 1st pointer parameter + void * m_param2; // 2nd pointer parameter }; - simple_list m_ooblist; // list of oob handlers + std::list m_oob_list; // list of active oob handlers + std::list m_oob_free; // list of recyclable oob handlers // free lists struct free_link @@ -110,5 +113,4 @@ private: free_link * m_nearfree[MAX_PERMANENT_ALLOC / CACHE_ALIGNMENT]; }; - -#endif /* __DRCCACHE_H__ */ +#endif // MAME_CPU_DRCCACHE_H diff --git a/src/devices/cpu/drcuml.cpp b/src/devices/cpu/drcuml.cpp index e935913aceb..af7e97cee4c 100644 --- a/src/devices/cpu/drcuml.cpp +++ b/src/devices/cpu/drcuml.cpp @@ -348,7 +348,9 @@ void drcuml_block::end() disassemble(); // generate the code via the back-end + m_drcuml.cache().codegen_init(); m_drcuml.generate(*this, &m_inst[0], m_nextinst); + m_drcuml.cache().codegen_complete(); // block is no longer in use m_inuse = false; 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 #include #include #include @@ -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 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(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 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 std::enable_if::value, T>::type bind(char const *symbol) + typename std::enable_if_t, T> bind(char const *symbol) { return reinterpret_cast(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 -#include -#include -#include -#include -#include -#include +// MAME headers +#include "osdcore.h" +#include "osdlib.h" +#include #include +#include #include #include +#include +#include +#include +#include +#include #include #include #include -// MAME headers -#include "osdcore.h" -#include "osdlib.h" //============================================================ // osd_getenv @@ -56,42 +56,6 @@ void osd_process_kill() kill(getpid(), SIGKILL); } -//============================================================ -// 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 &libraries) - : m_module(nullptr) + dynamic_module_posix_impl(std::vector &&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(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(dlsym(module, symbol)); + generic_fptr_t const function = reinterpret_cast(dlsym(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -259,12 +219,61 @@ protected: private: std::vector 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(start)); + char const *const end(begin + size); + __builtin___clear_cache(const_cast(begin), const_cast(end)); + return true; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list 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 &&names) { - return std::make_unique(names); + return std::make_unique(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 -#include -#include -#include -#include -#include +// MAME headers +#include "osdcore.h" +#include "osdlib.h" +#include + +#include #include +#include #include #include +#include +#include +#include +#include -// MAME headers -#include "osdcore.h" -#include "osdlib.h" - -#include //============================================================ // osd_getenv @@ -53,37 +53,6 @@ void osd_process_kill() kill(getpid(), SIGKILL); } -//============================================================ -// 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 &libraries) - : m_module(nullptr) + dynamic_module_posix_impl(std::vector &&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(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(dlsym(module, symbol)); + generic_fptr_t const function = reinterpret_cast(dlsym(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -190,12 +155,67 @@ protected: private: std::vector 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(start)); + char const *const end(begin + size); + __builtin___clear_cache(const_cast(begin), const_cast(end)); + return true; +} + + +void *virtual_memory_allocation::do_alloc(std::initializer_list 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(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(start), size, prot) == 0; +} + + dynamic_module::ptr dynamic_module::open(std::vector &&names) { - return std::make_unique(names); + return std::make_unique(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 -#include - -#include - -#include -#include - // MAME headers #include "osdlib.h" #include "osdcomm.h" #include "osdcore.h" #include "strconv.h" +#include +#include +#include +#include + #include +#include + #include -#include "strconv.h" using namespace Platform; using namespace Windows::ApplicationModel::DataTransfer; using namespace Windows::Foundation; -#include //============================================================ // GLOBAL VARIABLES @@ -88,30 +85,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 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 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 -#include - -#include -#ifndef _MSC_VER -#include -#endif - -#include -#include - // MAME headers #include "osdlib.h" #include "osdcomm.h" @@ -29,6 +18,17 @@ #include "winutf8.h" #endif +#include +#include + +#include +#include + +#ifndef _MSC_VER +#include +#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 &libraries) - : m_module(nullptr) + dynamic_module_win32_impl(std::vector &&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(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(GetProcAddress(module, symbol)); + auto const function = reinterpret_cast(GetProcAddress(module, symbol)); - if (function != nullptr) + if (function) { m_module = module; return function; @@ -281,12 +256,56 @@ protected: private: std::vector 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 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 &&names) { - return std::make_unique(names); + return std::make_unique(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 -- cgit v1.2.3