Commit | Line | Data |
---|---|---|
2aa830e4 DJ |
1 | /* Target-dependent code for Linux/MIPS. |
2 | Copyright 2001 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GDB. | |
5 | ||
6 | This program 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 2 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program 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 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | #include "defs.h" | |
22 | #include "gdbcore.h" | |
23 | #include "target.h" | |
24 | #include "solib-svr4.h" | |
25 | ||
26 | /* Copied from <asm/elf.h>. */ | |
27 | #define ELF_NGREG 45 | |
28 | #define ELF_NFPREG 33 | |
29 | ||
30 | typedef unsigned char elf_greg_t[4]; | |
31 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | |
32 | ||
33 | typedef unsigned char elf_fpreg_t[8]; | |
34 | typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; | |
35 | ||
36 | /* 0 - 31 are integer registers, 32 - 63 are fp registers. */ | |
37 | #define FPR_BASE 32 | |
38 | #define PC 64 | |
39 | #define CAUSE 65 | |
40 | #define BADVADDR 66 | |
41 | #define MMHI 67 | |
42 | #define MMLO 68 | |
43 | #define FPC_CSR 69 | |
44 | #define FPC_EIR 70 | |
45 | ||
46 | #define EF_REG0 6 | |
47 | #define EF_REG31 37 | |
48 | #define EF_LO 38 | |
49 | #define EF_HI 39 | |
50 | #define EF_CP0_EPC 40 | |
51 | #define EF_CP0_BADVADDR 41 | |
52 | #define EF_CP0_STATUS 42 | |
53 | #define EF_CP0_CAUSE 43 | |
54 | ||
55 | #define EF_SIZE 180 | |
56 | ||
57 | /* Figure out where the longjmp will land. | |
58 | We expect the first arg to be a pointer to the jmp_buf structure from | |
59 | which we extract the pc (JB_PC) that we will land at. The pc is copied | |
60 | into PC. This routine returns 1 on success. */ | |
61 | ||
62 | int | |
63 | mips_linux_get_longjmp_target (CORE_ADDR *pc) | |
64 | { | |
65 | CORE_ADDR jb_addr; | |
66 | char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT]; | |
67 | ||
68 | jb_addr = read_register (A0_REGNUM); | |
69 | ||
70 | if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf, | |
71 | TARGET_PTR_BIT / TARGET_CHAR_BIT)) | |
72 | return 0; | |
73 | ||
74 | *pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT); | |
75 | ||
76 | return 1; | |
77 | } | |
78 | ||
79 | /* Unpack an elf_gregset_t into GDB's register cache. */ | |
80 | ||
81 | void | |
82 | supply_gregset (elf_gregset_t *gregsetp) | |
83 | { | |
84 | int regi; | |
85 | elf_greg_t *regp = *gregsetp; | |
86 | static char zerobuf[MAX_REGISTER_RAW_SIZE] = {0}; | |
87 | ||
88 | for (regi = EF_REG0; regi <= EF_REG31; regi++) | |
89 | supply_register ((regi - EF_REG0), (char *)(regp + regi)); | |
90 | ||
91 | supply_register (LO_REGNUM, (char *)(regp + EF_LO)); | |
92 | supply_register (HI_REGNUM, (char *)(regp + EF_HI)); | |
93 | ||
94 | supply_register (PC_REGNUM, (char *)(regp + EF_CP0_EPC)); | |
95 | supply_register (BADVADDR_REGNUM, (char *)(regp + EF_CP0_BADVADDR)); | |
96 | supply_register (PS_REGNUM, (char *)(regp + EF_CP0_STATUS)); | |
97 | supply_register (CAUSE_REGNUM, (char *)(regp + EF_CP0_CAUSE)); | |
98 | ||
99 | /* Fill inaccessible registers with zero. */ | |
100 | supply_register (FP_REGNUM, zerobuf); | |
101 | supply_register (UNUSED_REGNUM, zerobuf); | |
102 | for (regi = FIRST_EMBED_REGNUM; regi < LAST_EMBED_REGNUM; regi++) | |
103 | supply_register (regi, zerobuf); | |
104 | } | |
105 | ||
106 | /* Pack our registers (or one register) into an elf_gregset_t. */ | |
107 | ||
108 | void | |
109 | fill_gregset (elf_gregset_t *gregsetp, int regno) | |
110 | { | |
111 | int regaddr, regi; | |
112 | elf_greg_t *regp = *gregsetp; | |
113 | void *src, *dst; | |
114 | ||
115 | if (regno == -1) | |
116 | { | |
117 | memset (regp, 0, sizeof (elf_gregset_t)); | |
118 | for (regi = 0; regi < 32; regi++) | |
119 | fill_gregset (gregsetp, regi); | |
120 | fill_gregset (gregsetp, LO_REGNUM); | |
121 | fill_gregset (gregsetp, HI_REGNUM); | |
122 | fill_gregset (gregsetp, PC_REGNUM); | |
123 | fill_gregset (gregsetp, BADVADDR_REGNUM); | |
124 | fill_gregset (gregsetp, PS_REGNUM); | |
125 | fill_gregset (gregsetp, CAUSE_REGNUM); | |
126 | ||
127 | return; | |
128 | } | |
129 | ||
130 | if (regno < 32) | |
131 | { | |
132 | src = ®isters[REGISTER_BYTE (regno)]; | |
133 | dst = regp + regno + EF_REG0; | |
134 | memcpy (dst, src, sizeof (elf_greg_t)); | |
135 | return; | |
136 | } | |
137 | ||
138 | regaddr = -1; | |
139 | switch (regno) | |
140 | { | |
141 | case LO_REGNUM: | |
142 | regaddr = EF_LO; | |
143 | break; | |
144 | case HI_REGNUM: | |
145 | regaddr = EF_HI; | |
146 | break; | |
147 | case PC_REGNUM: | |
148 | regaddr = EF_CP0_EPC; | |
149 | break; | |
150 | case BADVADDR_REGNUM: | |
151 | regaddr = EF_CP0_BADVADDR; | |
152 | break; | |
153 | case PS_REGNUM: | |
154 | regaddr = EF_CP0_STATUS; | |
155 | break; | |
156 | case CAUSE_REGNUM: | |
157 | regaddr = EF_CP0_CAUSE; | |
158 | break; | |
159 | } | |
160 | ||
161 | if (regaddr != -1) | |
162 | { | |
163 | src = ®isters[REGISTER_BYTE (regno)]; | |
164 | dst = regp + regaddr; | |
165 | memcpy (dst, src, sizeof (elf_greg_t)); | |
166 | } | |
167 | } | |
168 | ||
169 | /* Likewise, unpack an elf_fpregset_t. */ | |
170 | ||
171 | void | |
172 | supply_fpregset (elf_fpregset_t *fpregsetp) | |
173 | { | |
174 | register int regi; | |
175 | static char zerobuf[MAX_REGISTER_RAW_SIZE] = {0}; | |
176 | ||
177 | for (regi = 0; regi < 32; regi++) | |
178 | supply_register (FP0_REGNUM + regi, | |
179 | (char *)(*fpregsetp + regi)); | |
180 | ||
181 | supply_register (FCRCS_REGNUM, (char *)(*fpregsetp + 32)); | |
182 | ||
183 | /* FIXME: how can we supply FCRIR_REGNUM? The ABI doesn't tell us. */ | |
184 | supply_register (FCRIR_REGNUM, zerobuf); | |
185 | } | |
186 | ||
187 | /* Likewise, pack one or all floating point registers into an | |
188 | elf_fpregset_t. */ | |
189 | ||
190 | void | |
191 | fill_fpregset (elf_fpregset_t *fpregsetp, int regno) | |
192 | { | |
193 | char *from, *to; | |
194 | ||
195 | if ((regno >= FP0_REGNUM) && (regno < FP0_REGNUM + 32)) | |
196 | { | |
197 | from = (char *) ®isters[REGISTER_BYTE (regno)]; | |
198 | to = (char *) (*fpregsetp + regno - FP0_REGNUM); | |
199 | memcpy (to, from, REGISTER_RAW_SIZE (regno - FP0_REGNUM)); | |
200 | } | |
201 | else if (regno == FCRCS_REGNUM) | |
202 | { | |
203 | from = (char *) ®isters[REGISTER_BYTE (regno)]; | |
204 | to = (char *) (*fpregsetp + 32); | |
205 | memcpy (to, from, REGISTER_RAW_SIZE (regno)); | |
206 | } | |
207 | else if (regno == -1) | |
208 | { | |
209 | int regi; | |
210 | ||
211 | for (regi = 0; regi < 32; regi++) | |
212 | fill_fpregset (fpregsetp, FP0_REGNUM + regi); | |
213 | fill_fpregset(fpregsetp, FCRCS_REGNUM); | |
214 | } | |
215 | } | |
216 | ||
217 | /* Map gdb internal register number to ptrace ``address''. | |
218 | These ``addresses'' are normally defined in <asm/ptrace.h>. */ | |
219 | ||
220 | CORE_ADDR | |
221 | register_addr (int regno, CORE_ADDR blockend) | |
222 | { | |
223 | int regaddr; | |
224 | ||
225 | if (regno < 0 || regno >= NUM_REGS) | |
226 | error ("Bogon register number %d.", regno); | |
227 | ||
228 | if (regno < 32) | |
229 | regaddr = regno; | |
230 | else if ((regno >= FP0_REGNUM) && (regno < FP0_REGNUM + 32)) | |
231 | regaddr = FPR_BASE + (regno - FP0_REGNUM); | |
232 | else if (regno == PC_REGNUM) | |
233 | regaddr = PC; | |
234 | else if (regno == CAUSE_REGNUM) | |
235 | regaddr = CAUSE; | |
236 | else if (regno == BADVADDR_REGNUM) | |
237 | regaddr = BADVADDR; | |
238 | else if (regno == LO_REGNUM) | |
239 | regaddr = MMLO; | |
240 | else if (regno == HI_REGNUM) | |
241 | regaddr = MMHI; | |
242 | else if (regno == FCRCS_REGNUM) | |
243 | regaddr = FPC_CSR; | |
244 | else if (regno == FCRIR_REGNUM) | |
245 | regaddr = FPC_EIR; | |
246 | else | |
247 | error ("Unknowable register number %d.", regno); | |
248 | ||
249 | return regaddr; | |
250 | } | |
251 | ||
252 | /* Use a local version of this function to get the correct types for | |
253 | regsets, until multi-arch core support is ready. */ | |
254 | ||
255 | static void | |
256 | fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, | |
257 | int which, CORE_ADDR reg_addr) | |
258 | { | |
259 | elf_gregset_t gregset; | |
260 | elf_fpregset_t fpregset; | |
261 | ||
262 | if (which == 0) | |
263 | { | |
264 | if (core_reg_size != sizeof (gregset)) | |
265 | { | |
266 | warning ("wrong size gregset struct in core file"); | |
267 | } | |
268 | else | |
269 | { | |
270 | memcpy ((char *) &gregset, core_reg_sect, sizeof (gregset)); | |
271 | supply_gregset (&gregset); | |
272 | } | |
273 | } | |
274 | else if (which == 2) | |
275 | { | |
276 | if (core_reg_size != sizeof (fpregset)) | |
277 | { | |
278 | warning ("wrong size fpregset struct in core file"); | |
279 | } | |
280 | else | |
281 | { | |
282 | memcpy ((char *) &fpregset, core_reg_sect, sizeof (fpregset)); | |
283 | supply_fpregset (&fpregset); | |
284 | } | |
285 | } | |
286 | } | |
287 | ||
288 | /* Register that we are able to handle ELF file formats using standard | |
289 | procfs "regset" structures. */ | |
290 | ||
291 | static struct core_fns regset_core_fns = | |
292 | { | |
293 | bfd_target_elf_flavour, /* core_flavour */ | |
294 | default_check_format, /* check_format */ | |
295 | default_core_sniffer, /* core_sniffer */ | |
296 | fetch_core_registers, /* core_read_registers */ | |
297 | NULL /* next */ | |
298 | }; | |
299 | ||
300 | /* Fetch (and possibly build) an appropriate link_map_offsets | |
301 | structure for native Linux/MIPS targets using the struct offsets | |
302 | defined in link.h (but without actual reference to that file). | |
303 | ||
304 | This makes it possible to access Linux/MIPS shared libraries from a | |
305 | GDB that was not built on an Linux/MIPS host (for cross debugging). */ | |
306 | ||
307 | struct link_map_offsets * | |
308 | mips_linux_svr4_fetch_link_map_offsets (void) | |
309 | { | |
310 | static struct link_map_offsets lmo; | |
311 | static struct link_map_offsets *lmp = NULL; | |
312 | ||
313 | if (lmp == NULL) | |
314 | { | |
315 | lmp = &lmo; | |
316 | ||
317 | lmo.r_debug_size = 8; /* The actual size is 20 bytes, but | |
318 | this is all we need. */ | |
319 | lmo.r_map_offset = 4; | |
320 | lmo.r_map_size = 4; | |
321 | ||
322 | lmo.link_map_size = 20; | |
323 | ||
324 | lmo.l_addr_offset = 0; | |
325 | lmo.l_addr_size = 4; | |
326 | ||
327 | lmo.l_name_offset = 4; | |
328 | lmo.l_name_size = 4; | |
329 | ||
330 | lmo.l_next_offset = 12; | |
331 | lmo.l_next_size = 4; | |
332 | ||
333 | lmo.l_prev_offset = 16; | |
334 | lmo.l_prev_size = 4; | |
335 | } | |
336 | ||
337 | return lmp; | |
338 | } | |
339 | ||
340 | void | |
d1bacddc | 341 | _initialize_mips_linux_tdep (void) |
2aa830e4 DJ |
342 | { |
343 | add_core_fns (®set_core_fns); | |
344 | } |