[libunwind] a85da64 - [libunwind][AIX] implementation of the unwinder for AIX

Xing Xue via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 13 08:04:51 PDT 2022


Author: Xing Xue
Date: 2022-04-13T11:01:59-04:00
New Revision: a85da649b9ac67afffec6bff9487e6405e1f9cba

URL: https://github.com/llvm/llvm-project/commit/a85da649b9ac67afffec6bff9487e6405e1f9cba
DIFF: https://github.com/llvm/llvm-project/commit/a85da649b9ac67afffec6bff9487e6405e1f9cba.diff

LOG: [libunwind][AIX] implementation of the unwinder for AIX

Summary:
This patch contains the implementation of the unwinder for IBM AIX.

AIX does not support the eh_frame section. Instead, the traceback table located at the end of each function provides the information for stack unwinding and EH. In this patch macro _LIBUNWIND_SUPPORT_TBTAB_UNWIND is used to guard code for AIX traceback table based unwinding. Function getInfoFromTBTable() and stepWithTBTable() are added to get the EH information from the traceback table and to step up the stack respectively.

There are two kinds of LSDA information for EH on AIX, the state table and the range table. The state table is used by the previous version of the IBM XL compiler, i.e., xlC and xlclang++. The DWARF based range table is used by AIX clang++. The traceback table has flags to differentiate these cases. For the range table, relative addresses are calculated using a base of DW_EH_PE_datarel, which is the TOC base of the module where the function of the current frame belongs.

Two personality routines are employed to handle these two different LSDAs, __xlcxx_personality_v0() for the state table and __xlcxx_personality_v1() for the range table. Since the traceback table does not have the information of the personality for the state table approach, its personality __xlcxx_personality_v0() is dynamically resolved as the handler for the state table. For the range table, the locations of the LSDA and its associated personality routine are found in the traceback table.

Assembly code for 32- and 64-bit PowerPC in UnwindRegistersRestore.S and UnwindRegistersSave.S are modified so that it can be consumed by the GNU flavor assembler and the AIX assembler. The restoration of vector registers does not check VRSAVE on AIX because VRSAVE is not used in the AIX ABI.

Reviewed by: MaskRay, compnerd, cebowleratibm, sfertile, libunwind

Differential Revision: https://reviews.llvm.org/D100132

Added: 
    libunwind/src/Unwind_AIXExtras.cpp

Modified: 
    libunwind/include/libunwind.h
    libunwind/include/unwind.h
    libunwind/src/AddressSpace.hpp
    libunwind/src/CMakeLists.txt
    libunwind/src/Registers.hpp
    libunwind/src/UnwindCursor.hpp
    libunwind/src/UnwindLevel1-gcc-ext.c
    libunwind/src/UnwindLevel1.c
    libunwind/src/UnwindRegistersRestore.S
    libunwind/src/UnwindRegistersSave.S
    libunwind/src/assembly.h
    libunwind/src/config.h
    libunwind/src/libunwind.cpp
    libunwind/src/libunwind_ext.h

Removed: 
    


################################################################################
diff  --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index 9a74faa48d6ff..a69e72fc132df 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -120,6 +120,9 @@ extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL;
 extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL;
 #endif
 
+#ifdef _AIX
+extern uintptr_t unw_get_data_rel_base(unw_cursor_t *) LIBUNWIND_AVAIL;
+#endif
 
 extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
 extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL;

diff  --git a/libunwind/include/unwind.h b/libunwind/include/unwind.h
index b8d6020a33672..6557374fa9d31 100644
--- a/libunwind/include/unwind.h
+++ b/libunwind/include/unwind.h
@@ -160,7 +160,7 @@ extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *);
 extern void *_Unwind_FindEnclosingFunction(void *pc);
 
 // Mac OS X does not support text-rel and data-rel addressing so these functions
-// are unimplemented
+// are unimplemented.
 extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context)
     LIBUNWIND_UNAVAIL;
 extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context)

diff  --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp
index 0c4dfeb4e6834..3d5c001608d05 100644
--- a/libunwind/src/AddressSpace.hpp
+++ b/libunwind/src/AddressSpace.hpp
@@ -24,11 +24,11 @@
 #include "Registers.hpp"
 
 #ifndef _LIBUNWIND_USE_DLADDR
-  #if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32)
-    #define _LIBUNWIND_USE_DLADDR 1
-  #else
-    #define _LIBUNWIND_USE_DLADDR 0
-  #endif
+#if !(defined(_LIBUNWIND_IS_BAREMETAL) || defined(_WIN32) || defined(_AIX))
+#define _LIBUNWIND_USE_DLADDR 1
+#else
+#define _LIBUNWIND_USE_DLADDR 0
+#endif
 #endif
 
 #if _LIBUNWIND_USE_DLADDR
@@ -45,6 +45,13 @@ struct EHABIIndexEntry {
 };
 #endif
 
+#if defined(_AIX)
+namespace libunwind {
+char *getFuncNameFromTBTable(uintptr_t pc, uint16_t &NameLen,
+                             unw_word_t *offset);
+}
+#endif
+
 #ifdef __APPLE__
 
   struct dyld_unwind_sections
@@ -580,6 +587,11 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
   (void)targetAddr;
   (void)info;
   return true;
+#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  // The traceback table is used for unwinding.
+  (void)targetAddr;
+  (void)info;
+  return true;
 #elif defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX)
   int length = 0;
   info.arm_section =
@@ -596,7 +608,6 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
   return false;
 }
 
-
 inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
   // TO DO: if OS has way to dynamically register FDEs, check that.
   (void)targetAddr;
@@ -616,6 +627,13 @@ inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
       return true;
     }
   }
+#elif defined(_AIX)
+  uint16_t nameLen;
+  char *funcName = getFuncNameFromTBTable(addr, nameLen, offset);
+  if (funcName != NULL) {
+    snprintf(buf, bufLen, "%.*s", nameLen, funcName);
+    return true;
+  }
 #else
   (void)addr;
   (void)buf;

diff  --git a/libunwind/src/CMakeLists.txt b/libunwind/src/CMakeLists.txt
index 710198550a061..d21f4bd71ee43 100644
--- a/libunwind/src/CMakeLists.txt
+++ b/libunwind/src/CMakeLists.txt
@@ -11,6 +11,12 @@ if(APPLE)
     )
 endif()
 
