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

Reply via email to