//===----------------------------------------------------------------------===// // // 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 #include #include #include #include #include #include #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(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(p + 1); if (!TBTable->tb.name_present) return NULL; // Get to the optional portion of the traceback table. p = reinterpret_cast(&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(p); return reinterpret_cast(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