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