> Date: Thu, 2 Apr 2020 16:05:09 -0400
> From: George Koehler <[email protected]>
>
> 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
ok kettenis@
> 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__)
>
>