+if(${CMAKE_SYSTEM_NAME} MATCHES "AIX")
+  list(APPEND LIBUNWIND_CXX_SOURCES
+    Unwind_AIXExtras.cpp
+    )
+endif()
+
 set(LIBUNWIND_C_SOURCES
     UnwindLevel1.c
     UnwindLevel1-gcc-ext.c
@@ -26,7 +32,11 @@ set(LIBUNWIND_ASM_SOURCES
     )
 
 # See add_asm_sources() in compiler-rt for explanation of this workaround.
-if((APPLE AND CMAKE_VERSION VERSION_LESS 3.19) OR (MINGW AND CMAKE_VERSION VERSION_LESS 3.17))
+# CMake doesn't work correctly with assembly on AIX. Workaround by compiling
+# as C files as well.
+if((APPLE AND CMAKE_VERSION VERSION_LESS 3.19) OR
+   (MINGW AND CMAKE_VERSION VERSION_LESS 3.17) OR
+   (${CMAKE_SYSTEM_NAME} MATCHES "AIX"))
   set_source_files_properties(${LIBUNWIND_ASM_SOURCES} PROPERTIES LANGUAGE C)
 endif()
 

diff  --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index cbc3876d672e1..32c13e6c9a248 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -609,6 +609,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc {
   void      setSP(uint32_t value) { _registers.__r1 = value; }
   uint64_t  getIP() const         { return _registers.__srr0; }
   void      setIP(uint32_t value) { _registers.__srr0 = value; }
+  uint64_t  getCR() const         { return _registers.__cr; }
+  void      setCR(uint32_t value) { _registers.__cr = value; }
 
 private:
   struct ppc_thread_state_t {
@@ -1175,6 +1177,8 @@ class _LIBUNWIND_HIDDEN Registers_ppc64 {
   void      setSP(uint64_t value) { _registers.__r1 = value; }
   uint64_t  getIP() const         { return _registers.__srr0; }
   void      setIP(uint64_t value) { _registers.__srr0 = value; }
+  uint64_t  getCR() const         { return _registers.__cr; }
+  void      setCR(uint64_t value) { _registers.__cr = value; }
 
 private:
   struct ppc64_thread_state_t {

diff  --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 1ca842f33aa51..dd849b781753e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -24,6 +24,11 @@
 #ifdef __APPLE__
   #include <mach-o/dyld.h>
 #endif
+#ifdef _AIX
+#include <dlfcn.h>
+#include <sys/debug.h>
+#include <sys/pseg.h>
+#endif
 
 #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
 // Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and
@@ -451,6 +456,12 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
   virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); }
 #endif
 
+#ifdef _AIX
+  virtual uintptr_t getDataRelBase() {
+    _LIBUNWIND_ABORT("getDataRelBase not implemented");
+  }
+#endif
+
 #if defined(_LIBUNWIND_USE_CET)
   virtual void *get_registers() {
     _LIBUNWIND_ABORT("get_registers not implemented");
@@ -910,9 +921,14 @@ class UnwindCursor : public AbstractUnwindCursor{
   virtual void        saveVFPAsX();
 #endif
 
+#ifdef _AIX
+  virtual uintptr_t getDataRelBase();
+#endif
+
 #if defined(_LIBUNWIND_USE_CET)
   virtual void *get_registers() { return &_registers; }
 #endif
+
   // libunwind does not and should not depend on C++ library which means that we
   // need our own defition of inline placement new.
   static void *operator new(size_t, UnwindCursor<A, R> *p) { return p; }
@@ -1220,6 +1236,16 @@ class UnwindCursor : public AbstractUnwindCursor{
   int stepWithSEHData() { /* FIXME: Implement */ return 0; }
 #endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
 
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  bool getInfoFromTBTable(pint_t pc, R &registers);
+  int stepWithTBTable(pint_t pc, tbtable *TBTable, R &registers,
+                      bool &isSignalFrame);
+  int stepWithTBTableData() {
+    return stepWithTBTable(reinterpret_cast<pint_t>(this->getReg(UNW_REG_IP)),
+                           reinterpret_cast<tbtable *>(_info.unwind_info),
+                           _registers, _isSignalFrame);
+  }
+#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
 
   A               &_addressSpace;
   R                _registers;
@@ -1292,6 +1318,13 @@ template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() {
 }
 #endif
 
+#ifdef _AIX
+template <typename A, typename R>
+uintptr_t UnwindCursor<A, R>::getDataRelBase() {
+  return reinterpret_cast<uintptr_t>(_info.extra);
+}
+#endif
+
 template <typename A, typename R>
 const char *UnwindCursor<A, R>::getRegisterName(int regNum) {
   return _registers.getRegisterName(regNum);
@@ -1922,6 +1955,507 @@ bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
 }
 #endif
 
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+// Masks for traceback table field xtbtable.
+enum xTBTableMask : uint8_t {
+  reservedBit = 0x02, // The traceback table was incorrectly generated if set
+                      // (see comments in function getInfoFromTBTable().
+  ehInfoBit = 0x08    // Exception handling info is present if set
+};
+
+enum frameType : unw_word_t {
+  frameWithXLEHStateTable = 0,
+  frameWithEHInfo = 1
+};
+
+extern "C" {
+typedef _Unwind_Reason_Code __xlcxx_personality_v0_t(int, _Unwind_Action,
+                                                     uint64_t,
+                                                     _Unwind_Exception *,
+                                                     struct _Unwind_Context *);
+__attribute__((__weak__)) __xlcxx_personality_v0_t __xlcxx_personality_v0;
+}
+
+static __xlcxx_personality_v0_t *xlcPersonalityV0;
+static RWMutex xlcPersonalityV0InitLock;
+
+template <typename A, typename R>
+bool UnwindCursor<A, R>::getInfoFromTBTable(pint_t pc, R &registers) {
+  uint32_t *p = reinterpret_cast<uint32_t *>(pc);
+
+  // Keep looking forward until a word of 0 is found. The traceback
+  // table starts at the following word.
+  while (*p)
+    ++p;
+  tbtable *TBTable = reinterpret_cast<tbtable *>(p + 1);
+
+  if (_LIBUNWIND_TRACING_UNWINDING) {
+    char functionBuf[512];
+    const char *functionName = functionBuf;
+    unw_word_t offset;
+    if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) {
+      functionName = ".anonymous.";
+    }
+    _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p",
+                               __func__, functionName,
+                               reinterpret_cast<void *>(TBTable));
+  }
+
+  // If the traceback table does not contain necessary info, bypass this frame.
+  if (!TBTable->tb.has_tboff)
+    return false;
+
+  // Structure tbtable_ext contains important data we are looking for.
+  p = reinterpret_cast<uint32_t *>(&TBTable->tb_ext);
+
+  // Skip field parminfo if it exists.
+  if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
+    ++p;
+
+  // p now points to tb_offset, the offset from start of function to TB table.
+  unw_word_t start_ip =
+      reinterpret_cast<unw_word_t>(TBTable) - *p - sizeof(uint32_t);
+  unw_word_t end_ip = reinterpret_cast<unw_word_t>(TBTable);
+  ++p;
+
+  _LIBUNWIND_TRACE_UNWINDING("start_ip=%p, end_ip=%p\n",
+                             reinterpret_cast<void *>(start_ip),
+                             reinterpret_cast<void *>(end_ip));
+
+  // Skip field hand_mask if it exists.
+  if (TBTable->tb.int_hndl)
+    ++p;
+
+  unw_word_t lsda = 0;
+  unw_word_t handler = 0;
+  unw_word_t flags = frameType::frameWithXLEHStateTable;
+
+  if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl) {
+    // State table info is available. The ctl_info field indicates the
+    // number of CTL anchors. There should be only one entry for the C++
+    // state table.
+    assert(*p == 1 && "libunwind: there must be only one ctl_info entry");
+    ++p;
+    // p points to the offset of the state table into the stack.
+    pint_t stateTableOffset = *p++;
+
+    int framePointerReg;
+
+    // Skip fields name_len and name if exist.
+    if (TBTable->tb.name_present) {
+      const uint16_t name_len = *(reinterpret_cast<uint16_t *>(p));
+      p = reinterpret_cast<uint32_t *>(reinterpret_cast<char *>(p) + name_len +
+                                       sizeof(uint16_t));
+    }
+
+    if (TBTable->tb.uses_alloca)
+      framePointerReg = *(reinterpret_cast<char *>(p));
+    else
+      framePointerReg = 1; // default frame pointer == SP
+
+    _LIBUNWIND_TRACE_UNWINDING(
+        "framePointerReg=%d, framePointer=%p, "
+        "stateTableOffset=%#lx\n",
+        framePointerReg,
+        reinterpret_cast<void *>(_registers.getRegister(framePointerReg)),
+        stateTableOffset);
+    lsda = _registers.getRegister(framePointerReg) + stateTableOffset;
+
+    // Since the traceback table generated by the legacy XLC++ does not
+    // provide the location of the personality for the state table,
+    // function __xlcxx_personality_v0(), which is the personality for the state
+    // table and is exported from libc++abi, is directly assigned as the
+    // handler here. When a legacy XLC++ frame is encountered, the symbol
+    // is resolved dynamically using dlopen() to avoid hard dependency from
+    // libunwind on libc++abi.
+
+    // Resolve the function pointer to the state table personality if it has
+    // not already.
+    if (xlcPersonalityV0 == NULL) {
+      xlcPersonalityV0InitLock.lock();
+      if (xlcPersonalityV0 == NULL) {
+        // If libc++abi is statically linked in, symbol __xlcxx_personality_v0
+        // has been resolved at the link time.
+        xlcPersonalityV0 = &__xlcxx_personality_v0;
+        if (xlcPersonalityV0 == NULL) {
+          // libc++abi is dynamically linked. Resolve __xlcxx_personality_v0
+          // using dlopen().
+          const char libcxxabi[] = "libc++abi.a(libc++abi.so.1)";
+          void *libHandle;
+          libHandle = dlopen(libcxxabi, RTLD_MEMBER | RTLD_NOW);
+          if (libHandle == NULL) {
+            _LIBUNWIND_TRACE_UNWINDING("dlopen() failed with errno=%d\n",
+                                       errno);
+            assert(0 && "dlopen() failed");
+          }
+          xlcPersonalityV0 = reinterpret_cast<__xlcxx_personality_v0_t *>(
+              dlsym(libHandle, "__xlcxx_personality_v0"));
+          if (xlcPersonalityV0 == NULL) {
+            _LIBUNWIND_TRACE_UNWINDING("dlsym() failed with errno=%d\n", errno);
+            assert(0 && "dlsym() failed");
+          }
+          dlclose(libHandle);
+        }
+      }
+      xlcPersonalityV0InitLock.unlock();
+    }
+    handler = reinterpret_cast<unw_word_t>(xlcPersonalityV0);
+    _LIBUNWIND_TRACE_UNWINDING("State table: LSDA=%p, Personality=%p\n",
+                               reinterpret_cast<void *>(lsda),
+                               reinterpret_cast<void *>(handler));
+  } else if (TBTable->tb.longtbtable) {
+    // This frame has the traceback table extension. Possible cases are
+    // 1) a C++ frame that has the 'eh_info' structure; 2) a C++ frame that
+    // is not EH aware; or, 3) a frame of other languages. We need to figure out
+    // if the traceback table extension contains the 'eh_info' structure.
+    //
+    // We also need to deal with the complexity arising from some XL compiler
+    // versions use the wrong ordering of 'longtbtable' and 'has_vec' bits
+    // where the 'longtbtable' bit is meant to be the 'has_vec' bit and vice
+    // versa. For frames of code generated by those compilers, the 'longtbtable'
+    // bit may be set but there isn't really a traceback table extension.
+    //
+    // In </usr/include/sys/debug.h>, there is the following definition of
+    // 'struct tbtable_ext'. It is not really a structure but a dummy to
+    // collect the description of optional parts of the traceback table.
+    //
+    // struct tbtable_ext {
+    //   ...
+    //   char alloca_reg;        /* Register for alloca automatic storage */
+    //   struct vec_ext vec_ext; /* Vector extension (if has_vec is set) */
+    //   unsigned char xtbtable; /* More tbtable fields, if longtbtable is set*/
+    // };
+    //
+    // Depending on how the 'has_vec'/'longtbtable' bit is interpreted, the data
+    // following 'alloca_reg' can be treated either as 'struct vec_ext' or
+    // 'unsigned char xtbtable'. 'xtbtable' bits are defined in
+    // </usr/include/sys/debug.h> as flags. The 7th bit '0x02' is currently
+    // unused and should not be set. 'struct vec_ext' is defined in
+    // </usr/include/sys/debug.h> as follows:
+    //
+    // struct vec_ext {
+    //   unsigned vr_saved:6;      /* Number of non-volatile vector regs saved
+    //   */
+    //                             /* first register saved is assumed to be */
+    //                             /* 32 - vr_saved                         */
+    //   unsigned saves_vrsave:1;  /* Set if vrsave is saved on the stack */
+    //   unsigned has_varargs:1;
+    //   ...
+    // };
+    //
+    // Here, the 7th bit is used as 'saves_vrsave'. To determine whether it
+    // is 'struct vec_ext' or 'xtbtable' that follows 'alloca_reg',
+    // we checks if the 7th bit is set or not because 'xtbtable' should
+    // never have the 7th bit set. The 7th bit of 'xtbtable' will be reserved
+    // in the future to make sure the mitigation works. This mitigation
+    // is not 100% bullet proof because 'struct vec_ext' may not always have
+    // 'saves_vrsave' bit set.
+    //
+    // 'reservedBit' is defined in enum 'xTBTableMask' above as the mask for
+    // checking the 7th bit.
+
+    // p points to field name len.
+    uint8_t *charPtr = reinterpret_cast<uint8_t *>(p);
+
+    // Skip fields name_len and name if they exist.
+    if (TBTable->tb.name_present) {
+      const uint16_t name_len = *(reinterpret_cast<uint16_t *>(charPtr));
+      charPtr = charPtr + name_len + sizeof(uint16_t);
+    }
+
+    // Skip field alloc_reg if it exists.
+    if (TBTable->tb.uses_alloca)
+      ++charPtr;
+
+    // Check traceback table bit has_vec. Skip struct vec_ext if it exists.
+    if (TBTable->tb.has_vec)
+      // Note struct vec_ext does exist at this point because whether the
+      // ordering of longtbtable and has_vec bits is correct or not, both
+      // are set.
+      charPtr += sizeof(struct vec_ext);
+
+    // charPtr points to field 'xtbtable'. Check if the EH info is available.
+    // Also check if the reserved bit of the extended traceback table field
+    // 'xtbtable' is set. If it is, the traceback table was incorrectly
+    // generated by an XL compiler that uses the wrong ordering of 'longtbtable'
+    // and 'has_vec' bits and this is in fact 'struct vec_ext'. So skip the
+    // frame.
+    if ((*charPtr & xTBTableMask::ehInfoBit) &&
+        !(*charPtr & xTBTableMask::reservedBit)) {
+      // Mark this frame has the new EH info.
+      flags = frameType::frameWithEHInfo;
+
+      // eh_info is available.
+      charPtr++;
+      // The pointer is 4-byte aligned.
+      if (reinterpret_cast<uintptr_t>(charPtr) % 4)
+        charPtr += 4 - reinterpret_cast<uintptr_t>(charPtr) % 4;
+      uintptr_t *ehInfo =
+          reinterpret_cast<uintptr_t *>(*(reinterpret_cast<uintptr_t *>(
+              registers.getRegister(2) +
+              *(reinterpret_cast<uintptr_t *>(charPtr)))));
+
+      // ehInfo points to structure en_info. The first member is version.
+      // Only version 0 is currently supported.
+      assert(*(reinterpret_cast<uint32_t *>(ehInfo)) == 0 &&
+             "libunwind: ehInfo version other than 0 is not supported");
+
+      // Increment ehInfo to point to member lsda.
+      ++ehInfo;
+      lsda = *ehInfo++;
+
+      // enInfo now points to member personality.
+      handler = *ehInfo;
+
+      _LIBUNWIND_TRACE_UNWINDING("Range table: LSDA=%#lx, Personality=%#lx\n",
+                                 lsda, handler);
+    }
+  }
+
+  _info.start_ip = start_ip;
+  _info.end_ip = end_ip;
+  _info.lsda = lsda;
+  _info.handler = handler;
+  _info.gp = 0;
+  _info.flags = flags;
+  _info.format = 0;
+  _info.unwind_info = reinterpret_cast<unw_word_t>(TBTable);
+  _info.unwind_info_size = 0;
+  _info.extra = registers.getRegister(2);
+
+  return true;
+}
+
+// Step back up the stack following the frame back link.
+template <typename A, typename R>
+int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
+                                        R &registers, bool &isSignalFrame) {
+  if (_LIBUNWIND_TRACING_UNWINDING) {
+    char functionBuf[512];
+    const char *functionName = functionBuf;
+    unw_word_t offset;
+    if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) {
+      functionName = ".anonymous.";
+    }
+    _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p",
+                               __func__, functionName,
+                               reinterpret_cast<void *>(TBTable));
+  }
+
+#if defined(__powerpc64__)
+  // Instruction to reload TOC register "l r2,40(r1)"
+  const uint32_t loadTOCRegInst = 0xe8410028;
+  const int32_t unwPPCF0Index = UNW_PPC64_F0;
+  const int32_t unwPPCV0Index = UNW_PPC64_V0;
+#else
+  // Instruction to reload TOC register "l r2,20(r1)"
+  const uint32_t loadTOCRegInst = 0x80410014;
+  const int32_t unwPPCF0Index = UNW_PPC_F0;
+  const int32_t unwPPCV0Index = UNW_PPC_V0;
+#endif
+
+  R newRegisters = registers;
+
+  // lastStack points to the stack frame of the next routine up.
+  pint_t lastStack = *(reinterpret_cast<pint_t *>(registers.getSP()));
+
+  // Return address is the address after call site instruction.
+  pint_t returnAddress;
+
+  if (isSignalFrame) {
+    _LIBUNWIND_TRACE_UNWINDING("Possible signal handler frame: lastStack=%p",
+                               reinterpret_cast<void *>(lastStack));
+
+    sigcontext *sigContext = reinterpret_cast<sigcontext *>(
+        reinterpret_cast<char *>(lastStack) + STKMIN);
+    returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
+
+    _LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n",
+                               reinterpret_cast<void *>(sigContext),
+                               reinterpret_cast<void *>(returnAddress));
+
+    if (returnAddress < 0x10000000) {
+      // Try again using STKMINALIGN
+      sigContext = reinterpret_cast<sigcontext *>(
+          reinterpret_cast<char *>(lastStack) + STKMINALIGN);
+      returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
+      if (returnAddress < 0x10000000) {
+        _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n",
+                                   reinterpret_cast<void *>(returnAddress));
+        return UNW_EBADFRAME;
+      } else {
+        _LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: "
+                                   "sigContext=%p, returnAddress=%p. "
+                                   "Seems to be a valid address\n",
+                                   reinterpret_cast<void *>(sigContext),
+                                   reinterpret_cast<void *>(returnAddress));
+      }
+    }
+    // Restore the condition register from sigcontext.
+    newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr);
+
+    // Restore GPRs from sigcontext.
+    for (int i = 0; i < 32; ++i)
+      newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]);
+
+    // Restore FPRs from sigcontext.
+    for (int i = 0; i < 32; ++i)
+      newRegisters.setFloatRegister(i + unwPPCF0Index,
+                                    sigContext->sc_jmpbuf.jmp_context.fpr[i]);
+
+    // Restore vector registers if there is an associated extended context
+    // structure.
+    if (sigContext->sc_jmpbuf.jmp_context.msr & __EXTCTX) {
+      ucontext_t *uContext = reinterpret_cast<ucontext_t *>(sigContext);
+      if (uContext->__extctx->__extctx_magic == __EXTCTX_MAGIC) {
+        for (int i = 0; i < 32; ++i)
+          newRegisters.setVectorRegister(
+              i + unwPPCV0Index, *(reinterpret_cast<v128 *>(
+                                     &(uContext->__extctx->__vmx.__vr[i]))));
+      }
+    }
+  } else {
+    // Step up a normal frame.
+    returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
+
+    _LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, "
+                               "returnAddress=%p\n",
+                               reinterpret_cast<void *>(lastStack),
+                               reinterpret_cast<void *>(returnAddress));
+    _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n",
+                               TBTable->tb.fpr_saved, TBTable->tb.gpr_saved,
+                               TBTable->tb.saves_cr);
+
+    // Restore FP registers.
+    char *ptrToRegs = reinterpret_cast<char *>(lastStack);
+    double *FPRegs = reinterpret_cast<double *>(
+        ptrToRegs - (TBTable->tb.fpr_saved * sizeof(double)));
+    for (int i = 0; i < TBTable->tb.fpr_saved; ++i)
+      newRegisters.setFloatRegister(
+          32 - TBTable->tb.fpr_saved + i + unwPPCF0Index, FPRegs[i]);
+
+    // Restore GP registers.
+    ptrToRegs = reinterpret_cast<char *>(FPRegs);
+    uintptr_t *GPRegs = reinterpret_cast<uintptr_t *>(
+        ptrToRegs - (TBTable->tb.gpr_saved * sizeof(uintptr_t)));
+    for (int i = 0; i < TBTable->tb.gpr_saved; ++i)
+      newRegisters.setRegister(32 - TBTable->tb.gpr_saved + i, GPRegs[i]);
+
+    // Restore Vector registers.
+    ptrToRegs = reinterpret_cast<char *>(GPRegs);
+
+    // Restore vector registers only if this is a Clang frame. Also
+    // check if traceback table bit has_vec is set. If it is, structure
+    // vec_ext is available.
+    if (_info.flags == frameType::frameWithEHInfo && TBTable->tb.has_vec) {
+
+      // Get to the vec_ext structure to check if vector registers are saved.
+      uint32_t *p = reinterpret_cast<uint32_t *>(&TBTable->tb_ext);
+
+      // Skip field parminfo if exists.
+      if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
+        ++p;
+
+      // Skip field tb_offset if exists.
+      if (TBTable->tb.has_tboff)
+        ++p;
+
+      // Skip field hand_mask if exists.
+      if (TBTable->tb.int_hndl)
+        ++p;
+
+      // Skip fields ctl_info and ctl_info_disp if exist.
+      if (TBTable->tb.has_ctl) {
+        // Skip field ctl_info.
+        ++p;
+        // Skip field ctl_info_disp.
+        ++p;
+      }
+
+      // Skip fields name_len and name if exist.
+      // p is supposed to point to field name_len now.
+      uint8_t *charPtr = reinterpret_cast<uint8_t *>(p);
+      if (TBTable->tb.name_present) {
+        const uint16_t name_len = *(reinterpret_cast<uint16_t *>(charPtr));
+        charPtr = charPtr + name_len + sizeof(uint16_t);
+      }
+
+      // Skip field alloc_reg if it exists.
+      if (TBTable->tb.uses_alloca)
+        ++charPtr;
+
+      struct vec_ext *vec_ext = reinterpret_cast<struct vec_ext *>(charPtr);
+
+      _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved);
+
+      // Restore vector register(s) if saved on the stack.
+      if (vec_ext->vr_saved) {
+        // Saved vector registers are 16-byte aligned.
+        if (reinterpret_cast<uintptr_t>(ptrToRegs) % 16)
+          ptrToRegs -= reinterpret_cast<uintptr_t>(ptrToRegs) % 16;
+        v128 *VecRegs = reinterpret_cast<v128 *>(ptrToRegs - vec_ext->vr_saved *
+                                                                 sizeof(v128));
+        for (int i = 0; i < vec_ext->vr_saved; ++i) {
+          newRegisters.setVectorRegister(
+              32 - vec_ext->vr_saved + i + unwPPCV0Index, VecRegs[i]);
+        }
+      }
+    }
+    if (TBTable->tb.saves_cr) {
+      // Get the saved condition register. The condition register is only
+      // a single word.
+      newRegisters.setCR(
+          *(reinterpret_cast<uint32_t *>(lastStack + sizeof(uintptr_t))));
+    }
+
+    // Restore the SP.
+    newRegisters.setSP(lastStack);
+
+    // The first instruction after return.
+    uint32_t firstInstruction = *(reinterpret_cast<uint32_t *>(returnAddress));
+
+    // Do we need to set the TOC register?
+    _LIBUNWIND_TRACE_UNWINDING(
+        "Current gpr2=%p\n",
+        reinterpret_cast<void *>(newRegisters.getRegister(2)));
+    if (firstInstruction == loadTOCRegInst) {
+      _LIBUNWIND_TRACE_UNWINDING(
+          "Set gpr2=%p from frame\n",
+          reinterpret_cast<void *>(reinterpret_cast<pint_t *>(lastStack)[5]));
+      newRegisters.setRegister(2, reinterpret_cast<pint_t *>(lastStack)[5]);
+    }
+  }
+  _LIBUNWIND_TRACE_UNWINDING("lastStack=%p, returnAddress=%p, pc=%p\n",
+                             reinterpret_cast<void *>(lastStack),
+                             reinterpret_cast<void *>(returnAddress),
+                             reinterpret_cast<void *>(pc));
+
+  // The return address is the address after call site instruction, so
+  // setting IP to that simualates a return.
+  newRegisters.setIP(reinterpret_cast<uintptr_t>(returnAddress));
+
+  // Simulate the step by replacing the register set with the new ones.
+  registers = newRegisters;
+
+  // Check if the next frame is a signal frame.
+  pint_t nextStack = *(reinterpret_cast<pint_t *>(registers.getSP()));
+
+  // Return address is the address after call site instruction.
+  pint_t nextReturnAddress = reinterpret_cast<pint_t *>(nextStack)[2];
+
+  if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) {
+    _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: "
+                               "nextStack=%p, next return address=%p\n",
+                               reinterpret_cast<void *>(nextStack),
+                               reinterpret_cast<void *>(nextReturnAddress));
+    isSignalFrame = true;
+  } else {
+    isSignalFrame = false;
+  }
+
+  return UNW_STEP_SUCCESS;
+}
+#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
 
 template <typename A, typename R>
 void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
