]>
Commit | Line | Data |
---|---|---|
1 | /* DWARF2 EH unwinding support for PA Linux. | |
2 | Copyright (C) 2004-2024 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
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. | |
19 | ||
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/>. */ | |
24 | ||
25 | ||
26 | /* Do code reading to identify a signal frame, and set the frame | |
27 | state data appropriately. See unwind-dw2.c for the structs. */ | |
28 | ||
29 | /* Don't use this if inhibit_libc is set. | |
30 | The build for this target will fail trying to include missing headers. */ | |
31 | #ifndef inhibit_libc | |
32 | #include <signal.h> | |
33 | #include <sys/ucontext.h> | |
34 | ||
35 | /* Return TRUE if read access to *P is allowed. */ | |
36 | ||
37 | static inline long | |
38 | pa32_read_access_ok (void *p) | |
39 | { | |
40 | long ret; | |
41 | ||
42 | __asm__ ("proberi (%1),3,%0" : "=r" (ret) : "r" (p) :); | |
43 | return ret; | |
44 | } | |
45 | ||
46 | /* Unfortunately, because of various bugs and changes to the kernel, | |
47 | we have several cases to deal with. | |
48 | ||
49 | In 2.4, the signal trampoline is 4 words, and (CONTEXT)->ra should | |
50 | point directly at the beginning of the trampoline and struct rt_sigframe. | |
51 | ||
52 | In <= 2.6.5-rc2-pa3, the signal trampoline is 9 words, and | |
53 | (CONTEXT)->ra points at the 4th word in the trampoline structure. This | |
54 | is wrong, it should point at the 5th word. This is fixed in 2.6.5-rc2-pa4. | |
55 | ||
56 | To detect these cases, we first take (CONTEXT)->ra, align it to 64-bytes | |
57 | to get the beginning of the signal frame, and then check offsets 0, 4 | |
58 | and 5 to see if we found the beginning of the trampoline. This will | |
59 | tell us how to locate the sigcontext structure. | |
60 | ||
61 | Note that with a 2.4 64-bit kernel, the signal context is not properly | |
62 | passed back to userspace so the unwind will not work correctly. | |
63 | ||
64 | There is also a bug in various glibc versions. The (CONTEXT)->ra | |
65 | for the outermost frame is not marked as undefined, so we need to | |
66 | check whether read access is allowed for all the accesses used in | |
67 | searching for the signal trampoline. */ | |
68 | ||
69 | #define MD_FALLBACK_FRAME_STATE_FOR pa32_fallback_frame_state | |
70 | ||
71 | static _Unwind_Reason_Code | |
72 | pa32_fallback_frame_state (struct _Unwind_Context *context, | |
73 | _Unwind_FrameState *fs) | |
74 | { | |
75 | unsigned long sp = (unsigned long)context->ra & ~63; | |
76 | unsigned int *pc = (unsigned int *)sp; | |
77 | unsigned long off; | |
78 | _Unwind_Ptr new_cfa; | |
79 | int i; | |
80 | struct sigcontext *sc; | |
81 | struct rt_sigframe { | |
82 | siginfo_t info; | |
83 | ucontext_t uc; | |
84 | } *frame; | |
85 | ||
86 | /* rt_sigreturn trampoline: | |
87 | 3419000x ldi 0, %r25 or ldi 1, %r25 (x = 0 or 2) | |
88 | 3414015a ldi __NR_rt_sigreturn, %r20 | |
89 | e4008200 be,l 0x100(%sr2, %r0), %sr0, %r31 | |
90 | 08000240 nop */ | |
91 | ||
92 | if (pa32_read_access_ok (pc) | |
93 | && (pc[0] == 0x34190000 || pc[0] == 0x34190002)) | |
94 | off = 4*4; | |
95 | else if (pa32_read_access_ok (&pc[4]) | |
96 | && (pc[4] == 0x34190000 || pc[4] == 0x34190002)) | |
97 | { | |
98 | pc += 4; | |
99 | off = 10 * 4; | |
100 | } | |
101 | else if (pa32_read_access_ok (&pc[5]) | |
102 | && (pc[5] == 0x34190000 || pc[5] == 0x34190002)) | |
103 | { | |
104 | pc += 5; | |
105 | off = 10 * 4; | |
106 | } | |
107 | else | |
108 | { | |
109 | /* We may have to unwind through an alternate signal stack. | |
110 | We assume that the alignment of the alternate signal stack | |
111 | is BIGGEST_ALIGNMENT (i.e., that it has been allocated using | |
112 | malloc). As a result, we can't distinguish trampolines | |
113 | used prior to 2.6.5-rc2-pa4. However after 2.6.5-rc2-pa4, | |
114 | the return address of a signal trampoline will be on an odd | |
115 | word boundary and we can then determine the frame offset. */ | |
116 | sp = (unsigned long)context->ra; | |
117 | pc = (unsigned int *)sp; | |
118 | if ((sp & 4) | |
119 | && pa32_read_access_ok (pc) | |
120 | && (pc[0] == 0x34190000 || pc[0] == 0x34190002)) | |
121 | off = 5 * 4; | |
122 | else | |
123 | return _URC_END_OF_STACK; | |
124 | } | |
125 | ||
126 | if (!pa32_read_access_ok (&pc[3]) | |
127 | || pc[1] != 0x3414015a | |
128 | || pc[2] != 0xe4008200 | |
129 | || pc[3] != 0x08000240) | |
130 | return _URC_END_OF_STACK; | |
131 | ||
132 | frame = (struct rt_sigframe *)(sp + off); | |
133 | sc = (struct sigcontext *)&frame->uc.uc_mcontext; | |
134 | ||
135 | new_cfa = sc->sc_gr[30]; | |
136 | fs->regs.cfa_how = CFA_REG_OFFSET; | |
137 | fs->regs.cfa_reg = 30; | |
138 | fs->regs.cfa_offset = new_cfa - (long) context->cfa; | |
139 | for (i = 1; i <= 31; i++) | |
140 | { | |
141 | fs->regs.how[i] = REG_SAVED_OFFSET; | |
142 | fs->regs.reg[i].loc.offset = (long)&sc->sc_gr[i] - new_cfa; | |
143 | } | |
144 | for (i = 4; i <= 31; i++) | |
145 | { | |
146 | /* FP regs have left and right halves */ | |
147 | fs->regs.how[2*i+24] = REG_SAVED_OFFSET; | |
148 | fs->regs.reg[2*i+24].loc.offset | |
149 | = (long)&sc->sc_fr[i] - new_cfa; | |
150 | fs->regs.how[2*i+24+1] = REG_SAVED_OFFSET; | |
151 | fs->regs.reg[2*i+24+1].loc.offset | |
152 | = (long)&sc->sc_fr[i] + 4 - new_cfa; | |
153 | } | |
154 | fs->regs.how[88] = REG_SAVED_OFFSET; | |
155 | fs->regs.reg[88].loc.offset = (long) &sc->sc_sar - new_cfa; | |
156 | fs->regs.how[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__] | |
157 | = REG_SAVED_OFFSET; | |
158 | fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset | |
159 | = (long) &sc->sc_iaoq[0] - new_cfa; | |
160 | fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__; | |
161 | fs->signal_frame = 1; | |
162 | return _URC_NO_REASON; | |
163 | } | |
164 | #endif /* inhibit_libc */ |