//===----------------------------------------------------------------------===//
//
// 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={{.+}}-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