@@ -1948,7 +2482,14 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
   // To disambiguate this, back up the pc when we know it is a return
   // address.
   if (isReturnAddress)
+#if defined(_AIX)
+    // PC needs to be a 4-byte aligned address to be able to look for a
+    // word of 0 that indicates the start of the traceback table at the end
+    // of a function on AIX.
+    pc -= 4;
+#else
     --pc;
+#endif
 
   // Ask address space object to find unwind sections for this pc.
   UnwindInfoSections sects;
@@ -1982,6 +2523,12 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
       return;
 #endif
 
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+    // If there is unwind info in the traceback table, look there next.
+    if (this->getInfoFromTBTable(pc, _registers))
+      return;
+#endif
+
 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
     // If there is dwarf unwind info, look there next.
     if (sects.dwarf_section != 0) {
@@ -2116,6 +2663,8 @@ int UnwindCursor<A, R>::step() {
     result = this->stepWithCompactEncoding();
 #elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
     result = this->stepWithSEHData();
+#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+    result = this->stepWithTBTableData();
 #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
     result = this->stepWithDwarfFDE();
 #elif defined(_LIBUNWIND_ARM_EHABI)

diff  --git a/libunwind/src/UnwindLevel1-gcc-ext.c b/libunwind/src/UnwindLevel1-gcc-ext.c
index 951d5d219a3eb..0250664bbc7ef 100644
--- a/libunwind/src/UnwindLevel1-gcc-ext.c
+++ b/libunwind/src/UnwindLevel1-gcc-ext.c
@@ -59,12 +59,15 @@ _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
 /// relative encodings.
 _LIBUNWIND_EXPORT uintptr_t
 _Unwind_GetDataRelBase(struct _Unwind_Context *context) {
-  (void)context;
   _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
+#if defined(_AIX)
+  return unw_get_data_rel_base((unw_cursor_t *)context);
+#else
+  (void)context;
   _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
+#endif
 }
 
-
 /// Called by personality handler during phase 2 to get base address for text
 /// relative encodings.
 _LIBUNWIND_EXPORT uintptr_t

diff  --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index d5e3cf36b8a30..84f865e591fcb 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -484,11 +484,13 @@ _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
   _LIBUNWIND_TRACE_API(
       "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,
       (void *)context, result);
+#if !defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
   if (result != 0) {
     if (*((uint8_t *)result) != 0xFF)
       _LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF",
                            result);
   }
+#endif
   return result;
 }
 

