1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// Test that _Unwind_Backtrace() walks up from a signal handler and produces
10// a correct traceback when the function raising the signal does not save
11// the link register or does not store the stack back chain.
12
13// REQUIRES: target=powerpc{{(64)?}}-ibm-aix
14
15// Test when the function raising the signal does not save the link register
16// RUN: %{cxx} -x c++ %s -o %t.exe -DCXX_CODE %{flags} %{compile_flags}
17// RUN: %{exec} %t.exe
18
19// Test when the function raising the signal does not store stack back chain.
20// RUN: %{cxx} -x c++ -c %s -o %t1.o -DCXX_CODE -DNOBACKCHAIN %{flags} \
21// RUN: %{compile_flags}
22// RUN: %{cxx} -c %s -o %t2.o %{flags} %{compile_flags}
23// RUN: %{cxx} -o %t1.exe %t1.o %t2.o %{flags} %{link_flags}
24// RUN: %{exec} %t1.exe
25
26#ifdef CXX_CODE
27
28#undef NDEBUG
29#include <assert.h>
30#include <signal.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <sys/debug.h>
35#include <unwind.h>
36
37#define NAME_ARRAY_SIZE 10
38#define NAMES_EXPECTED 6
39
40const char* namesExpected[] = {"handler", "abc", "bar", "foo", "main",
41 "__start"};
42char *namesObtained[NAME_ARRAY_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
43
44int funcIndex = 0;
45
46// Get the function name from traceback table.
47char *getFuncName(uintptr_t pc, uint16_t *nameLen) {
48 uint32_t *p = reinterpret_cast<uint32_t *>(pc);
49
50 // Keep looking forward until a word of 0 is found. The traceback
51 // table starts at the following word.
52 while (*p)
53 ++p;
54 tbtable *TBTable = reinterpret_cast<tbtable *>(p + 1);
55
56 if (!TBTable->tb.name_present)
57 return NULL;
58
59 // Get to the optional portion of the traceback table.
60 p = reinterpret_cast<uint32_t *>(&TBTable->tb_ext);
61
62 // Skip field parminfo if it exists.
63 if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
64 ++p;
65
66 // Skip field tb_offset if it exists.
67 if (TBTable->tb.has_tboff)
68 ++p;
69
70 // Skip field hand_mask if it exists.
71 if (TBTable->tb.int_hndl)
72 ++p;
73
74 // Skip fields ctl_info and ctl_info_disp if they exist.
75 if (TBTable->tb.has_ctl)
76 p += 1 + *p;
77
78 *nameLen = *reinterpret_cast<uint16_t *>(p);
79 return reinterpret_cast<char *>(p) + sizeof(uint16_t);
80}
81
82_Unwind_Reason_Code callBack(struct _Unwind_Context *uc, void *arg) {
83 (void)arg;
84 uint16_t nameLen;
85 uintptr_t ip = _Unwind_GetIP(uc);
86 if (funcIndex < NAME_ARRAY_SIZE)
87 namesObtained[funcIndex++] = strndup(getFuncName(ip, &nameLen), nameLen);
88 return _URC_NO_REASON;
89}
90
91extern "C" void handler(int signum) {
92 (void)signum;
93 // Walk stack frames for traceback.
94 _Unwind_Backtrace(callBack, NULL);
95
96 // Verify the traceback.
97 assert(funcIndex <= NAMES_EXPECTED && "Obtained names more than expected");
98 for (int i = 0; i < funcIndex; ++i) {
99 assert(!strcmp(namesExpected[i], namesObtained[i]) &&
100 "Function names do not match");
101 free(namesObtained[i]);
102 }
103 exit(0);
104}
105
106#ifdef NOBACKCHAIN
107// abc() is in assembly. It raises signal SIGSEGV and does not store
108// the stack back chain.
109extern "C" void abc();
110
111#else
112volatile int *null = 0;
113
114// abc() raises signal SIGSEGV and does not save the link register.
115extern "C" __attribute__((noinline)) void abc() {
116 // Produce a SIGSEGV.
117 *null = 0;
118}
119#endif
120
121extern "C" __attribute__((noinline)) void bar() {
122 abc();
123}
124
125extern "C" __attribute__((noinline)) void foo() {
126 bar();
127}
128
129int main() {
130 // Set signal handler for SIGSEGV.
131 signal(SIGSEGV, handler);
132 foo();
133}
134
135#else // Assembly code for abc().
136// This assembly code is similar to the following C code but it saves the
137// link register.
138//
139// int *badp = 0;
140// void abc() {
141// *badp = 0;
142// }
143
144#ifdef __64BIT__
145 .csect [PR],5
146 .file "abc.c"
147 .globl abc[DS] # -- Begin function abc
148 .globl .abc
149 .align 4
150 .csect abc[DS],3
151 .vbyte 8, .abc # @abc
152 .vbyte 8, TOC[TC0]
153 .vbyte 8, 0
154 .csect [PR],5
155.abc:
156# %bb.0: # %entry
157 mflr 0
158 std 0, 16(1)
159 ld 3, L..C0(2) # @badp
160 bl $+4
161 ld 4, 0(3)
162 li 3, 0
163 stw 3, 0(4)
164 ld 0, 16(1)
165 mtlr 0
166 blr
167L..abc0:
168 .vbyte 4, 0x00000000 # Traceback table begin
169 .byte 0x00 # Version = 0
170 .byte 0x09 # Language = CPlusPlus
171 .byte 0x20 # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue
172 # +HasTraceBackTableOffset, -IsInternalProcedure
173 # -HasControlledStorage, -IsTOCless
174 # -IsFloatingPointPresent
175 # -IsFloatingPointOperationLogOrAbortEnabled
176 .byte 0x61 # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed
177 # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved
178 .byte 0x00 # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0
179 .byte 0x01 # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1
180 .byte 0x00 # NumberOfFixedParms = 0
181 .byte 0x01 # NumberOfFPParms = 0, +HasParmsOnStack
182 .vbyte 4, L..abc0-.abc # Function size
183 .vbyte 2, 0x0003 # Function name len = 3
184 .byte "abc" # Function Name
185 .byte 0x1f # AllocaUsed
186 # -- End function
187 .csect badp[RW],3
188 .globl badp[RW] # @badp
189 .align 3
190 .vbyte 8, 0
191 .toc
192L..C0:
193 .tc badp[TC],badp[RW]
194#else
195 .csect [PR],5
196 .file "abc.c"
197 .globl abc[DS] # -- Begin function abc
198 .globl .abc
199 .align 4
200 .csect abc[DS],2
201 .vbyte 4, .abc # @abc
202 .vbyte 4, TOC[TC0]
203 .vbyte 4, 0
204 .csect [PR],5
205.abc:
206# %bb.0: # %entry
207 mflr 0
208 stw 0, 8(1)
209 lwz 3, L..C0(2) # @badp
210 bl $+4
211 lwz 4, 0(3)
212 li 3, 0
213 stw 3, 0(4)
214 lwz 0, 8(1)
215 mtlr 0
216 blr
217L..abc0:
218 .vbyte 4, 0x00000000 # Traceback table begin
219 .byte 0x00 # Version = 0
220 .byte 0x09 # Language = CPlusPlus
221 .byte 0x20 # -IsGlobaLinkage, -IsOutOfLineEpilogOrPrologue
222 # +HasTraceBackTableOffset, -IsInternalProcedure
223 # -HasControlledStorage, -IsTOCless
224 # -IsFloatingPointPresent
225 # -IsFloatingPointOperationLogOrAbortEnabled
226 .byte 0x61 # -IsInterruptHandler, +IsFunctionNamePresent, +IsAllocaUsed
227 # OnConditionDirective = 0, -IsCRSaved, +IsLRSaved
228 .byte 0x00 # -IsBackChainStored, -IsFixup, NumOfFPRsSaved = 0
229 .byte 0x01 # -HasExtensionTable, -HasVectorInfo, NumOfGPRsSaved = 1
230 .byte 0x00 # NumberOfFixedParms = 0
231 .byte 0x01 # NumberOfFPParms = 0, +HasParmsOnStack
232 .vbyte 4, L..abc0-.abc # Function size
233 .vbyte 2, 0x0003 # Function name len = 3
234 .byte "abc" # Function Name
235 .byte 0x1f # AllocaUsed
236 # -- End function
237 .csect badp[RW],2
238 .globl badp[RW] # @badp
239 .align 2
240 .vbyte 4, 0
241 .toc
242L..C0:
243 .tc badp[TC],badp[RW]
244#endif // __64BIT__
245#endif // CXX_CODE
246

source code of libunwind/test/aix_signal_unwind.pass.sh.S