//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// Test that _Unwind_Backtrace() walks up from a signal handler and produces
// a correct traceback when the function raising the signal does not save
// the link register or does not store the stack back chain.

// REQUIRES: target=powerpc{{(64)?}}-ibm-aix

// Test when the function raising the signal does not save the link register
// RUN: %{cxx} -x c++ %s -o %t.exe -DCXX_CODE %{flags} %{compile_flags}
// RUN: %{exec} %t.exe

// Test when the function raising the signal does not store stack back chain.
// RUN: %{cxx} -x c++ -c %s -o %t1.o -DCXX_CODE -DNOBACKCHAIN %{flags} \
// RUN:   %{compile_flags}
// RUN: %{cxx} -c %s -o %t2.o %{flags} %{compile_flags}
// RUN: %{cxx} -o %t1.exe %t1.o %t2.o %{flags} %{link_flags}
// RUN: %{exec} %t1.exe

#ifdef CXX_CODE

#undef NDEBUG
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/debug.h>
#include <unwind.h>

#define NAME_ARRAY_SIZE 10
#define NAMES_EXPECTED   6

const char* namesExpected[] = {"handler", "abc", "bar", "foo", "main",
                               "__start"};
char *namesObtained[NAME_ARRAY_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int funcIndex = 0;

// Get the function name from traceback table.
char *getFuncName(uintptr_t pc, uint16_t *nameLen) {
  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 (!TBTable->tb.name_present)
    return NULL;

  // Get to the optional portion of the traceback table.
  p = reinterpret_cast<uint32_t *>(&TBTable->tb_ext);

  // Skip field parminfo if it exists.
  if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
    ++p;

  // Skip field tb_offset if it exists.
  if (TBTable->tb.has_tboff)
    ++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);
}

_Unwind_Reason_Code callBack(struct _Unwind_Context *uc, void *arg) {
  (void)arg;
  uint16_t nameLen;
  uintptr_t ip = _Unwind_GetIP(uc);
  if (funcIndex < NAME_ARRAY_SIZE)
    namesObtained[funcIndex++] = strndup(getFuncName(ip, &nameLen), nameLen);
  return _URC_NO_REASON;
}

extern "C" void handler(int signum) {
  (void)signum;
  // Walk stack frames for traceback.
  _Unwind_Backtrace(callBack, NULL);

  // Verify the traceback.
  assert(funcIndex <= NAMES_EXPECTED && "Obtained names more than expected");
  for (int i = 0; i < funcIndex; ++i) {
    assert(!strcmp(namesExpected[i], namesObtained[i]) &&
           "Function names do not match");
    free(namesObtained[i]);
  }
  exit(0);
}

#ifdef NOBACKCHAIN
// abc() is in assembly. It raises signal SIGSEGV and does not store
// the stack back chain.
extern "C" void abc();

#else
volatile int *null = 0;

// abc() raises signal SIGSEGV and does not save the link register.
extern "C" __attribute__((noinline)) void abc() {
  // Produce a SIGSEGV.
  *null = 0;
}
#endif

extern "C" __attribute__((noinline)) void bar() {
  abc();
}

extern "C" __attribute__((noinline)) void foo() {
  bar();
}

int main() {
  // Set signal handler for SIGSEGV.
  signal(SIGSEGV, handler);
  foo();
}

#else // Assembly code for abc().
// This assembly code is similar to the following C code but it saves the
// link register.
//
// int *badp = 0;
// void abc() {
//   *badp = 0;
// }

#ifdef __64BIT__
        .csect [PR],5
        .file   "abc.c"
        .globl  abc[DS]                 # -- Begin function abc
        .globl  .abc
        .align  4
        .csect abc[DS],3
        .vbyte  8, .abc                 # @abc
        .vbyte  8, TOC[TC0]
        .vbyte  8, 0
        .csect [PR],5
.abc:
# %bb.0:                                # %entry
        mflr 0
        std 0, 16(1)
        ld 3, L..C0(2)                  # @badp
        bl $+4
        ld 4, 0(3)
        li 3, 0
        stw 3, 0(4)
        ld 0, 16(1)
        mtlr 0
        blr
L..abc0:
        .vbyte  4, 0x00000000           # Traceback table begin
        .byte   0x00                    # Version = 0
        .byte   0x09                    # Language = CPlusPlus
        .byte   0x20                    # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue
                                        # +HasTraceBackTableOffset, -IsInternalProcedure
                                        # -HasControlledStorage, -IsTOCless
                                        # -IsFloatingPointPresent
                                        # -IsFloatingPointOperationLogOrAbortEnabled
        .byte   0x61                    # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed
                                        # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved
        .byte   0x00                    # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0
        .byte   0x01                    # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1
        .byte   0x00                    # NumberOfFixedParms = 0
        .byte   0x01                    # NumberOfFPParms = 0, +HasParmsOnStack
        .vbyte  4, L..abc0-.abc         # Function size
        .vbyte  2, 0x0003               # Function name len = 3
        .byte   "abc"                   # Function Name
        .byte   0x1f                    # AllocaUsed
                                        # -- End function
        .csect badp[RW],3
        .globl  badp[RW]                # @badp
        .align  3
        .vbyte  8, 0
        .toc
L..C0:
        .tc badp[TC],badp[RW]
#else
        .csect [PR],5
        .file   "abc.c"
        .globl  abc[DS]                 # -- Begin function abc
        .globl  .abc
        .align  4
        .csect abc[DS],2
        .vbyte  4, .abc                 # @abc
        .vbyte  4, TOC[TC0]
        .vbyte  4, 0
        .csect [PR],5
.abc:
# %bb.0:                                # %entry
        mflr 0
        stw 0, 8(1)
        lwz 3, L..C0(2)                 # @badp
        bl $+4
        lwz 4, 0(3)
        li 3, 0
        stw 3, 0(4)
        lwz 0, 8(1)
        mtlr 0
        blr
L..abc0:
        .vbyte  4, 0x00000000           # Traceback table begin
        .byte   0x00                    # Version = 0
        .byte   0x09                    # Language = CPlusPlus
        .byte   0x20                    # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue
                                        # +HasTraceBackTableOffset, -IsInternalProcedure
                                        # -HasControlledStorage, -IsTOCless
                                        # -IsFloatingPointPresent
                                        # -IsFloatingPointOperationLogOrAbortEnabled
        .byte   0x61                    # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed
                                        # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved
        .byte   0x00                    # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0
        .byte   0x01                    # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1
        .byte   0x00                    # NumberOfFixedParms = 0
        .byte   0x01                    # NumberOfFPParms = 0, +HasParmsOnStack
        .vbyte  4, L..abc0-.abc         # Function size
        .vbyte  2, 0x0003               # Function name len = 3
        .byte   "abc"                   # Function Name
        .byte   0x1f                    # AllocaUsed
                                        # -- End function
        .csect badp[RW],2
        .globl  badp[RW]                # @badp
        .align  2
        .vbyte  4, 0
        .toc
L..C0:
        .tc badp[TC],badp[RW]
#endif // __64BIT__
#endif // CXX_CODE
