This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch AOO42X in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 7d4e17cca1c1cfb281c3214250cde7c667ad6d43 Author: Jim Jagielski <[email protected]> AuthorDate: Mon Aug 3 14:36:56 2026 -0400 bridges: add arm64 RTTI synthesis QA harnesses (cherry picked from commit 4ca7e75a8a392f3dadfa2bcceac9342116803c9d) --- .../qa/keyless_rtti_visibility_demo.cxx | 19 ++++ .../qa/rtti_crossdylib_test/catcher.cxx | 10 ++ .../qa/rtti_crossdylib_test/thrower.cxx | 47 ++++++++ .../qa/rtti_crossdylib_test/uno_like.hxx | 5 + .../qa/rtti_synthesis_test.cxx | 120 +++++++++++++++++++++ 5 files changed, 201 insertions(+) diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/keyless_rtti_visibility_demo.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/keyless_rtti_visibility_demo.cxx new file mode 100644 index 0000000000..b066071014 --- /dev/null +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/keyless_rtti_visibility_demo.cxx @@ -0,0 +1,19 @@ +// Demonstrates why arm64 Darwin needs the RTTI synthesis in except.cxx: a +// class with no key function (every UNO exception, since they declare no +// virtual functions) gets hidden ("weak private external") typeinfo on +// arm64 regardless of visibility attributes, whereas the same class gets +// exported ("weak external") typeinfo on x86_64. Compare: +// +// clang++ -arch arm64 -c keyless_rtti_visibility_demo.cxx && nm -m keyless_rtti_visibility_demo.o | grep __ZTI +// clang++ -arch x86_64 -c keyless_rtti_visibility_demo.cxx && nm -m keyless_rtti_visibility_demo.o | grep __ZTI +// +// A/B (keyless) are hidden on arm64 and exported on x86_64; C/D (have a key +// function) are exported on both, with or without the visibility attribute. +struct A { int x; }; +struct __attribute__((visibility("default"))) B { int x; }; +struct C { int x; virtual void key(); }; +void C::key() {} +struct __attribute__((visibility("default"))) D { int x; virtual void key(); }; +void D::key() {} +#include <typeinfo> +const void* refs[] = { &typeid(A), &typeid(B), &typeid(C), &typeid(D) }; diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/catcher.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/catcher.cxx new file mode 100644 index 0000000000..8fdbcb5324 --- /dev/null +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/catcher.cxx @@ -0,0 +1,10 @@ +// See thrower.cxx for build/run instructions and expected output. +#include "uno_like.hxx" +#include <cstdio> +extern "C" void throw_synth(); +int main() { + try { throw_synth(); } + catch (com::sun::star::ucb::InteractiveAugmentedIOException&) { printf("RESULT: caught as EXACT derived type (WORKS)\n"); return 0; } + catch (com::sun::star::uno::Exception&) { printf("RESULT: caught as BASE css::uno::Exception (WORKS)\n"); return 0; } + catch (...) { printf("RESULT: caught only by catch(...) (BROKEN)\n"); return 1; } +} diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/thrower.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/thrower.cxx new file mode 100644 index 0000000000..acc100be5d --- /dev/null +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/thrower.cxx @@ -0,0 +1,47 @@ +// Cross-image ABI harness: synthesizes RTTI for a whole exception chain +// (neither type dlsym-able, mirroring how the real bridge is used) and +// throws it from a separate dylib. Not wired into gbuild; build and run +// manually: +// +// clang++ -std=c++11 -arch arm64 -dynamiclib -o libthrower.dylib thrower.cxx \ +// -install_name @rpath/libthrower.dylib +// clang++ -std=c++11 -arch arm64 -o catcher catcher.cxx -L. -lthrower \ +// -Wl,-rpath,@executable_path +// ./catcher +// +// Expected: "RESULT: caught as EXACT derived type (WORKS)". +#include "uno_like.hxx" +#include <typeinfo> +#include <cstring> +#include <cstdlib> +#include <cstdint> +#include <cstdio> +extern "C" void* __cxa_allocate_exception(size_t) throw(); +extern "C" void __cxa_throw(void*, std::type_info*, void (*)(void*)) __attribute__((noreturn)); +// vtable donors: real, compiler-emitted, in THIS dylib +namespace { struct DonorBase { int a; }; struct DonorDerived : DonorBase { int b; }; } +struct ClassTi { void const* vptr; uintptr_t name; }; +struct SiTi { void const* vptr; uintptr_t name; void const* base; }; +static const uintptr_t NONUNIQUE = 1ULL << 63; +static uintptr_t mkname(const char* s){ return (uintptr_t)strdup(s) | NONUNIQUE; } +static std::type_info* synth_base() { + static ClassTi* t = 0; + if (!t) { t = (ClassTi*)calloc(1,sizeof(ClassTi)); + t->vptr = ((ClassTi const*)&typeid(DonorBase))->vptr; + t->name = mkname("N3com3sun4star3uno9ExceptionE"); } + return (std::type_info*)t; +} +static std::type_info* synth_derived() { + static SiTi* t = 0; + if (!t) { t = (SiTi*)calloc(1,sizeof(SiTi)); + t->vptr = ((SiTi const*)&typeid(DonorDerived))->vptr; + t->name = mkname("N3com3sun4star3ucb31InteractiveAugmentedIOExceptionE"); + t->base = synth_base(); } + return (std::type_info*)t; +} +static void nodel(void*) {} +extern "C" __attribute__((visibility("default"))) void throw_synth() { + void* e = __cxa_allocate_exception(sizeof(com::sun::star::ucb::InteractiveAugmentedIOException)); + memset(e,0,sizeof(com::sun::star::ucb::InteractiveAugmentedIOException)); + __cxa_throw(e, synth_derived(), nodel); +} diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/uno_like.hxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/uno_like.hxx new file mode 100644 index 0000000000..5f1ed6b9c3 --- /dev/null +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_crossdylib_test/uno_like.hxx @@ -0,0 +1,5 @@ +#pragma once +namespace com { namespace sun { namespace star { +namespace uno { struct Exception { char* Message; void* Context; }; } +namespace ucb { struct InteractiveAugmentedIOException : public ::com::sun::star::uno::Exception { int code; }; } +}}} diff --git a/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_synthesis_test.cxx b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_synthesis_test.cxx new file mode 100644 index 0000000000..272a0d7eef --- /dev/null +++ b/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/qa/rtti_synthesis_test.cxx @@ -0,0 +1,120 @@ +// Standalone ABI harness for the RTTI synthesis in ../except.cxx and +// ../share.hxx. Not wired into gbuild; compile and run manually: +// +// clang++ -std=c++11 -arch arm64 -o rtti_synthesis_test rtti_synthesis_test.cxx +// ./rtti_synthesis_test +// +// Expected output: test 3 and both variants of test 2 report CAUGHT; only +// test 1 (the naive hand-built type_info, i.e. the approach the +// s5abi_macosx_x86-64 bridge uses) reports the catch(...) fallback. +// +// Does libc++abi on arm64 Darwin catch an exception thrown with a hand-built +// type_info? Mirrors what bridges/.../s5abi_macosx_x86-64/except.cxx does. +#include <cxxabi.h> +#include <typeinfo> +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <cstdint> + +// ---- UNO-like plain structs (no virtual functions), as css::uno::Exception is +struct UnoException { char* Message; void* Context; }; +struct UnoRealDerived : UnoException { int extra; }; // typeid -> real __si_class_type_info + +// ---- AOO's stand-ins, copied verbatim in shape from s5abi share.hxx ---- +namespace fake { +class __class_type_info : public std::type_info { +public: + explicit __class_type_info(const char* n) : std::type_info(n) {} +}; +class __si_class_type_info : public __class_type_info { + const __class_type_info* mpBaseType; +public: + __si_class_type_info(const char* n, __class_type_info* b) + : __class_type_info(n), mpBaseType(b) {} +}; +} + +// Itanium layout of __cxxabiv1::__si_class_type_info +struct SiLayout { + void const* vptr; + char const* name; + void const* base; +}; + +extern "C" void* __cxa_allocate_exception(size_t) throw(); +extern "C" void __cxa_throw(void*, std::type_info*, void (*)(void*)) __attribute__((noreturn)); + +static void nodelete(void*) {} + +static void report(const char* what, int r) { + printf("%-46s : %s\n", what, + r == 1 ? "CAUGHT as UnoException& (WORKS)" + : r == 2 ? "caught only by catch(...) (BROKEN)" + : "??"); +} + +// Test 1: naive hand-built type_info (foreign vtable) -- what AOO x86-64 does +static int test_naive() { + static fake::__si_class_type_info* ti = + new fake::__si_class_type_info( + strdup("N3com3sun4star3ucb31InteractiveAugmentedIOExceptionE"), + (fake::__class_type_info*)const_cast<std::type_info*>(&typeid(UnoException))); + void* e = __cxa_allocate_exception(sizeof(UnoException)); + memset(e, 0, sizeof(UnoException)); + try { + __cxa_throw(e, (std::type_info*)ti, nodelete); + } catch (UnoException&) { return 1; } + catch (...) { return 2; } + return 0; +} + +// Test 2: vtable borrowed from a real compiler-emitted __si_class_type_info +static int test_vtable_borrow(bool set_nonunique_bit) { + SiLayout const* model = + reinterpret_cast<SiLayout const*>(&typeid(UnoRealDerived)); + SiLayout* ti = (SiLayout*)calloc(1, sizeof(SiLayout)); + ti->vptr = model->vptr; + char const* nm = strdup("N3com3sun4star3ucb31InteractiveAugmentedIOExceptionE"); + if (set_nonunique_bit) + nm = (char const*)((uintptr_t)nm | (1ULL << 63)); + ti->name = nm; + ti->base = &typeid(UnoException); + void* e = __cxa_allocate_exception(sizeof(UnoException)); + memset(e, 0, sizeof(UnoException)); + try { + __cxa_throw(e, (std::type_info*)ti, nodelete); + } catch (UnoException&) { return 1; } + catch (...) { return 2; } + return 0; +} + +// Test 3: sanity -- a genuinely compiler-emitted derived type +static int test_real() { + void* e = __cxa_allocate_exception(sizeof(UnoRealDerived)); + memset(e, 0, sizeof(UnoRealDerived)); + try { + __cxa_throw(e, (std::type_info*)&typeid(UnoRealDerived), nodelete); + } catch (UnoException&) { return 1; } + catch (...) { return 2; } + return 0; +} + +int main() { + printf("_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION-relevant arch: %s\n", +#if defined(__APPLE__) && defined(__LP64__) && !defined(__x86_64__) + "arm64 Apple -> NonUniqueARMRTTIBit (impl 3)"); +#else + "other"); +#endif + SiLayout const* m = reinterpret_cast<SiLayout const*>(&typeid(UnoRealDerived)); + printf("real typeid(UnoRealDerived).name ptr = %p high bit=%d str=%s\n", + (void*)m->name, (int)(((uintptr_t)m->name) >> 63), + typeid(UnoRealDerived).name()); + printf("\n"); + report("3: real compiler-emitted typeinfo (control)", test_real()); + report("1: naive hand-built (AOO x86-64 approach)", test_naive()); + report("2a: borrowed vtable, plain name", test_vtable_borrow(false)); + report("2b: borrowed vtable, non-unique bit set", test_vtable_borrow(true)); + return 0; +}
