From 2179eea7d9fea48d22cf819555044ba35d1280a4 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Thu, 22 Oct 2026 16:09:30 +0200 Subject: [PATCH 1/5] FreeBSD PowerPC64 bridge: drop the glibc stack protector code call.s was generated by gcc on a glibc system with -fstack-protector, so callVirtualMethod and privateSnippetExecutor keep their stack canary at -28688(r13), the slot where glibc stores the stack guard. On FreeBSD that slot is the first word of the thread control block, the DTV pointer. rtld replaces the DTV when a thread first uses TLS after a library with TLS was loaded, so a bridged call during which that happens fails the check and aborts in __stack_chk_fail. Saving a document from Python over the URP bridge was enough to trigger it. Remove the canary code from both functions. Co-authored-by: Claude Opus 5.5 --- .../cpp_uno/gcc3_freebsd_powerpc64/call.s | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s index 3803f5e8885..c42f8ecd6a2 100644 --- a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s @@ -56,9 +56,6 @@ callVirtualMethod: stw 9,296(31) ld 9,304(31) std 9,96(31) - ld 9,-28688(13) - std 9,184(31) - li 9,0 lwz 9,280(31) cmpwi 0,9,0 beq 0,.L2 @@ -198,14 +195,6 @@ callVirtualMethod: bl MapReturn nop nop - ld 9,184(31) - ld 10,-28688(13) - xor. 9,9,10 - li 10,0 - beq 0,.L5 - bl __stack_chk_fail - nop -.L5: addi 1,31,208 .cfi_def_cfa 1, 0 ld 0,16(1) @@ -241,9 +230,6 @@ privateSnippetExecutor: .cfi_offset 31, -8 mr 31,1 .cfi_def_cfa_register 31 - ld 0,-28688(13) - std 0,248(31) - li 0,0 std 3,80(31) std 4,88(31) std 5,96(31) @@ -392,14 +378,6 @@ stfd 13, 96(9) nop .L17: nop - ld 9,248(31) - ld 10,-28688(13) - xor. 9,9,10 - li 10,0 - beq 0,.L18 - bl __stack_chk_fail - nop -.L18: addi 1,31,272 .cfi_def_cfa 1, 0 ld 0,16(1) From 90aa412141b65896a0bda340f2b6563c8f3c7408 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Thu, 22 Oct 2026 16:09:30 +0200 Subject: [PATCH 2/5] FreeBSD PowerPC64 bridge: return small structs in registers The bridge used a hidden return pointer for every struct, but under the ELFv2 ABI structs of up to 16 bytes come back in r3/r4, and structs made of up to eight floats or doubles come back in f1-f8. Calling a C++ method that returns such a struct from Basic, Python or Java, e.g. getCellAddress() or getPosition(), passed the return buffer where the callee expected this, and the testtools bridgetest crashed. Use return_in_hidden_param() for both call directions and for the vtable snippets, pass f1-f8 to MapReturn(), and load f1-f8 in privateSnippetExecutor when a C++ caller expects such a float struct. With this the bridgetest passes on FreeBSD 15.1 powerpc64 and powerpc64le, in-process and over URP, against both the C++ and the Java test objects. Co-authored-by: Claude Opus 5.5 --- .../cpp_uno/gcc3_freebsd_powerpc64/call.s | 16 ++- .../gcc3_freebsd_powerpc64/cpp2uno.cxx | 31 +++++- .../cpp_uno/gcc3_freebsd_powerpc64/share.hxx | 2 + .../gcc3_freebsd_powerpc64/uno2cpp.cxx | 100 ++++++++++++++++-- 4 files changed, 136 insertions(+), 13 deletions(-) diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s index c42f8ecd6a2..f31fb1115d3 100644 --- a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/call.s @@ -187,8 +187,8 @@ callVirtualMethod: mr 9,3 mr 10,4 lfd 0,152(31) - ld 7,128(31) - ld 6,120(31) + ld 6,128(31) + ld 5,120(31) fmr 1,0 mr 4,10 mr 3,9 @@ -273,6 +273,8 @@ stfd 13, 96(9) mr 9,3 stw 9,44(31) lwa 9,44(31) + cmpwi 0,9,100 + beq 0,.Lfprs cmplwi 0,9,15 bgt 0,.L7 sldi 10,9,2 @@ -374,6 +376,16 @@ stfd 13, 96(9) # 0 "" 2 #NO_APP b .L17 +.Lfprs: + lfd 1,144(31) + lfd 2,152(31) + lfd 3,160(31) + lfd 4,168(31) + lfd 5,176(31) + lfd 6,184(31) + lfd 7,192(31) + lfd 8,200(31) + b .L17 .L19: nop .L17: diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/cpp2uno.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/cpp2uno.cxx index 30402d37710..8f435176c68 100644 --- a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/cpp2uno.cxx +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/cpp2uno.cxx @@ -76,12 +76,18 @@ static typelib_TypeClass cpp2uno_call( void * pUnoReturn = 0; void * pCppReturn = 0; // complex return ptr: if != 0 && != pUnoReturn, reconversion need + double * pFprReturn = reinterpret_cast< double * >( fpreg ); + typelib_TypeClass eFprBase = typelib_TypeClass_VOID; + sal_Int32 nFprMembers = 0; if (pReturnTypeDescr) { - if (bridges::cpp_uno::shared::isSimpleType( pReturnTypeDescr )) + if (!ppc64::return_in_hidden_param( pReturnTypeRef )) { - pUnoReturn = pRegisterReturn; // direct way for simple types + nFprMembers = ppc64::return_in_fprs( pReturnTypeRef, eFprBase ); + pUnoReturn = (nFprMembers != 0 + ? alloca( pReturnTypeDescr->nSize ) + : pRegisterReturn); // direct way for simple types } else // complex return via ptr (pCppReturn) { @@ -325,6 +331,23 @@ static typelib_TypeClass cpp2uno_call( if (pReturnTypeDescr) { typelib_TypeClass eRet = (typelib_TypeClass)pReturnTypeDescr->eTypeClass; + if (nFprMembers != 0) + { + for (sal_Int32 i = 0; i < nFprMembers; ++i) + pFprReturn[i] = (eFprBase == typelib_TypeClass_FLOAT + ? static_cast< float * >( pUnoReturn )[i] + : static_cast< double * >( pUnoReturn )[i]); + eRet = ppc64::TYPECLASS_FPRS; + } +#ifdef OSL_BIGENDIAN + else if (eRet == typelib_TypeClass_STRUCT && !pCppReturn && pReturnTypeDescr->nSize < 8) + { + sal_uInt64 n = 0; + memcpy( reinterpret_cast< char * >( &n ) + 8 - pReturnTypeDescr->nSize, + pRegisterReturn, pReturnTypeDescr->nSize ); + *reinterpret_cast< sal_uInt64 * >( pRegisterReturn ) = n; + } +#endif TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); return eRet; } @@ -710,7 +733,7 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( (s++)->fn = code + writetoexecdiff; code = codeSnippet( code, functionOffset++, vtableOffset, - bridges::cpp_uno::shared::isSimpleType( + !ppc64::return_in_hidden_param( reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( member)->pAttributeTypeRef)); @@ -729,7 +752,7 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( (s++)->fn = code + writetoexecdiff; code = codeSnippet( code, functionOffset++, vtableOffset, - bridges::cpp_uno::shared::isSimpleType( + !ppc64::return_in_hidden_param( reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( member)->pReturnTypeRef)); diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/share.hxx b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/share.hxx index 3ab7d359bfc..aaa4cf2ada7 100644 --- a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/share.hxx +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/share.hxx @@ -145,4 +145,6 @@ namespace ppc64 { enum ppclimits { MAX_GPR_REGS = 8, MAX_SSE_REGS = 13 }; bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ); + sal_Int32 return_in_fprs( typelib_TypeDescriptionReference *pTypeRef, typelib_TypeClass & eBase ); + const typelib_TypeClass TYPECLASS_FPRS = (typelib_TypeClass) 100; } diff --git a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/uno2cpp.cxx b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/uno2cpp.cxx index a0c1dfa0aa0..db9d0580d0d 100644 --- a/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/uno2cpp.cxx +++ b/main/bridges/source/cpp_uno/gcc3_freebsd_powerpc64/uno2cpp.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "bridges/cpp_uno/shared/bridge.hxx" @@ -84,7 +85,61 @@ namespace ppc64 return is_complex_struct(&p->pBaseTypeDescription->aBase); return false; } + + static bool collect_fpr_members(const typelib_TypeDescription * type, + typelib_TypeClass & eBase, sal_Int32 & nMembers) + { + const typelib_CompoundTypeDescription * p + = reinterpret_cast< const typelib_CompoundTypeDescription * >(type); + if (p->pBaseTypeDescription != 0 + && !collect_fpr_members(&p->pBaseTypeDescription->aBase, eBase, nMembers)) + return false; + for (sal_Int32 i = 0; i < p->nMembers; ++i) + { + typelib_TypeClass eClass = p->ppTypeRefs[i]->eTypeClass; + if (eClass == typelib_TypeClass_STRUCT) + { + typelib_TypeDescription * t = 0; + TYPELIB_DANGER_GET(&t, p->ppTypeRefs[i]); + bool b = collect_fpr_members(t, eBase, nMembers); + TYPELIB_DANGER_RELEASE(t); + if (!b) + return false; + } + else if (eClass == typelib_TypeClass_FLOAT || eClass == typelib_TypeClass_DOUBLE) + { + if (eBase == typelib_TypeClass_VOID) + eBase = eClass; + else if (eBase != eClass) + return false; + ++nMembers; + } + else + return false; + } + return true; + } +#endif + + sal_Int32 return_in_fprs( typelib_TypeDescriptionReference *pTypeRef, typelib_TypeClass & eBase ) + { + eBase = typelib_TypeClass_VOID; +#if defined(_CALL_ELF) && _CALL_ELF == 2 + if (pTypeRef->eTypeClass == typelib_TypeClass_STRUCT) + { + typelib_TypeDescription * pTypeDescr = 0; + TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); + sal_Int32 nMembers = 0; + if (!collect_fpr_members(pTypeDescr, eBase, nMembers) || nMembers > 8) + nMembers = 0; + TYPELIB_DANGER_RELEASE( pTypeDescr ); + return nMembers; + } +#else + (void)pTypeRef; #endif + return 0; + } bool return_in_hidden_param( typelib_TypeDescriptionReference *pTypeRef ) { @@ -93,6 +148,10 @@ namespace ppc64 #if defined(_CALL_ELF) && _CALL_ELF == 2 else if (pTypeRef->eTypeClass == typelib_TypeClass_STRUCT || pTypeRef->eTypeClass == typelib_TypeClass_EXCEPTION) { + typelib_TypeClass eBase; + if (return_in_fprs(pTypeRef, eBase) != 0) + return false; + typelib_TypeDescription * pTypeDescr = 0; TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef ); @@ -107,7 +166,8 @@ namespace ppc64 } } -extern "C" void MapReturn(long r3, long r4, double dret, typelib_TypeDescriptionReference* pReturnType, void *pRegisterReturn) +extern "C" void MapReturn(long r3, long r4, typelib_TypeDescriptionReference* pReturnType, void *pRegisterReturn, + double dret, double f2, double f3, double f4, double f5, double f6, double f7, double f8) { switch (pReturnType->eTypeClass) { @@ -138,15 +198,41 @@ extern "C" void MapReturn(long r3, long r4, double dret, typelib_TypeDescription #if defined(_CALL_ELF) && _CALL_ELF == 2 case typelib_TypeClass_STRUCT: case typelib_TypeClass_EXCEPTION: - if (!ppc64::return_in_hidden_param(pReturnType)) + { + typelib_TypeClass eBase; + sal_Int32 nMembers = ppc64::return_in_fprs(pReturnType, eBase); + if (nMembers != 0) { - sal_uInt64 *pRegisters = reinterpret_cast(pRegisterReturn); - pRegisters[0] = r3; - if (pReturnType->pType->nSize > 8) - pRegisters[1] = r4; + const double fpr[8] = { dret, f2, f3, f4, f5, f6, f7, f8 }; + for (sal_Int32 i = 0; i < nMembers; ++i) + { + if (eBase == typelib_TypeClass_FLOAT) + reinterpret_cast( pRegisterReturn )[i] = fpr[i]; + else + reinterpret_cast( pRegisterReturn )[i] = fpr[i]; + } + } + else if (!ppc64::return_in_hidden_param(pReturnType)) + { + char *p = reinterpret_cast(pRegisterReturn); + sal_Int32 nSize = pReturnType->pType->nSize; + if (nSize > 8) + { + memcpy(p, &r3, 8); + memcpy(p + 8, &r4, nSize - 8); + } + else +#ifdef OSL_BIGENDIAN + memcpy(p, reinterpret_cast(&r3) + 8 - nSize, nSize); +#else + memcpy(p, &r3, nSize); +#endif } + } + break; #else (void)r4; + (void)f2; (void)f3; (void)f4; (void)f5; (void)f6; (void)f7; (void)f8; #endif default: break; @@ -369,7 +455,7 @@ static void cpp_call( #ifdef CMC_DEBUG fprintf(stderr, "return type is %d\n", pReturnTypeDescr->eTypeClass); #endif - if (bridges::cpp_uno::shared::isSimpleType( pReturnTypeDescr )) + if (!ppc64::return_in_hidden_param( pReturnTypeRef )) { pCppReturn = pUnoReturn; // direct way for simple types #ifdef CMC_DEBUG From 5e376f3846f4fc35e1bba14c0f9c5082bbb5daca Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Thu, 22 Oct 2026 19:34:23 +0200 Subject: [PATCH 3/5] FreeBSD PowerPC64: find the build environment on little-endian too source_soenv.sh only matched the powerpc64-*-freebsd* triplet, so on powerpc64le bootstrap stopped with "unknown *Env.Set.sh for: powerpc64le-unknown-freebsd15.1" even though configure had written FreeBSDPPC64Env.Set.sh. Match powerpc64le-*-freebsd* too. Co-authored-by: Claude Opus 5.5 --- main/source_soenv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/source_soenv.sh b/main/source_soenv.sh index e460e8a99f7..8053578e541 100644 --- a/main/source_soenv.sh +++ b/main/source_soenv.sh @@ -104,7 +104,7 @@ else powerpc-*-freebsd*) . ./FreeBSDPPCEnv.Set.sh ;; - powerpc64-*-freebsd*) + powerpc64-*-freebsd*|powerpc64le-*-freebsd*) . ./FreeBSDPPC64Env.Set.sh ;; # the result here (whether you get x86_64) seems to depend on $CC... From cf96de6fcd2cc0a6f63d77d943c76866b44ec387 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Thu, 22 Oct 2026 16:09:31 +0200 Subject: [PATCH 4/5] FreeBSD PowerPC64: use the ppc64le JRE directories on little-endian OpenJDK on powerpc64le keeps its native libraries under jre/lib/ppc64le, not jre/lib/ppc64, so on little-endian FreeBSD the linker looked for libjawt in a directory that doesn't exist. Pick the directory from the platform triplet. Co-authored-by: Claude Opus 5.5 --- main/set_soenv.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main/set_soenv.in b/main/set_soenv.in index eb6b351a46a..17035e800d1 100644 --- a/main/set_soenv.in +++ b/main/set_soenv.in @@ -421,9 +421,10 @@ elsif ( $platform =~ m/freebsd/ ) $CPU = "P"; $CPUNAME = "POWERPC64"; $OUTPATH = "unxfbsdppc64"; - $JRELIBDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds."ppc64"; - $JRETOOLKITDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds."ppc64".$ds."server"; - $JRETHREADDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds."ppc64".$ds."native_threads"; + my $jrearch = ($platform =~ m/^powerpc64le/) ? "ppc64le" : "ppc64"; + $JRELIBDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds.$jrearch; + $JRETOOLKITDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds.$jrearch.$ds."server"; + $JRETHREADDIR = '$JAVA_HOME'.$ds."jre".$ds."lib".$ds.$jrearch.$ds."native_threads"; } elsif ($platform =~ m/powerpc/) { print "Setting FreeBSD PPC specific values... "; From f7297c60dbf71bad38d2f05be22b72ded759bca2 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Thu, 22 Oct 2026 16:09:31 +0200 Subject: [PATCH 5/5] jvmfwk: look for libjvm.so under ppc64le on little-endian PowerPC64 OpenJDK 8 on little-endian 64-bit PowerPC installs libjvm.so under jre/lib/ppc64le, so a Java 8 runtime was not recognized there. Use "ppc64le" as JFW_PLUGIN_ARCH when OSL_BIGENDIAN is not defined, like the MIPS entry does. Co-authored-by: Claude Opus 5.5 --- main/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.hxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.hxx b/main/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.hxx index 86ebdb8266c..1af42200002 100644 --- a/main/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.hxx +++ b/main/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.hxx @@ -44,7 +44,11 @@ namespace jfw_plugin #elif defined INTEL #define JFW_PLUGIN_ARCH "i386" #elif defined POWERPC64 +#ifdef OSL_BIGENDIAN #define JFW_PLUGIN_ARCH "ppc64" +#else +#define JFW_PLUGIN_ARCH "ppc64le" +#endif #elif defined POWERPC #define JFW_PLUGIN_ARCH "ppc" #elif defined MIPS