diff  --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 1df97f5fc41c4..b4843c912b0bd 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -8,7 +8,11 @@
 
 #include "assembly.h"
 
+#if defined(_AIX)
+  .toc
+#else
   .text
+#endif
 
 #if !defined(__USING_SJLJ_EXCEPTIONS__)
 
@@ -222,27 +226,36 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv)
   PPC64_LVS(30)
   PPC64_LVS(31)
 
-  // use VRSAVE to conditionally restore the remaining VS regs,
-  // that are where the V regs are mapped
+#define PPC64_CLVS_RESTORE(n)                    \
+  addi   4, 3, PPC64_OFFS_FP + n * 16           ;\
+  lxvd2x n, 0, 4
 
+#if !defined(_AIX)
+  // use VRSAVE to conditionally restore the remaining VS regs, that are
+  // where the V regs are mapped. In the AIX ABI, VRSAVE is not used.
   ld    5, PPC64_OFFS_VRSAVE(3)   // test VRsave
   cmpwi 5, 0
   beq   Lnovec
 
 // conditionally load VS
-#define PPC64_CLVS_BOTTOM(n)               \
-  beq    Ldone##n                         ;\
-  addi   4, 3, PPC64_OFFS_FP + n * 16     ;\
-  lxvd2x n, 0, 4                          ;\
+#define PPC64_CLVSl(n)                           \
+  andis. 0, 5, (1 PPC_LEFT_SHIFT(47-n))         ;\
+  beq    Ldone##n                               ;\
+  PPC64_CLVS_RESTORE(n)                         ;\
 Ldone##n:
 
