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__)
 

Reply via email to