diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2023-01-04 12:14:22 +0100 |
---|---|---|
committer | Stephan Bergmann <sbergman@redhat.com> | 2023-01-04 13:49:58 +0000 |
commit | ef533553559fe09b4afab651fc692885d1acf4ed (patch) | |
tree | bbd17cba9c5253ddbe62250efa63c3d169156180 /bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx | |
parent | 146eb1aa750a7612deb9d3e1825a7a2e1256bd4d (diff) |
Rudimentary support for dynamic_cast on UNO proxy objects
<https://gerrit.libreoffice.org/c/core/+/144139> "New loplugin:unocast" had
argued that uses of dynamic_cast from a UNO interface type are broken in general
(because if the source object is a proxy from the C*+ UNO bridge, its vtable's
RTTI slot will normally not be set up, which can cause a crash), and should be
replaced with uses of XUnoTunnel. Which the various recent "loplugin:unocast
(...)" commits started to do. However, it became clear that that is not the
most ideal way forward: For one, getting more and more implementations of
XUnoTunnel::getSomething into existing class hierarchies is error prone, as each
such implementation must manually delegate to all its base class
implementations. For another, uses of comphelper::getFromUnoTunnel (which often
needs to do a queryInterface to XUnoTunnel first) are easily more expensive than
uses of dynamic_cast.
Thanks to Noel, the insight here is that for the use case of a dynamic_cast from
a UNO interface type to a local C++ class type, and if the source object is a
proxy, it is sufficient that the dynamic_cast will not crash. It will
necessarily always return null (as the proxy will never be the implementation of
a local C++ class type), so it is sufficient to fill the RTTI slots of the
proxies' vtables with dummy values. That avoids having to set up proper RTTI
for those potentially multiple-inheritance proxy types. (And with this in
place, all those recent "loplugin:unocast (...)" commits can be reverted again
in a next step.)
I verified the changes for the gcc3_linux_aarch64 (on macOS), gcc3_linux_intel,
gcc3_linux_x86-64, gcc3_macosx_x86-64, msvc_win32_intel, and msvc_win32_x86-64
bridges. The changes for all the other bridges were done blindly.
(For gcc3_linux_x86-64, which already conditionally supported proper RTTI for
UBSan, setting the offset-to-top slot to non-zero had to be made conditional
too, as the dummy ProxyRtti will always pretend to be a full class rather than a
potential base class that could have a non-zero offset-to-top value. For
msvc_win32_*, it turned out that the existing code to set up dummy XInterface
RTTI (which was there for reasons lost to history) was broken.)
Change-Id: Iec4b8067d26b14b6fb02c2fdd15e1eee20919590
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145038
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Diffstat (limited to 'bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx')
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx | 113 |
1 files changed, 100 insertions, 13 deletions
diff --git a/bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx b/bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx index cfefa60e748a..fc956121dc07 100644 --- a/bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx +++ b/bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx @@ -24,6 +24,8 @@ #include <cstddef> #include <cstdlib> #include <cstring> +#include <limits> +#include <typeinfo> #include <com/sun/star/uno/XInterface.hpp> #include <com/sun/star/uno/genfunc.hxx> @@ -42,6 +44,8 @@ #include "abi.hxx" +extern "C" IMAGE_DOS_HEADER const __ImageBase; + extern "C" void vtableSlotCall(); using namespace ::com::sun::star; @@ -357,27 +361,110 @@ std::size_t bridges::cpp_uno::shared::VtableFactory::getBlockSize(sal_Int32 slot return (slotCount + 1) * sizeof(Slot) + slotCount * codeSnippetSize; } +static sal_uInt32 imageRelative(void const* p) +{ + assert(reinterpret_cast<sal_uIntPtr>(p) >= reinterpret_cast<sal_uIntPtr>(&__ImageBase) + && reinterpret_cast<sal_uIntPtr>(p) - reinterpret_cast<sal_uIntPtr>(&__ImageBase) + <= std::numeric_limits<sal_uInt32>::max()); + return reinterpret_cast<sal_uIntPtr>(p) - reinterpret_cast<sal_uIntPtr>(&__ImageBase); +} + +namespace +{ +// Some dummy type whose RTTI is used in the synthesized proxy vtables to make uses of dynamic_cast +// on such proxy objects not crash: +struct ProxyRtti +{ +}; + +// The following vtable RTTI data is based on how the code at +// <https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/MicrosoftCXXABI.cpp> computes +// such data, and on how <https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483> +// "Accessing the current module’s HINSTANCE from a static library" obtians __ImageBase: + +struct RttiClassHierarchyDescriptor; + +#pragma warning(push) +#pragma warning(disable : 4324) // "structure was padded due to alignment specifier" + +struct alignas(16) RttiBaseClassDescriptor +{ + sal_uInt32 n0 = imageRelative(&typeid(ProxyRtti)); + sal_uInt32 n1 = 0; + sal_uInt32 n2 = 0; + sal_uInt32 n3 = 0xFFFFFFFF; + sal_uInt32 n4 = 0; + sal_uInt32 n5 = 0x40; + sal_uInt32 n6; + RttiBaseClassDescriptor(RttiClassHierarchyDescriptor const* chd) + : n6(imageRelative(chd)) + { + } +}; + +struct alignas(4) RttiBaseClassArray +{ + sal_uInt32 n0; + sal_uInt32 n1 = 0; + RttiBaseClassArray(RttiBaseClassDescriptor const* bcd) + : n0(imageRelative(bcd)) + { + } +}; + +struct alignas(8) RttiClassHierarchyDescriptor +{ + sal_uInt32 n0 = 0; + sal_uInt32 n1 = 0; + sal_uInt32 n2 = 1; + sal_uInt32 n3; + RttiClassHierarchyDescriptor(RttiBaseClassArray const* bca) + : n3(imageRelative(bca)) + { + } +}; + +struct alignas(16) RttiCompleteObjectLocator +{ + sal_uInt32 n0 = 1; + sal_uInt32 n1 = 0; + sal_uInt32 n2 = 0; + sal_uInt32 n3 = imageRelative(&typeid(ProxyRtti)); + sal_uInt32 n4; + sal_uInt32 n5 = imageRelative(this); + RttiCompleteObjectLocator(RttiClassHierarchyDescriptor const* chd) + : n4(imageRelative(chd)) + { + } +}; + +struct Rtti +{ + RttiBaseClassDescriptor bcd; + RttiBaseClassArray bca; + RttiClassHierarchyDescriptor chd; + RttiCompleteObjectLocator col; + Rtti() + : bcd(&chd) + , bca(&bcd) + , chd(&bca) + , col(&chd) + { + } +}; + +#pragma warning(pop) +} + bridges::cpp_uno::shared::VtableFactory::Slot* bridges::cpp_uno::shared::VtableFactory::initializeBlock(void* block, sal_Int32 slotCount, sal_Int32, typelib_InterfaceTypeDescription*) { - struct Rtti - { - sal_Int32 n0, n1, n2; - type_info* rtti; - Rtti() - : n0(0) - , n1(0) - , n2(0) - , rtti(RTTInfos::get("com.sun.star.uno.XInterface")) - { - } - }; static Rtti rtti; Slot* slots = mapBlockToVtable(block); - slots[-1].fn = &rtti; + slots[-1].fn = &rtti.col; return slots + slotCount; } |