-#define PPC64_CLVSl(n)                    \
-  andis. 0, 5, (1 PPC_LEFT_SHIFT(47-n))  ;\
-PPC64_CLVS_BOTTOM(n)
+#define PPC64_CLVSh(n)                           \
+  andi.  0, 5, (1 PPC_LEFT_SHIFT(63-n))         ;\
+  beq    Ldone##n                               ;\
+  PPC64_CLVS_RESTORE(n)                         ;\
+Ldone##n:
 
-#define PPC64_CLVSh(n)                    \
-  andi.  0, 5, (1 PPC_LEFT_SHIFT(63-n))  ;\
-PPC64_CLVS_BOTTOM(n)
+#else
+
+#define PPC64_CLVSl(n) PPC64_CLVS_RESTORE(n)
+#define PPC64_CLVSh(n) PPC64_CLVS_RESTORE(n)
+
+#endif // !defined(_AIX)
 
   PPC64_CLVSl(32)
   PPC64_CLVSl(33)
@@ -318,32 +331,44 @@ PPC64_CLVS_BOTTOM(n)
   PPC64_LF(31)
 
 #if defined(__ALTIVEC__)
-  // restore vector registers if any are in use
+
+#define PPC64_CLV_UNALIGNED_RESTORE(n)       \
+  ld     0, (PPC64_OFFS_V + n * 16)(3)      ;\
+  std    0, 0(4)                            ;\
+  ld     0, (PPC64_OFFS_V + n * 16 + 8)(3)  ;\
+  std    0, 8(4)                            ;\
+  lvx    n, 0, 4
+
+#if !defined(_AIX)
+  // restore vector registers if any are in use. In the AIX ABI, VRSAVE is
+  // not used.
   ld    5, PPC64_OFFS_VRSAVE(3)   // test VRsave
   cmpwi 5, 0
   beq   Lnovec
 
