Commit | Line | Data |
---|---|---|
5ecb7103 KB |
1 | /* Target-dependent code for GNU/Linux running on the Fujitsu FR-V, |
2 | for GDB. | |
3 | Copyright 2004 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | Boston, MA 02111-1307, USA. */ | |
21 | ||
22 | #include "defs.h" | |
23 | #include "target.h" | |
24 | #include "frame.h" | |
25 | #include "osabi.h" | |
26 | #include "elf-bfd.h" | |
27 | #include "elf/frv.h" | |
28 | #include "frv-tdep.h" | |
29 | ||
30 | /* Define the size (in bytes) of an FR-V instruction. */ | |
31 | static const int frv_instr_size = 4; | |
32 | ||
33 | enum { | |
34 | NORMAL_SIGTRAMP = 1, | |
35 | RT_SIGTRAMP = 2 | |
36 | }; | |
37 | ||
38 | static int | |
39 | frv_linux_pc_in_sigtramp (CORE_ADDR pc, char *name) | |
40 | { | |
41 | char buf[frv_instr_size]; | |
42 | LONGEST instr; | |
43 | int retval = 0; | |
44 | ||
45 | if (target_read_memory (pc, buf, sizeof buf) != 0) | |
46 | return 0; | |
47 | ||
48 | instr = extract_unsigned_integer (buf, sizeof buf); | |
49 | ||
50 | if (instr == 0x8efc0077) /* setlos #__NR_sigreturn, gr7 */ | |
51 | retval = NORMAL_SIGTRAMP; | |
52 | else if (instr -= 0x8efc00ad) /* setlos #__NR_rt_sigreturn, gr7 */ | |
53 | retval = RT_SIGTRAMP; | |
54 | else | |
55 | return 0; | |
56 | ||
57 | if (target_read_memory (pc + frv_instr_size, buf, sizeof buf) != 0) | |
58 | return 0; | |
59 | instr = extract_unsigned_integer (buf, sizeof buf); | |
60 | if (instr != 0xc0700000) /* tira gr0, 0 */ | |
61 | return 0; | |
62 | ||
63 | /* If we get this far, we'll return a non-zero value, either | |
64 | NORMAL_SIGTRAMP (1) or RT_SIGTRAMP (2). */ | |
65 | return retval; | |
66 | } | |
67 | ||
68 | /* Given NEXT_FRAME, "callee" frame of the sigtramp frame that we | |
69 | wish to decode, and REGNO, one of the frv register numbers defined | |
70 | in frv-tdep.h, return the address of the saved register (corresponding | |
71 | to REGNO) in the sigtramp frame. Return -1 if the register is not | |
72 | found in the sigtramp frame. The magic numbers in the code below | |
73 | were computed by examining the following kernel structs: | |
74 | ||
75 | From arch/frvnommu/signal.c: | |
76 | ||
77 | struct sigframe | |
78 | { | |
79 | void (*pretcode)(void); | |
80 | int sig; | |
81 | struct sigcontext sc; | |
82 | unsigned long extramask[_NSIG_WORDS-1]; | |
83 | uint32_t retcode[2]; | |
84 | }; | |
85 | ||
86 | struct rt_sigframe | |
87 | { | |
88 | void (*pretcode)(void); | |
89 | int sig; | |
90 | struct siginfo *pinfo; | |
91 | void *puc; | |
92 | struct siginfo info; | |
93 | struct ucontext uc; | |
94 | uint32_t retcode[2]; | |
95 | }; | |
96 | ||
97 | From include/asm-frvnommu/ucontext.h: | |
98 | ||
99 | struct ucontext { | |
100 | unsigned long uc_flags; | |
101 | struct ucontext *uc_link; | |
102 | stack_t uc_stack; | |
103 | struct sigcontext uc_mcontext; | |
104 | sigset_t uc_sigmask; | |
105 | }; | |
106 | ||
107 | From include/asm-frvnommu/sigcontext.h: | |
108 | ||
109 | struct sigcontext { | |
110 | struct user_context sc_context; | |
111 | unsigned long sc_oldmask; | |
112 | } __attribute__((aligned(8))); | |
113 | ||
114 | From include/asm-frvnommu/registers.h: | |
115 | struct user_int_regs | |
116 | { | |
117 | unsigned long psr; | |
118 | unsigned long isr; | |
119 | unsigned long ccr; | |
120 | unsigned long cccr; | |
121 | unsigned long lr; | |
122 | unsigned long lcr; | |
123 | unsigned long pc; | |
124 | unsigned long __status; | |
125 | unsigned long syscallno; | |
126 | unsigned long orig_gr8; | |
127 | unsigned long gner[2]; | |
128 | unsigned long long iacc[1]; | |
129 | ||
130 | union { | |
131 | unsigned long tbr; | |
132 | unsigned long gr[64]; | |
133 | }; | |
134 | }; | |
135 | ||
136 | struct user_fpmedia_regs | |
137 | { | |
138 | unsigned long fr[64]; | |
139 | unsigned long fner[2]; | |
140 | unsigned long msr[2]; | |
141 | unsigned long acc[8]; | |
142 | unsigned char accg[8]; | |
143 | unsigned long fsr[1]; | |
144 | }; | |
145 | ||
146 | struct user_context | |
147 | { | |
148 | struct user_int_regs i; | |
149 | struct user_fpmedia_regs f; | |
150 | ||
151 | void *extension; | |
152 | } __attribute__((aligned(8))); */ | |
153 | ||
154 | static CORE_ADDR | |
155 | frv_linux_sigcontext_reg_addr (struct frame_info *next_frame, int regno, | |
156 | CORE_ADDR *sc_addr_cache_ptr) | |
157 | { | |
158 | CORE_ADDR sc_addr; | |
159 | ||
160 | if (sc_addr_cache_ptr && *sc_addr_cache_ptr) | |
161 | { | |
162 | sc_addr = *sc_addr_cache_ptr; | |
163 | } | |
164 | else | |
165 | { | |
166 | CORE_ADDR pc, sp; | |
167 | char buf[4]; | |
168 | int tramp_type; | |
169 | ||
170 | pc = frame_pc_unwind (next_frame); | |
171 | tramp_type = frv_linux_pc_in_sigtramp (pc, 0); | |
172 | ||
173 | frame_unwind_register (next_frame, sp_regnum, buf); | |
174 | sp = extract_unsigned_integer (buf, sizeof buf); | |
175 | ||
176 | if (tramp_type == NORMAL_SIGTRAMP) | |
177 | { | |
178 | /* For a normal sigtramp frame, the sigcontext struct starts | |
179 | at SP + 8. */ | |
180 | sc_addr = sp + 8; | |
181 | } | |
182 | else if (tramp_type == RT_SIGTRAMP) | |
183 | { | |
184 | /* For a realtime sigtramp frame, SP + 12 contains a pointer | |
185 | to the a ucontext struct. The ucontext struct contains | |
186 | a sigcontext struct starting 12 bytes in. */ | |
187 | if (target_read_memory (sp + 12, buf, sizeof buf) != 0) | |
188 | { | |
189 | warning ("Can't read realtime sigtramp frame."); | |
190 | return 0; | |
191 | } | |
192 | sc_addr = extract_unsigned_integer (buf, sizeof buf); | |
193 | sc_addr += 12; | |
194 | } | |
195 | else | |
196 | internal_error (__FILE__, __LINE__, "not a signal trampoline"); | |
197 | ||
198 | if (sc_addr_cache_ptr) | |
199 | *sc_addr_cache_ptr = sc_addr; | |
200 | } | |
201 | ||
202 | switch (regno) | |
203 | { | |
204 | case psr_regnum : | |
205 | return sc_addr + 0; | |
206 | /* sc_addr + 4 has "isr", the Integer Status Register. */ | |
207 | case ccr_regnum : | |
208 | return sc_addr + 8; | |
209 | case cccr_regnum : | |
210 | return sc_addr + 12; | |
211 | case lr_regnum : | |
212 | return sc_addr + 16; | |
213 | case lcr_regnum : | |
214 | return sc_addr + 20; | |
215 | case pc_regnum : | |
216 | return sc_addr + 24; | |
217 | /* sc_addr + 28 is __status, the exception status. | |
218 | sc_addr + 32 is syscallno, the syscall number or -1. | |
219 | sc_addr + 36 is orig_gr8, the original syscall arg #1. | |
220 | sc_addr + 40 is gner[0]. | |
221 | sc_addr + 44 is gner[1]. */ | |
222 | case iacc0h_regnum : | |
223 | return sc_addr + 48; | |
224 | case iacc0l_regnum : | |
225 | return sc_addr + 52; | |
226 | default : | |
227 | if (first_gpr_regnum <= regno && regno <= last_gpr_regnum) | |
228 | return sc_addr + 56 + 4 * (regno - first_gpr_regnum); | |
229 | else if (first_fpr_regnum <= regno && regno <= last_fpr_regnum) | |
230 | return sc_addr + 312 + 4 * (regno - first_fpr_regnum); | |
231 | else | |
232 | return -1; /* not saved. */ | |
233 | } | |
234 | } | |
235 | ||
236 | static void | |
237 | frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | |
238 | { | |
239 | /* When the FR-V Linux kernel calls a signal handler, the return | |
240 | address points to a bit of code on the stack. This function is | |
241 | used to identify this bit of code as a signal trampoline in order | |
242 | to support backtracing through calls to signal handlers. */ | |
f561f026 | 243 | set_gdbarch_deprecated_pc_in_sigtramp (gdbarch, frv_linux_pc_in_sigtramp); |
5ecb7103 KB |
244 | frv_set_sigcontext_reg_addr (gdbarch, frv_linux_sigcontext_reg_addr); |
245 | } | |
246 | ||
247 | static enum gdb_osabi | |
248 | frv_linux_elf_osabi_sniffer (bfd *abfd) | |
249 | { | |
250 | int elf_flags; | |
251 | ||
252 | elf_flags = elf_elfheader (abfd)->e_flags; | |
253 | ||
254 | /* Assume GNU/Linux if using the FDPIC ABI. If/when another OS shows | |
255 | up that uses this ABI, we'll need to start using .note sections | |
256 | or some such. */ | |
257 | if (elf_flags & EF_FRV_FDPIC) | |
258 | return GDB_OSABI_LINUX; | |
259 | else | |
260 | return GDB_OSABI_UNKNOWN; | |
261 | } | |
262 | ||
263 | /* Provide a prototype to silence -Wmissing-prototypes. */ | |
264 | void _initialize_frv_linux_tdep (void); | |
265 | ||
266 | void | |
267 | _initialize_frv_linux_tdep (void) | |
268 | { | |
269 | gdbarch_register_osabi (bfd_arch_frv, 0, GDB_OSABI_LINUX, frv_linux_init_abi); | |
270 | gdbarch_register_osabi_sniffer (bfd_arch_frv, | |
271 | bfd_target_elf_flavour, | |
272 | frv_linux_elf_osabi_sniffer); | |
273 | } |