1 /* DWARF2 EH unwinding support for PowerPC and PowerPC64 Linux.
2 Copyright (C) 2004-2022 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 3, or (at your
9 option) any later version.
11 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
34 #define TOC_SAVE_SLOT 24
36 #define TOC_SAVE_SLOT 40
42 __attribute__ ((vector_size (16))) int vr
[32];
57 unsigned long gpr
[32];
60 unsigned long orig_gpr3
;
70 unsigned long pad1
[4];
79 struct gcc_vregs vregs
;
85 unsigned long pad
[28];
87 unsigned long pad
[12];
89 struct gcc_regs
*regs
;
90 struct gcc_regs rsave
;
95 enum { SIGNAL_FRAMESIZE
= 128 };
98 char gap
[SIGNAL_FRAMESIZE
];
99 struct gcc_ucontext uc
;
100 unsigned long pad
[2];
103 struct gcc_ucontext
*puc
;
106 /* If PC is at a sigreturn trampoline, return a pointer to the
107 regs. Otherwise return NULL. */
109 static struct gcc_regs
*
110 get_regs (struct _Unwind_Context
*context
)
112 const unsigned int *pc
= context
->ra
;
114 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
115 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
116 if (pc
[0] != 0x38210000 + SIGNAL_FRAMESIZE
|| pc
[2] != 0x44000002)
118 if (pc
[1] == 0x38000077)
121 char gap
[SIGNAL_FRAMESIZE
];
122 unsigned long pad
[7];
123 struct gcc_regs
*regs
;
124 } *frame
= (struct sigframe
*) context
->cfa
;
127 else if (pc
[1] == 0x380000AC)
130 /* These old kernel versions never supported ELFv2. */
131 /* This works for 2.4 kernels, but not for 2.6 kernels with vdso
132 because pc isn't pointing into the stack. Can be removed when
133 no one is running 2.4.19 or 2.4.20, the first two ppc64
135 const struct rt_sigframe_24
{
138 struct gcc_ucontext
*puc
;
139 } *frame24
= (const struct rt_sigframe_24
*) context
->ra
;
141 /* Test for magic value in *puc of vdso. */
142 if ((long) frame24
->puc
!= -21 * 8)
143 return frame24
->puc
->regs
;
147 /* This works for 2.4.21 and later kernels. */
148 struct rt_sigframe
*frame
= (struct rt_sigframe
*) context
->cfa
;
149 return frame
->uc
.regs
;
155 #else /* !__powerpc64__ */
157 enum { SIGNAL_FRAMESIZE
= 64 };
160 char gap
[SIGNAL_FRAMESIZE
+ 16];
162 struct gcc_ucontext uc
;
165 static struct gcc_regs
*
166 get_regs (struct _Unwind_Context
*context
)
168 const unsigned int *pc
= context
->ra
;
170 /* li r0, 0x7777; sc (sigreturn old) */
171 /* li r0, 0x0077; sc (sigreturn new) */
172 /* li r0, 0x6666; sc (rt_sigreturn old) */
173 /* li r0, 0x00AC; sc (rt_sigreturn new) */
174 if (pc
[1] != 0x44000002)
176 if (pc
[0] == 0x38007777 || pc
[0] == 0x38000077)
179 char gap
[SIGNAL_FRAMESIZE
];
180 unsigned long pad
[7];
181 struct gcc_regs
*regs
;
182 } *frame
= (struct sigframe
*) context
->cfa
;
185 else if (pc
[0] == 0x38006666 || pc
[0] == 0x380000AC)
187 struct rt_sigframe
*frame
= (struct rt_sigframe
*) context
->cfa
;
188 return frame
->uc
.regs
;
194 /* Do code reading to identify a signal frame, and set the frame
195 state data appropriately. See unwind-dw2.c for the structs. */
197 #define MD_FALLBACK_FRAME_STATE_FOR ppc_fallback_frame_state
199 static _Unwind_Reason_Code
200 ppc_fallback_frame_state (struct _Unwind_Context
*context
,
201 _Unwind_FrameState
*fs
)
203 struct gcc_regs
*regs
= get_regs (context
);
204 struct gcc_vregs
*vregs
;
210 return _URC_NORMAL_STOP
;
212 new_cfa
= regs
->gpr
[__LIBGCC_STACK_POINTER_REGNUM__
];
213 fs
->regs
.cfa_how
= CFA_REG_OFFSET
;
214 fs
->regs
.cfa_reg
= __LIBGCC_STACK_POINTER_REGNUM__
;
215 fs
->regs
.cfa_offset
= new_cfa
- (long) context
->cfa
;
218 fs
->regs
.reg
[2].how
= REG_SAVED_OFFSET
;
219 fs
->regs
.reg
[2].loc
.offset
= (long) ®s
->gpr
[2] - new_cfa
;
221 for (i
= 14; i
< 32; i
++)
223 fs
->regs
.reg
[i
].how
= REG_SAVED_OFFSET
;
224 fs
->regs
.reg
[i
].loc
.offset
= (long) ®s
->gpr
[i
] - new_cfa
;
227 /* The CR is saved in the low 32 bits of regs->ccr. */
228 cr_offset
= (long) ®s
->ccr
- new_cfa
;
229 #ifndef __LITTLE_ENDIAN__
230 cr_offset
+= sizeof (long) - 4;
232 /* In the ELFv1 ABI, CR2 stands in for the whole CR. */
233 fs
->regs
.reg
[R_CR2
].how
= REG_SAVED_OFFSET
;
234 fs
->regs
.reg
[R_CR2
].loc
.offset
= cr_offset
;
236 /* In the ELFv2 ABI, every CR field has a separate CFI entry. */
237 fs
->regs
.reg
[R_CR3
].how
= REG_SAVED_OFFSET
;
238 fs
->regs
.reg
[R_CR3
].loc
.offset
= cr_offset
;
239 fs
->regs
.reg
[R_CR4
].how
= REG_SAVED_OFFSET
;
240 fs
->regs
.reg
[R_CR4
].loc
.offset
= cr_offset
;
243 fs
->regs
.reg
[R_LR
].how
= REG_SAVED_OFFSET
;
244 fs
->regs
.reg
[R_LR
].loc
.offset
= (long) ®s
->link
- new_cfa
;
246 fs
->regs
.reg
[ARG_POINTER_REGNUM
].how
= REG_SAVED_OFFSET
;
247 fs
->regs
.reg
[ARG_POINTER_REGNUM
].loc
.offset
= (long) ®s
->nip
- new_cfa
;
248 fs
->retaddr_column
= ARG_POINTER_REGNUM
;
249 fs
->signal_frame
= 1;
251 /* If we have a FPU... */
252 for (i
= 14; i
< 32; i
++)
254 fs
->regs
.reg
[i
+ 32].how
= REG_SAVED_OFFSET
;
255 fs
->regs
.reg
[i
+ 32].loc
.offset
= (long) ®s
->fpr
[i
] - new_cfa
;
258 /* If we have a VMX unit... */
262 vregs
= ®s
->vregs
;
264 if (regs
->msr
& (1 << 25))
266 for (i
= 20; i
< 32; i
++)
268 fs
->regs
.reg
[i
+ R_VR0
].how
= REG_SAVED_OFFSET
;
269 fs
->regs
.reg
[i
+ R_VR0
].loc
.offset
= (long) &vregs
->vr
[i
] - new_cfa
;
273 fs
->regs
.reg
[R_VRSAVE
].how
= REG_SAVED_OFFSET
;
274 fs
->regs
.reg
[R_VRSAVE
].loc
.offset
= (long) &vregs
->vsave
- new_cfa
;
276 /* If we have SPE register high-parts... we check at compile-time to
277 avoid expanding the code for all other PowerPC. */
279 for (i
= 14; i
< 32; i
++)
281 fs
->regs
.reg
[i
+ FIRST_SPE_HIGH_REGNO
- 4].how
= REG_SAVED_OFFSET
;
282 fs
->regs
.reg
[i
+ FIRST_SPE_HIGH_REGNO
- 4].loc
.offset
283 = (long) ®s
->vregs
- new_cfa
+ 4 * i
;
287 return _URC_NO_REASON
;
290 #define MD_FROB_UPDATE_CONTEXT frob_update_context
293 frob_update_context (struct _Unwind_Context
*context
, _Unwind_FrameState
*fs ATTRIBUTE_UNUSED
)
295 const unsigned int *pc
= (const unsigned int *) context
->ra
;
297 /* Fix up for 2.6.12 - 2.6.16 Linux kernels that have vDSO, but don't
298 have S flag in it. */
300 /* addi r1, r1, 128; li r0, 0x0077; sc (sigreturn) */
301 /* addi r1, r1, 128; li r0, 0x00AC; sc (rt_sigreturn) */
302 if (pc
[0] == 0x38210000 + SIGNAL_FRAMESIZE
303 && (pc
[1] == 0x38000077 || pc
[1] == 0x380000AC)
304 && pc
[2] == 0x44000002)
305 _Unwind_SetSignalFrame (context
, 1);
307 /* li r0, 0x7777; sc (sigreturn old) */
308 /* li r0, 0x0077; sc (sigreturn new) */
309 /* li r0, 0x6666; sc (rt_sigreturn old) */
310 /* li r0, 0x00AC; sc (rt_sigreturn new) */
311 if ((pc
[0] == 0x38007777 || pc
[0] == 0x38000077
312 || pc
[0] == 0x38006666 || pc
[0] == 0x380000AC)
313 && pc
[1] == 0x44000002)
314 _Unwind_SetSignalFrame (context
, 1);
318 if (fs
->regs
.reg
[2].how
== REG_UNSAVED
)
320 /* If the current unwind info (FS) does not contain explicit info
321 saving R2, then we have to do a minor amount of code reading to
322 figure out if it was saved. The big problem here is that the
323 code that does the save/restore is generated by the linker, so
324 we have no good way to determine at compile time what to do. */
325 if (pc
[0] == 0xF8410000 + TOC_SAVE_SLOT
327 /* The ELFv2 linker never generates the old PLT stub form. */
328 || ((pc
[0] & 0xFFFF0000) == 0x3D820000
329 && pc
[1] == 0xF8410000 + TOC_SAVE_SLOT
)
333 /* We are in a plt call stub or r2 adjusting long branch stub,
334 before r2 has been saved. Keep REG_UNSAVED. */
339 = (unsigned int *) _Unwind_GetGR (context
, R_LR
);
340 if (insn
&& *insn
== 0xE8410000 + TOC_SAVE_SLOT
)
341 _Unwind_SetGRPtr (context
, 2, context
->cfa
+ TOC_SAVE_SLOT
);
343 /* ELFv2 does not use this function pointer call sequence. */
344 else if (pc
[0] == 0x4E800421
345 && pc
[1] == 0xE8410000 + TOC_SAVE_SLOT
)
347 /* We are at the bctrl instruction in a call via function
348 pointer. gcc always emits the load of the new R2 just
349 before the bctrl so this is the first and only place
350 we need to use the stored R2. */
351 _Unwind_Word sp
= _Unwind_GetGR (context
, 1);
352 _Unwind_SetGRPtr (context
, 2, (void *)(sp
+ TOC_SAVE_SLOT
));
360 #define MD_BACKCHAIN_FALLBACK ppc_backchain_fallback
364 /* Stores the list of addresses. */
366 struct unwind_link
*unwind_link
;
368 /* Number of addresses currently stored. */
370 /* Maximum number of addresses. */
374 /* This is the stack layout we see with every stack frame.
375 Note that every routine is required by the ABI to lay out the stack
378 +----------------+ +-----------------+
379 %r1 -> | previous frame--------> | previous frame--->... --> NULL
381 | cr save | | cr save |
383 | (unused) | | lr save |
384 +----------------+ +-----------------+
386 The CR save is only present on 64-bit ABIs.
390 struct frame_layout
*backchain
;
399 ppc_backchain_fallback (struct _Unwind_Context
*context
, void *a
)
401 struct frame_layout
*current
;
402 struct trace_arg
*arg
= a
;
405 /* Get the last address computed. */
406 current
= context
->cfa
;
408 /* If the trace CFA is not the context CFA the backtrace is done. */
409 if (arg
== NULL
|| arg
->cfa
!= current
)
412 /* Start with next address. */
413 current
= current
->backchain
;
415 for (count
= arg
->count
; current
!= NULL
; current
= current
->backchain
)
417 arg
->array
[count
] = current
->lr_save
;
419 /* Check if the symbol is the signal trampoline and get the interrupted
420 symbol address from the trampoline saved area. */
421 context
->ra
= current
->lr_save
;
422 if (current
->lr_save
&& get_regs (context
))
424 struct rt_sigframe
*sigframe
= (struct rt_sigframe
*) current
;
425 if (count
+ 1 == arg
->size
)
427 arg
->array
[++count
] = (void *) sigframe
->uc
.rsave
.nip
;
428 current
= (void *) sigframe
->uc
.rsave
.gpr
[1];
430 if (count
++ >= arg
->size
)
434 arg
->count
= count
-1;