-  subi  4, 1, 16
-  // r4 is now a 16-byte aligned pointer into the red zone
-  // the _vectorScalarRegisters may not be 16-byte aligned
-  // so copy via red zone temp buffer
+#define PPC64_CLV_UNALIGNEDl(n)              \
+  andis. 0, 5, (1 PPC_LEFT_SHIFT(15-n))     ;\
+  beq    Ldone##n                           ;\
+  PPC64_CLV_UNALIGNED_RESTORE(n)            ;\
+Ldone  ## n:
 
-#define PPC64_CLV_UNALIGNED_BOTTOM(n)            \
-  beq    Ldone##n                               ;\
-  ld     0, (PPC64_OFFS_V + n * 16)(3)          ;\
-  std    0, 0(4)                                ;\
-  ld     0, (PPC64_OFFS_V + n * 16 + 8)(3)      ;\
-  std    0, 8(4)                                ;\
-  lvx    n, 0, 4                                ;\
+#define PPC64_CLV_UNALIGNEDh(n)              \
+  andi.  0, 5, (1 PPC_LEFT_SHIFT(31-n))     ;\
+  beq    Ldone##n                           ;\
+  PPC64_CLV_UNALIGNED_RESTORE(n)            ;\
 Ldone  ## n:
 
-#define PPC64_CLV_UNALIGNEDl(n)                 \
-  andis. 0, 5, (1 PPC_LEFT_SHIFT(15-n))        ;\
-PPC64_CLV_UNALIGNED_BOTTOM(n)
+#else
+
+#define PPC64_CLV_UNALIGNEDl(n) PPC64_CLV_UNALIGNED_RESTORE(n)
+#define PPC64_CLV_UNALIGNEDh(n) PPC64_CLV_UNALIGNED_RESTORE(n)
+
+#endif // !defined(_AIX)
 
-#define PPC64_CLV_UNALIGNEDh(n)                \
-  andi.  0, 5, (1 PPC_LEFT_SHIFT(31-n))       ;\
-PPC64_CLV_UNALIGNED_BOTTOM(n)
+  subi  4, 1, 16
+  // r4 is now a 16-byte aligned pointer into the red zone
+  // the _vectorScalarRegisters may not be 16-byte aligned
+  // so copy via red zone temp buffer
 
   PPC64_CLV_UNALIGNEDl(0)
   PPC64_CLV_UNALIGNEDl(1)
@@ -387,11 +412,23 @@ Lnovec:
   ld    0, PPC64_OFFS_SRR0(3)
   mtctr 0
 
