Hello tech, powerpc libunwind is broken on machines without altivec. It crashes SIGILL when code (built with base-clang++) throws a C++ exception, because libunwind always saves the altivec registers. You don't have altivec if sysctl machdep.altivec is 0. I believe that G3 cpus don't have altivec, and G4 and G5 do have it.
This diff defers saving the altivec registers until we need to access them. I took the idea from arm, which defers saving VFP registers. The diff fixes a small C++ demo on my G3. I didn't try other C++ code; I was building ports with macppc base-clang but stopped at an error from lang/python/3.7. Registers_arm has members like "bool _saved_vfp_d0_d15;" to know if VFP got saved. I can't add a "bool _saved_vrs" to Registers_ppc, because some assertions would fail, because unw_context_t would be too small. I don't enlarge unw_context_t (an opaque struct of 117 * 8 bytes) because it is in /usr/include/c++/v1/libunwind.h and I don't want to change the ABI. I instead check if vrsave != 0; vrsave is a bitset of altivec registers in use (Altivec Programming Environments Manual, ALTIVECPEM.pdf, section 2.3.3). libunwind operates on a saved context, not real registers. If a compiler exists that would tell libunwind to set v31 when vrsave == 0, this diff would break it. I have no bool to check if I have already saved the vrs but vrsave == 0. I also stop using the red zone, because it doesn't exist: a signal handler may clobber anything below the stack pointer. Is the diff OK to commit? It applies to src/lib/libunwind but you build it in src/lib/libcxxabi $ cat thrown.cpp #include <iostream> #include <stdexcept> using std::cout; using std::runtime_error; int main() { try { throw runtime_error("ouch"); } catch(runtime_error e) { cout << "caught " << e.what() << "\n"; } } $ clang++ -o thrown thrown.cpp $ ./thrown # without the fix Illegal instruction (core dumped) $ ./thrown # with the fixed libc++abi.so.2.0 caught ouch Index: src/Registers.hpp =================================================================== RCS file: /cvs/src/lib/libunwind/src/Registers.hpp,v retrieving revision 1.8 diff -u -p -r1.8 Registers.hpp --- src/Registers.hpp 17 Jun 2019 22:28:51 -0000 1.8 +++ src/Registers.hpp 2 Apr 2020 16:40:32 -0000 @@ -630,7 +630,7 @@ private: unsigned int __lr; /* Link register */ unsigned int __ctr; /* Count register */ unsigned int __mq; /* MQ register (601 only) */ - unsigned int __vrsave; /* Vector Save Register */ + mutable unsigned int __vrsave; /* Vector Save Register */ }; struct ppc_float_state_t { @@ -640,9 +640,11 @@ private: unsigned int __fpscr; /* floating point status register */ }; + void saveVectorRegisters() const; + ppc_thread_state_t _registers; ppc_float_state_t _floatRegisters; - v128 _vectorRegisters[32]; // offset 424 + mutable v128 _vectorRegisters[32]; // offset 424 }; inline Registers_ppc::Registers_ppc(const void *registers) { @@ -657,10 +659,8 @@ inline Registers_ppc::Registers_ppc(cons sizeof(_floatRegisters)); static_assert(sizeof(ppc_thread_state_t) + sizeof(ppc_float_state_t) == 424, "expected vector register offset to be 424 bytes"); - memcpy(_vectorRegisters, - static_cast<const uint8_t *>(registers) + sizeof(ppc_thread_state_t) + - sizeof(ppc_float_state_t), - sizeof(_vectorRegisters)); + // no values until saveVectorRegisters() + memset(&_vectorRegisters, 0, sizeof(_vectorRegisters)); } inline Registers_ppc::Registers_ppc() { @@ -780,6 +780,7 @@ inline uint32_t Registers_ppc::getRegist case UNW_PPC_CR7: return (_registers.__cr & 0x0000000F); case UNW_PPC_VRSAVE: + saveVectorRegisters(); return _registers.__vrsave; } _LIBUNWIND_ABORT("unsupported ppc register"); @@ -932,6 +933,7 @@ inline void Registers_ppc::setRegister(i _registers.__cr |= (value & 0x0000000F); return; case UNW_PPC_VRSAVE: + saveVectorRegisters(); _registers.__vrsave = value; return; // not saved @@ -976,12 +978,14 @@ inline bool Registers_ppc::validVectorRe inline v128 Registers_ppc::getVectorRegister(int regNum) const { assert(validVectorRegister(regNum)); + saveVectorRegisters(); v128 result = _vectorRegisters[regNum - UNW_PPC_V0]; return result; } inline void Registers_ppc::setVectorRegister(int regNum, v128 value) { assert(validVectorRegister(regNum)); + saveVectorRegisters(); _vectorRegisters[regNum - UNW_PPC_V0] = value; } Index: src/UnwindRegistersRestore.S =================================================================== RCS file: /cvs/src/lib/libunwind/src/UnwindRegistersRestore.S,v retrieving revision 1.8 diff -u -p -r1.8 UnwindRegistersRestore.S --- src/UnwindRegistersRestore.S 17 Jun 2019 22:28:51 -0000 1.8 +++ src/UnwindRegistersRestore.S 2 Apr 2020 16:40:33 -0000 @@ -476,11 +476,11 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9li cmpwi %r5,0 beq Lnovec - subi %r4,%r1,16 - rlwinm %r4,%r4,0,0,27 // mask low 4-bits - // r4 is now a 16-byte aligned pointer into the red zone - // the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer - + stwu %r1,-32(%r1) // allocate a stack frame + addi %r4,%r1,16 + clrrwi %r4,%r4,4 + // r4 is now a 16-byte aligned pointer + // the _vectorRegisters may not be 16-byte aligned so copy via r4 #define LOAD_VECTOR_UNALIGNEDl(_index) \ andis. %r0,%r5,(1<<(15-_index)) ;\ @@ -543,6 +543,8 @@ Ldone ## _index: LOAD_VECTOR_UNALIGNEDh(29) LOAD_VECTOR_UNALIGNEDh(30) LOAD_VECTOR_UNALIGNEDh(31) + + addi %r1,%r1,32 // drop the stack frame Lnovec: lwz %r0, 136(%r3) // __cr Index: src/UnwindRegistersSave.S =================================================================== RCS file: /cvs/src/lib/libunwind/src/UnwindRegistersSave.S,v retrieving revision 1.7 diff -u -p -r1.7 UnwindRegistersSave.S --- src/UnwindRegistersSave.S 17 Jun 2019 22:28:51 -0000 1.7 +++ src/UnwindRegistersSave.S 2 Apr 2020 16:40:33 -0000 @@ -599,8 +599,8 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext stw %r30,128(%r3) stw %r31,132(%r3) - // save VRSave register - mfspr %r0,256 + // zero VRSave register; saveVectorRegisters() will save it + li %r0,0 stw %r0,156(%r3) // save CR registers mfcr %r0 @@ -643,12 +643,29 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext stfd %f30,400(%r3) stfd %f31,408(%r3) + li %r3, 0 // return UNW_ESUCCESS + blr - // save vector registers - - subi %r4,%r1,16 - rlwinm %r4,%r4,0,0,27 // mask low 4-bits - // r4 is now a 16-byte aligned pointer into the red zone +// +// void libunwind::Registers_ppc::saveVectorRegisters() const +// +// On entry: +// thread_state pointer is in r3 +// +DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZNK9libunwind13Registers_ppc19saveVectorRegistersEv) + // return if we have already saved VRsave + lwz %r0,156(%r3) + cmpwi %r0,0 + bnelr + + stwu %r1,-32(%r1) // allocate a stack frame + addi %r4,%r1,16 + clrrwi %r4,%r4,4 + // r4 is now a 16-byte aligned pointer + + // save VRsave register + mfvrsave %r0 + stw %r0,156(%r3) #define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ stvx _vec,0,%r4 ;\ @@ -694,9 +711,8 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext SAVE_VECTOR_UNALIGNED(%v30, 424+0x1E0) SAVE_VECTOR_UNALIGNED(%v31, 424+0x1F0) - li %r3, 0 // return UNW_ESUCCESS + addi %r1,%r1,32 // drop the stack frame blr - #elif defined(__arm64__) || defined(__aarch64__)