summaryrefslogtreecommitdiff
path: root/bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2023-01-04 12:14:22 +0100
committerStephan Bergmann <sbergman@redhat.com>2023-01-04 13:49:58 +0000
commitef533553559fe09b4afab651fc692885d1acf4ed (patch)
treebbd17cba9c5253ddbe62250efa63c3d169156180 /bridges/source/cpp_uno/msvc_win32_arm64/cpp2uno.cxx
parent146eb1aa750a7612deb9d3e1825a7a2e1256bd4d (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.cxx113
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;
}