+#if defined(_AIX)
+  // After setting GPR1 to a higher address, AIX wipes out the original
+  // stack space below that address invalidated by the new GPR1 value. Use
+  // GPR0 to save the value of GPR3 in the context before it is wiped out.
+  // This compromises the content of GPR0 which is a volatile register.
+  ld 0, (8 * (3 + 2))(3)
+#else
   PPC64_LR(0)
+#endif
   PPC64_LR(5)
   PPC64_LR(4)
   PPC64_LR(1)
+#if defined(_AIX)
+  mr 3, 0
+#else
   PPC64_LR(3)
+#endif
   bctr
 
 #elif defined(__powerpc__)
@@ -475,45 +512,48 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv)
 #endif
 
 #if defined(__ALTIVEC__)
-  // restore vector registers if any are in use
+
+#define LOAD_VECTOR_RESTORE(_index)                 \
+  lwz     0, 424+_index*16(3)             SEPARATOR \
+  stw     0, 0(4)                         SEPARATOR \
+  lwz     0, 424+_index*16+4(3)           SEPARATOR \
+  stw     0, 4(4)                         SEPARATOR \
+  lwz     0, 424+_index*16+8(3)           SEPARATOR \
+  stw     0, 8(4)                         SEPARATOR \
+  lwz     0, 424+_index*16+12(3)          SEPARATOR \
+  stw     0, 12(4)                        SEPARATOR \
+  lvx     _index, 0, 4
+
+#if !defined(_AIX)
+  // restore vector registers if any are in use. In the AIX ABI, VRSAVE
+  // is not used.
   lwz     5, 156(3)       // test VRsave
   cmpwi   5, 0
   beq     Lnovec
 
-  subi    4, 1, 16
-  rlwinm  4, 4, 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
-
-
-#define LOAD_VECTOR_UNALIGNEDl(_index)          \
-  andis.  0, 5, (1 PPC_LEFT_SHIFT(15-_index)) SEPARATOR \
-  beq     Ldone ## _index             SEPARATOR \
-  lwz     0, 424+_index*16(3)         SEPARATOR \
-  stw     0, 0(%r4)                   SEPARATOR \
-  lwz     0, 424+_index*16+4(%r3)     SEPARATOR \
-  stw     0, 4(%r4)                   SEPARATOR \
-  lwz     0, 424+_index*16+8(%r3)     SEPARATOR \
-  stw     0, 8(%r4)                   SEPARATOR \
-  lwz     0, 424+_index*16+12(%r3)    SEPARATOR \
-  stw     0, 12(%r4)                  SEPARATOR \
-  lvx     _index, 0, 4                SEPARATOR \
+#define LOAD_VECTOR_UNALIGNEDl(_index)                   \
+  andis.  0, 5, (1 PPC_LEFT_SHIFT(15-_index))  SEPARATOR \
+  beq     Ldone ## _index                      SEPARATOR \
+  LOAD_VECTOR_RESTORE(_index)                  SEPARATOR \
   Ldone ## _index:
 
-#define LOAD_VECTOR_UNALIGNEDh(_index)          \
-  andi.   0, 5, (1 PPC_LEFT_SHIFT(31-_index)) SEPARATOR \
-  beq     Ldone ## _index             SEPARATOR \
-  lwz     0, 424+_index*16(3)         SEPARATOR \
-  stw     0, 0(4)                     SEPARATOR \
-  lwz     0, 424+_index*16+4(3)       SEPARATOR \
-  stw     0, 4(4)                     SEPARATOR \
-  lwz     0, 424+_index*16+8(3)       SEPARATOR \
-  stw     0, 8(%r4)                   SEPARATOR \
-  lwz     0, 424+_index*16+12(3)      SEPARATOR \
-  stw     0, 12(4)                    SEPARATOR \
-  lvx     _index, 0, 4                SEPARATOR \
+#define LOAD_VECTOR_UNALIGNEDh(_index)                   \
+  andi.   0, 5, (1 PPC_LEFT_SHIFT(31-_index))  SEPARATOR \
+  beq     Ldone ## _index                      SEPARATOR \
+  LOAD_VECTOR_RESTORE(_index)                  SEPARATOR \
   Ldone ## _index:
 
+#else
+
+#define LOAD_VECTOR_UNALIGNEDl(_index) LOAD_VECTOR_RESTORE(_index)
+#define LOAD_VECTOR_UNALIGNEDh(_index) LOAD_VECTOR_RESTORE(_index)
+
+#endif // !defined(_AIX)
+
+  subi    4, 1, 16
+  rlwinm  4, 4, 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
 
   LOAD_VECTOR_UNALIGNEDl(0)
   LOAD_VECTOR_UNALIGNEDl(1)

diff  --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index b39489235ce63..0cfc7b565863e 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -8,7 +8,11 @@
 
 #include "assembly.h"
 
+#if defined(_AIX)
+    .toc
+#else
     .text
+#endif
 
 #if !defined(__USING_SJLJ_EXCEPTIONS__)
 
@@ -334,8 +338,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
 // On entry:
 //  thread_state pointer is in r3
 //
+#if defined(_AIX)
+DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(__unw_getcontext, unw_getcontext)
+#else
 DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
-
+#endif
 // store register (GPR)
 #define PPC64_STR(n) \
   std   n, (8 * (n + 2))(3)
@@ -567,7 +574,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
 // On entry:
 //  thread_state pointer is in r3
 //
+#if defined(_AIX)
+DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(__unw_getcontext, unw_getcontext)
+#else
 DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
+#endif
   stw     0,   8(3)
   mflr    0
   stw     0,   0(3) // store lr as ssr0

diff  --git a/libunwind/src/Unwind_AIXExtras.cpp b/libunwind/src/Unwind_AIXExtras.cpp
new file mode 100644
index 0000000000000..7e47f70186e76
--- /dev/null
+++ b/libunwind/src/Unwind_AIXExtras.cpp
@@ -0,0 +1,63 @@
+//===--------------------- Unwind_AIXExtras.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//
+//===----------------------------------------------------------------------===//
+
+// This file is only used for AIX.
+#if defined(_AIX)
+
+#include "config.h"
+#include "libunwind_ext.h"
+#include <sys/debug.h>
+
+namespace libunwind {
+// getFuncNameFromTBTable
+// Get the function name from its traceback table.
+char *getFuncNameFromTBTable(uintptr_t Pc, uint16_t &NameLen,
+                             unw_word_t *Offset) {
+  uint32_t *p = reinterpret_cast<uint32_t *>(Pc);
+  *Offset = 0;
+
+  // Keep looking forward until a word of 0 is found. The traceback
+  // table starts at the following word.
+  while (*p)
+    p++;
+  tbtable *TBTable = reinterpret_cast<tbtable *>(p + 1);
+
+  if (!TBTable->tb.name_present)
+    return NULL;
+
+  // Get to the name of the function.
+  p = reinterpret_cast<uint32_t *>(&TBTable->tb_ext);
+
+  // Skip field parminfo if it exists.
+  if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
+    p++;
+
+  // If the tb_offset field exisits, get the offset from the start of
+  // the function to pc. Skip the field.
+  if (TBTable->tb.has_tboff) {
+    unw_word_t StartIp =
+        reinterpret_cast<uintptr_t>(TBTable) - *p - sizeof(uint32_t);
+    *Offset = Pc - StartIp;
+    p++;
+  }
+
+  // Skip field hand_mask if it exists.
+  if (TBTable->tb.int_hndl)
+    p++;
+
+  // Skip fields ctl_info and ctl_info_disp if they exist.
+  if (TBTable->tb.has_ctl) {
+    p += 1 + *p;
+  }
+
+  NameLen = *(reinterpret_cast<uint16_t *>(p));
+  return reinterpret_cast<char *>(p) + sizeof(uint16_t);
+}
+} // namespace libunwind
+#endif // defined(_AIX)

