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