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 | |
40 | const char* namesExpected[] = {"handler" , "abc" , "bar" , "foo" , "main" , |
41 | "__start" }; |
42 | char *namesObtained[NAME_ARRAY_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
43 | |
44 | int funcIndex = 0; |
45 | |
46 | // Get the function name from traceback table. |
47 | char *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 | |
91 | extern "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. |
109 | extern "C" void abc(); |
110 | |
111 | #else |
112 | volatile int *null = 0; |
113 | |
114 | // abc() raises signal SIGSEGV and does not save the link register. |
115 | extern "C" __attribute__((noinline)) void abc() { |
116 | // Produce a SIGSEGV. |
117 | *null = 0; |
118 | } |
119 | #endif |
120 | |
121 | extern "C" __attribute__((noinline)) void bar() { |
122 | abc(); |
123 | } |
124 | |
125 | extern "C" __attribute__((noinline)) void foo() { |
126 | bar(); |
127 | } |
128 | |
129 | int 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 |
167 | L..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 |
192 | L..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 |
217 | L..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 |
242 | L..C0: |
243 | .tc badp[TC],badp[RW] |
244 | #endif // __64BIT__ |
245 | #endif // CXX_CODE |
246 | |