diff  --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index 89293a555bfc4..9554ca8cc62b1 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -67,7 +67,8 @@
 #define SEPARATOR ;
 #endif
 
-#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1)
+#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) &&       \
+    !defined(_AIX)
 #define PPC64_OPD1 .section .opd,"aw", at progbits SEPARATOR
 #define PPC64_OPD2 SEPARATOR \
   .p2align 3 SEPARATOR \
@@ -203,12 +204,57 @@
 
 #elif defined(__sparc__)
 
+#elif defined(_AIX)
+
+#if defined(__powerpc64__)
+#define VBYTE_LEN 8
+#define CSECT_ALIGN 3
+#elif defined(__ppc__)
+#define VBYTE_LEN 4
+#define CSECT_ALIGN 2
+#endif
+
+// clang-format off
+#define DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(name, aliasname)              \
+  .csect .text[PR], 2 SEPARATOR                                                \
+  .csect .name[PR], 2 SEPARATOR                                                \
+  .globl name[DS] SEPARATOR                                                    \
+  .globl .name[PR] SEPARATOR                                                   \
+  .align 4 SEPARATOR                                                           \
+  .csect name[DS], CSECT_ALIGN SEPARATOR                                       \
+aliasname:                                                                     \
+  .vbyte VBYTE_LEN, .name[PR] SEPARATOR                                        \
+  .vbyte VBYTE_LEN, TOC[TC0] SEPARATOR                                         \
+  .vbyte VBYTE_LEN, 0 SEPARATOR                                                \
+  .weak  aliasname SEPARATOR                                                   \
+  .weak  .aliasname SEPARATOR                                                  \
+  .csect .name[PR], 2 SEPARATOR                                                \
+.aliasname:                                                                    \
+
+#define WEAK_ALIAS(name, aliasname)
+#define NO_EXEC_STACK_DIRECTIVE
+
+// clang-format on
 #else
 
 #error Unsupported target
 
 #endif
 
+#if defined(_AIX)
+  // clang-format off
+#define DEFINE_LIBUNWIND_FUNCTION(name)                                        \
+  .globl name[DS] SEPARATOR                                                    \
+  .globl .name SEPARATOR                                                       \
+  .align 4 SEPARATOR                                                           \
+  .csect name[DS], CSECT_ALIGN SEPARATOR                                       \
+  .vbyte VBYTE_LEN, .name SEPARATOR                                            \
+  .vbyte VBYTE_LEN, TOC[TC0] SEPARATOR                                         \
+  .vbyte VBYTE_LEN, 0 SEPARATOR                                                \
+  .csect .text[PR], 2 SEPARATOR                                                \
+.name:
+  // clang-format on
+#else
 #define DEFINE_LIBUNWIND_FUNCTION(name)                                        \
   .globl SYMBOL_NAME(name) SEPARATOR                                           \
   HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR                                   \
@@ -217,6 +263,7 @@
   SYMBOL_NAME(name):                                                           \
   PPC64_OPD2                                                                   \
   AARCH64_BTI
+#endif
 
 #if defined(__arm__)
 #if !defined(__ARM_ARCH)

diff  --git a/libunwind/src/config.h b/libunwind/src/config.h
index d2309186df57a..7fd6b7334053e 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -43,6 +43,9 @@
   // For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After
   // API 21, dl_iterate_phdr exists, but dl_unwind_find_exidx is much faster.
   #define _LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX 1
+#elif defined(_AIX)
+// The traceback table at the end of each function is used for unwinding.
+#define _LIBUNWIND_SUPPORT_TBTAB_UNWIND 1
 #else
   // Assume an ELF system with a dl_iterate_phdr function.
   #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1
@@ -57,13 +60,13 @@
   #define _LIBUNWIND_EXPORT
   #define _LIBUNWIND_HIDDEN
 #else
-  #if !defined(__ELF__) && !defined(__MACH__)
-    #define _LIBUNWIND_EXPORT __declspec(dllexport)
-    #define _LIBUNWIND_HIDDEN
-  #else
-    #define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
-    #define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
-  #endif
+#if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX)
+#define _LIBUNWIND_EXPORT __declspec(dllexport)
+#define _LIBUNWIND_HIDDEN
+#else
+#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
+#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
+#endif
 #endif
 
 #define STR(a) #a
@@ -80,7 +83,7 @@
   __asm__(".globl " SYMBOL_NAME(aliasname));                                   \
   __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name));                     \
   _LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname))
-#elif defined(__ELF__)
+#elif defined(__ELF__) || defined(_AIX)
 #define _LIBUNWIND_WEAK_ALIAS(name, aliasname)                                 \
   extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname                        \
       __attribute__((weak, alias(#name)));

diff  --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index 03f8b75b5bba4..21aa42680e5b7 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -247,6 +247,16 @@ _LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) {
 }
 _LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame)
 
+#ifdef _AIX
+_LIBUNWIND_EXPORT uintptr_t __unw_get_data_rel_base(unw_cursor_t *cursor) {
+  _LIBUNWIND_TRACE_API("unw_get_data_rel_base(cursor=%p)",
+                       static_cast<void *>(cursor));
+  AbstractUnwindCursor *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
+  return co->getDataRelBase();
+}
+_LIBUNWIND_WEAK_ALIAS(__unw_get_data_rel_base, unw_get_data_rel_base)
+#endif
+
 #ifdef __arm__
 // Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD
 _LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) {

diff  --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h
index 7065ffcdaeff5..fdc533cb89622 100644
--- a/libunwind/src/libunwind_ext.h
+++ b/libunwind/src/libunwind_ext.h
@@ -43,6 +43,10 @@ extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t);
 extern int __unw_is_signal_frame(unw_cursor_t *);
 extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *);
 
+#if defined(_AIX)
+extern uintptr_t __unw_get_data_rel_base(unw_cursor_t *);
+#endif
+
 // SPI
 extern void __unw_iterate_dwarf_unwind_cache(void (*func)(
     unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh));


        


More information about the cfe-commits mailing list