diff options
author | 2025-01-14 03:59:02 +1100 | |
---|---|---|
committer | 2025-01-14 03:59:02 +1100 | |
commit | f39baa51dc03397212dcfebd751bce2d438391ac (patch) | |
tree | d086035d235efbfbbdb71f1a4686507d235610c6 /src/lib/util/delegate.cpp | |
parent | e0b1e2356e9c0c8ca96be6dea5be59c561ed4903 (diff) |
-util/mfpresolve.cpp: Moved member function resolution code to a common location.
* util/delegate.cpp, cpu/drcbex64.cpp, cpu/drcbearm64.cpp: Use common member function resolution code.
-cpu/drcbearm64.cpp, cpu/drcbex64.cpp, cpu/drcbex86.cpp: Moved some stuff to anonymous namespaces.
-cpu/drcbex64.cpp, cpu/drcbex86.cpp: Defer allocating labels if they aren't always needed.
Diffstat (limited to 'src/lib/util/delegate.cpp')
-rw-r--r-- | src/lib/util/delegate.cpp | 208 |
1 files changed, 8 insertions, 200 deletions
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp index 8949f90f146..67fb7e72d42 100644 --- a/src/lib/util/delegate.cpp +++ b/src/lib/util/delegate.cpp @@ -10,6 +10,8 @@ #include "delegate.h" +#include "mfpresolve.h" + #include <cstdio> #include <sstream> @@ -70,31 +72,9 @@ const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const { - // apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant - LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta)); - object = reinterpret_cast<delegate_generic_class *>( - reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0))); - LOG("Calculated this=%p ", reinterpret_cast<void const *>(object)); - - // test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant - if ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? !(m_this_delta & 1) : !(m_function & 1)) - { - // conventional function pointer - LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function)); - return reinterpret_cast<delegate_generic_function>(m_function); - } - else - { - // byte index into the vtable to the function - std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1); - delegate_generic_function result; - if (MAME_ABI_CXX_VTABLE_FNDESC) - result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr)); - else - result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr); - LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(result)); - return result; - } + auto const [entrypoint, adjusted] = detail::resolve_member_function_itanium(m_function, m_this_delta, object); + object = reinterpret_cast<delegate_generic_class *>(adjusted); + return reinterpret_cast<delegate_generic_function>(entrypoint); } @@ -107,181 +87,9 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const { - LOG("Input this=%p ", reinterpret_cast<void const *>(object)); - if (sizeof(single_base_equiv) < m_size) - LOG("thisdelta=%d ", m_this_delta); - if (sizeof(unknown_base_equiv) == m_size) - LOG("vptrdelta=%d vindex=%d ", m_vptr_offs, m_vt_index); - std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object); - - // test for pointer to member function cast across virtual inheritance relationship - if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index) - { - // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable - byteptr += m_vptr_offs; - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr); - byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index); - } - - // add "this" pointer displacement if present in the pointer to member function - if (sizeof(single_base_equiv) < m_size) - byteptr += m_this_delta; - LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr)); - object = reinterpret_cast<delegate_generic_class *>(byteptr); - - // walk past recognisable thunks -#if defined(__x86_64__) || defined(_M_X64) - std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(m_function); - while (true) - { - // Assumes Windows calling convention, and doesn't consider that - // the "this" pointer could be in RDX if RCX is a pointer to - // space for an oversize scalar result. Since the result area - // is uninitialised on entry, you won't see something that looks - // like a vtable dispatch through RCX in this case - it won't - // behave badly, it just won't bypass virtual call thunks in the - // rare situations where the return type is an oversize scalar. - if (0xe9 == func[0]) - { - // relative jump with 32-bit displacement (typically a resolved PLT entry) - LOG("Found relative jump at %p ", func); - func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1); - LOG("redirecting to %p\n", func); - continue; - } - else if ((0x48 == func[0]) && (0x8b == func[1]) && (0x01 == func[2])) - { - if ((0xff == func[3]) && ((0x20 == func[4]) || (0x60 == func[4]) || (0xa0 == func[4]))) - { - // MSVC virtual function call thunk - mov rax,QWORD PTR [rcx] ; jmp QWORD PTR [rax+...] - LOG("Found virtual member function thunk at %p ", func); - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object); - if (0x20 == func[4]) // no displacement - func = *reinterpret_cast<std::uint8_t const *const *>(vptr); - else if (0x60 == func[4]) // 8-bit displacement - func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int8_t const *>(func + 5)); - else // 32-bit displacement - func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int32_t const *>(func + 5)); - LOG("redirecting to %p\n", func); - continue; - } - else if ((0x48 == func[3]) && (0x8b == func[4])) - { - // clang virtual function call thunk - mov rax,QWORD PTR [rcx] ; mov rax,QWORD PTR [rax+...] ; jmp rax - if ((0x00 == func[5]) && (0x48 == func[6]) && (0xff == func[7]) && (0xe0 == func[8])) - { - // no displacement - LOG("Found virtual member function thunk at %p ", func); - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object); - func = *reinterpret_cast<std::uint8_t const *const *>(vptr); - LOG("redirecting to %p\n", func); - continue; - } - else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9])) - { - // 8-bit displacement - LOG("Found virtual member function thunk at %p ", func); - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object); - func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int8_t const *>(func + 6)); - LOG("redirecting to %p\n", func); - continue; - } - else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12])) - { - // 32-bit displacement - LOG("Found virtual member function thunk at %p ", func); - std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object); - func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int32_t const *>(func + 6)); - LOG("redirecting to %p\n", func); - continue; - } - } - } - - // clang uses unoptimised thunks if optimisation is disabled - // Without optimisation, clang produces thunks like: - // 50 push rax - // 48 89 0c 24 mov QWORD PTR [rsp],rcx - // 48 8b 0c 24 mov rcx,QWORD PTR [rsp] - // 48 8b 01 mov rax,QWORD PTR [rcx] - // 48 8b 80 xx xx xx xx mov rax,QWORD PTR [rax+...] - // 41 5a pop r10 - // 48 ff e0 jmp rax - // Trying to decode these thunks likely isn't worth the effort. - // Chasing performance in unoptimised builds isn't very useful, - // and the format of these thunks may be fragile. - - // not something we can easily bypass - break; - } - return reinterpret_cast<delegate_generic_function>(std::uintptr_t(func)); -#elif defined(__aarch64__) || defined(_M_ARM64) - std::uint32_t const *func = reinterpret_cast<std::uint32_t const *>(m_function); - while (true) - { - // Assumes little Endian mode. Instructions are always stored - // in little Endian format on AArch64, so if big Endian mode is - // to be supported, the values need to be swapped. - if ((0x90000010 == (func[0] & 0x9f00001f)) && (0x91000210 == (func[1] & 0xffc003ff)) && (0xd61f0200 == func[2])) - { - // page-relative jump with +/-4GB reach - adrp xip0,... ; add xip0,xip0,#... ; br xip0 - LOG("Found page-relative jump at %p ", func); - std::int64_t const page = - (std::uint64_t(func[0] & 0x60000000) >> 17) | - (std::uint64_t(func[0] & 0x00ffffe0) << 9) | - ((func[0] & 0x00800000) ? (~std::uint64_t(0) << 33) : 0); - std::uint32_t const offset = (func[1] & 0x003ffc00) >> 10; - func = reinterpret_cast<std::uint32_t const *>(((std::uintptr_t(func) + page) & (~std::uintptr_t(0) << 12)) + offset); - LOG("redirecting to %p\n", func); - } - else if ((0xf9400010 == func[0]) && (0xf9400210 == (func[1] & 0xffc003ff)) && (0xd61f0200 == func[2])) - { - // virtual function call thunk - ldr xip0,[x0] ; ldr xip0,[x0,#...] ; br xip0 - LOG("Found virtual member function thunk at %p ", func); - std::uint32_t const *const *const vptr = *reinterpret_cast<std::uint32_t const *const *const *>(object); - func = vptr[(func[1] & 0x003ffc00) >> 10]; - LOG("redirecting to %p\n", func); - } - else - { - // not something we can easily bypass - break; - } - - // clang uses horribly sub-optimal thunks for AArch64 - // Without optimisation, clang produces thunks like: - // d10143ff sub sp,sp,#80 - // f90027e7 str x7,[sp,#72] - // f90023e6 str x6,[sp,#64] - // f9001fe5 str x5,[sp,#56] - // f9001be4 str x4,[sp,#48] - // f90017e3 str x3,[sp,#40] - // f90013e2 str x2,[sp,#32] - // f9000fe1 str x1,[sp,#24] - // f90007e0 str x0,[sp,#8] - // f94007e0 ldr x0,[sp,#8] - // f9400009 ldr x9,[x0] - // f9400129 ldr x9,[x9,#...] - // 910143ff add sp,sp,#80 - // d61f0120 br x9 - // With optimisation, clang produces thunks like: - // d10103ff sub sp,sp,#64 - // a9008be1 stp x1,x2,[sp,#8] - // a90193e3 stp x3,x4,[sp,#24] - // a9029be5 stp x5,x6,[sp,#40] - // f9001fe7 str x7,[sp,#56] - // f9400009 ldr x9,[x0] - // f9400129 ldr x9,[x9,#...] - // 910103ff add sp,sp,#64 - // d61f0120 br x9 - // It's more effort than it's worth to try decoding these - // thunks. - - } - return reinterpret_cast<delegate_generic_function>(std::uintptr_t(func)); -#else - return reinterpret_cast<delegate_generic_function>(m_function); -#endif + auto const [entrypoint, adjusted] = detail::resolve_member_function_msvc(&m_function, m_size, object); + object = reinterpret_cast<delegate_generic_class *>(adjusted); + return reinterpret_cast<delegate_generic_function>(entrypoint); } } // namespace util::detail |