]>
Commit | Line | Data |
---|---|---|
54af8e6e SC |
1 | /* Target-dependent code for the Acorn Risc Machine, for GDB, the GNU Debugger. |
2 | Copyright 1988, 1989, 1991, 1992, 1993, 1995 Free Software Foundation, Inc. | |
bd5635a1 RP |
3 | |
4 | This file is part of GDB. | |
5 | ||
54af8e6e | 6 | This program is free software; you can redistribute it and/or modify |
bd5635a1 | 7 | it under the terms of the GNU General Public License as published by |
54af8e6e SC |
8 | the Free Software Foundation; either version 2 of the License, or |
9 | (at your option) any later version. | |
bd5635a1 | 10 | |
54af8e6e | 11 | This program is distributed in the hope that it will be useful, |
bd5635a1 RP |
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 | |
54af8e6e SC |
17 | along with this program; if not, write to the Free Software |
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
bd5635a1 RP |
19 | |
20 | #include "defs.h" | |
bd5635a1 RP |
21 | #include "frame.h" |
22 | #include "inferior.h" | |
bd5635a1 | 23 | |
54af8e6e SC |
24 | #if 0 |
25 | #include "gdbcore.h" | |
bd5635a1 RP |
26 | #include <sys/param.h> |
27 | #include <sys/dir.h> | |
28 | #include <signal.h> | |
29 | #include <sys/ioctl.h> | |
30 | #include <sys/ptrace.h> | |
31 | #include <machine/reg.h> | |
32 | ||
33 | #define N_TXTADDR(hdr) 0x8000 | |
34 | #define N_DATADDR(hdr) (hdr.a_text + 0x8000) | |
35 | ||
bd5635a1 RP |
36 | #include <sys/user.h> /* After a.out.h */ |
37 | #include <sys/file.h> | |
2b576293 | 38 | #include "gdb_stat.h" |
bd5635a1 RP |
39 | |
40 | #include <errno.h> | |
54af8e6e | 41 | #endif |
bd5635a1 RP |
42 | |
43 | \f | |
54af8e6e | 44 | #if 0 |
bd5635a1 RP |
45 | /* Work with core dump and executable files, for GDB. |
46 | This code would be in core.c if it weren't machine-dependent. */ | |
47 | ||
48 | /* Structure to describe the chain of shared libraries used | |
49 | by the execfile. | |
50 | e.g. prog shares Xt which shares X11 which shares c. */ | |
51 | ||
52 | struct shared_library { | |
53 | struct exec_header header; | |
54 | char name[SHLIBLEN]; | |
55 | CORE_ADDR text_start; /* CORE_ADDR of 1st byte of text, this file */ | |
56 | long data_offset; /* offset of data section in file */ | |
57 | int chan; /* file descriptor for the file */ | |
58 | struct shared_library *shares; /* library this one shares */ | |
59 | }; | |
60 | static struct shared_library *shlib = 0; | |
61 | ||
62 | /* Hook for `exec_file_command' command to call. */ | |
63 | ||
64 | extern void (*exec_file_display_hook) (); | |
65 | ||
66 | static CORE_ADDR unshared_text_start; | |
67 | ||
68 | /* extended header from exec file (for shared library info) */ | |
69 | ||
70 | static struct exec_header exec_header; | |
71 | ||
72 | void | |
73 | exec_file_command (filename, from_tty) | |
74 | char *filename; | |
75 | int from_tty; | |
76 | { | |
77 | int val; | |
78 | ||
79 | /* Eliminate all traces of old exec file. | |
80 | Mark text segment as empty. */ | |
81 | ||
82 | if (execfile) | |
83 | free (execfile); | |
84 | execfile = 0; | |
85 | data_start = 0; | |
86 | data_end -= exec_data_start; | |
87 | text_start = 0; | |
88 | unshared_text_start = 0; | |
89 | text_end = 0; | |
90 | exec_data_start = 0; | |
91 | exec_data_end = 0; | |
92 | if (execchan >= 0) | |
93 | close (execchan); | |
94 | execchan = -1; | |
95 | if (shlib) { | |
96 | close_shared_library(shlib); | |
97 | shlib = 0; | |
98 | } | |
99 | ||
100 | /* Now open and digest the file the user requested, if any. */ | |
101 | ||
102 | if (filename) | |
103 | { | |
104 | filename = tilde_expand (filename); | |
105 | make_cleanup (free, filename); | |
106 | ||
107 | execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, | |
108 | &execfile); | |
109 | if (execchan < 0) | |
110 | perror_with_name (filename); | |
111 | ||
112 | { | |
113 | struct stat st_exec; | |
114 | ||
115 | #ifdef HEADER_SEEK_FD | |
116 | HEADER_SEEK_FD (execchan); | |
117 | #endif | |
118 | ||
119 | val = myread (execchan, &exec_header, sizeof exec_header); | |
120 | exec_aouthdr = exec_header.a_exec; | |
121 | ||
122 | if (val < 0) | |
123 | perror_with_name (filename); | |
124 | ||
125 | text_start = 0x8000; | |
126 | ||
127 | /* Look for shared library if needed */ | |
128 | if (exec_header.a_exec.a_magic & MF_USES_SL) | |
129 | shlib = open_shared_library(exec_header.a_shlibname, text_start); | |
130 | ||
131 | text_offset = N_TXTOFF (exec_aouthdr); | |
132 | exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; | |
133 | ||
134 | if (shlib) { | |
135 | unshared_text_start = shared_text_end(shlib) & ~0x7fff; | |
136 | stack_start = shlib->header.a_exec.a_sldatabase; | |
137 | stack_end = STACK_END_ADDR; | |
138 | } else | |
139 | unshared_text_start = 0x8000; | |
140 | text_end = unshared_text_start + exec_aouthdr.a_text; | |
141 | ||
142 | exec_data_start = unshared_text_start + exec_aouthdr.a_text; | |
143 | exec_data_end = exec_data_start + exec_aouthdr.a_data; | |
144 | ||
145 | data_start = exec_data_start; | |
146 | data_end += exec_data_start; | |
147 | ||
148 | fstat (execchan, &st_exec); | |
149 | exec_mtime = st_exec.st_mtime; | |
150 | } | |
151 | ||
152 | validate_files (); | |
153 | } | |
154 | else if (from_tty) | |
155 | printf ("No exec file now.\n"); | |
156 | ||
157 | /* Tell display code (if any) about the changed file name. */ | |
158 | if (exec_file_display_hook) | |
159 | (*exec_file_display_hook) (filename); | |
160 | } | |
54af8e6e | 161 | #endif |
bd5635a1 | 162 | |
54af8e6e | 163 | #if 0 |
bd5635a1 RP |
164 | /* Read from the program's memory (except for inferior processes). |
165 | This function is misnamed, since it only reads, never writes; and | |
166 | since it will use the core file and/or executable file as necessary. | |
167 | ||
168 | It should be extended to write as well as read, FIXME, for patching files. | |
169 | ||
170 | Return 0 if address could be read, EIO if addresss out of bounds. */ | |
171 | ||
172 | int | |
173 | xfer_core_file (memaddr, myaddr, len) | |
174 | CORE_ADDR memaddr; | |
175 | char *myaddr; | |
176 | int len; | |
177 | { | |
178 | register int i; | |
179 | register int val; | |
180 | int xferchan; | |
181 | char **xferfile; | |
182 | int fileptr; | |
183 | int returnval = 0; | |
184 | ||
185 | while (len > 0) | |
186 | { | |
187 | xferfile = 0; | |
188 | xferchan = 0; | |
189 | ||
190 | /* Determine which file the next bunch of addresses reside in, | |
191 | and where in the file. Set the file's read/write pointer | |
192 | to point at the proper place for the desired address | |
193 | and set xferfile and xferchan for the correct file. | |
194 | ||
195 | If desired address is nonexistent, leave them zero. | |
196 | ||
197 | i is set to the number of bytes that can be handled | |
198 | along with the next address. | |
199 | ||
200 | We put the most likely tests first for efficiency. */ | |
201 | ||
202 | /* Note that if there is no core file | |
203 | data_start and data_end are equal. */ | |
204 | if (memaddr >= data_start && memaddr < data_end) | |
205 | { | |
206 | i = min (len, data_end - memaddr); | |
207 | fileptr = memaddr - data_start + data_offset; | |
208 | xferfile = &corefile; | |
209 | xferchan = corechan; | |
210 | } | |
211 | /* Note that if there is no core file | |
212 | stack_start and stack_end define the shared library data. */ | |
213 | else if (memaddr >= stack_start && memaddr < stack_end) | |
214 | { | |
215 | if (corechan < 0) { | |
216 | struct shared_library *lib; | |
217 | for (lib = shlib; lib; lib = lib->shares) | |
218 | if (memaddr >= lib->header.a_exec.a_sldatabase && | |
219 | memaddr < lib->header.a_exec.a_sldatabase + | |
220 | lib->header.a_exec.a_data) | |
221 | break; | |
222 | if (lib) { | |
223 | i = min (len, lib->header.a_exec.a_sldatabase + | |
224 | lib->header.a_exec.a_data - memaddr); | |
225 | fileptr = lib->data_offset + memaddr - | |
226 | lib->header.a_exec.a_sldatabase; | |
227 | xferfile = execfile; | |
228 | xferchan = lib->chan; | |
229 | } | |
230 | } else { | |
231 | i = min (len, stack_end - memaddr); | |
232 | fileptr = memaddr - stack_start + stack_offset; | |
233 | xferfile = &corefile; | |
234 | xferchan = corechan; | |
235 | } | |
236 | } | |
237 | else if (corechan < 0 | |
238 | && memaddr >= exec_data_start && memaddr < exec_data_end) | |
239 | { | |
240 | i = min (len, exec_data_end - memaddr); | |
241 | fileptr = memaddr - exec_data_start + exec_data_offset; | |
242 | xferfile = &execfile; | |
243 | xferchan = execchan; | |
244 | } | |
245 | else if (memaddr >= text_start && memaddr < text_end) | |
246 | { | |
247 | struct shared_library *lib; | |
248 | for (lib = shlib; lib; lib = lib->shares) | |
249 | if (memaddr >= lib->text_start && | |
250 | memaddr < lib->text_start + lib->header.a_exec.a_text) | |
251 | break; | |
252 | if (lib) { | |
253 | i = min (len, lib->header.a_exec.a_text + | |
254 | lib->text_start - memaddr); | |
255 | fileptr = memaddr - lib->text_start + text_offset; | |
256 | xferfile = &execfile; | |
257 | xferchan = lib->chan; | |
258 | } else { | |
259 | i = min (len, text_end - memaddr); | |
260 | fileptr = memaddr - unshared_text_start + text_offset; | |
261 | xferfile = &execfile; | |
262 | xferchan = execchan; | |
263 | } | |
264 | } | |
265 | else if (memaddr < text_start) | |
266 | { | |
267 | i = min (len, text_start - memaddr); | |
268 | } | |
269 | else if (memaddr >= text_end | |
270 | && memaddr < (corechan >= 0? data_start : exec_data_start)) | |
271 | { | |
272 | i = min (len, data_start - memaddr); | |
273 | } | |
274 | else if (corechan >= 0 | |
275 | && memaddr >= data_end && memaddr < stack_start) | |
276 | { | |
277 | i = min (len, stack_start - memaddr); | |
278 | } | |
279 | else if (corechan < 0 && memaddr >= exec_data_end) | |
280 | { | |
281 | i = min (len, - memaddr); | |
282 | } | |
283 | else if (memaddr >= stack_end && stack_end != 0) | |
284 | { | |
285 | i = min (len, - memaddr); | |
286 | } | |
287 | else | |
288 | { | |
289 | /* Address did not classify into one of the known ranges. | |
290 | This shouldn't happen; we catch the endpoints. */ | |
291 | fatal ("Internal: Bad case logic in xfer_core_file."); | |
292 | } | |
293 | ||
294 | /* Now we know which file to use. | |
295 | Set up its pointer and transfer the data. */ | |
296 | if (xferfile) | |
297 | { | |
298 | if (*xferfile == 0) | |
299 | if (xferfile == &execfile) | |
300 | error ("No program file to examine."); | |
301 | else | |
302 | error ("No core dump file or running program to examine."); | |
303 | val = lseek (xferchan, fileptr, 0); | |
304 | if (val < 0) | |
305 | perror_with_name (*xferfile); | |
306 | val = myread (xferchan, myaddr, i); | |
307 | if (val < 0) | |
308 | perror_with_name (*xferfile); | |
309 | } | |
310 | /* If this address is for nonexistent memory, | |
311 | read zeros if reading, or do nothing if writing. | |
312 | Actually, we never right. */ | |
313 | else | |
314 | { | |
54af8e6e | 315 | memset (myaddr, '\0', i); |
bd5635a1 RP |
316 | returnval = EIO; |
317 | } | |
318 | ||
319 | memaddr += i; | |
320 | myaddr += i; | |
321 | len -= i; | |
322 | } | |
323 | return returnval; | |
324 | } | |
54af8e6e | 325 | #endif |
bd5635a1 RP |
326 | \f |
327 | /* APCS (ARM procedure call standard) defines the following prologue: | |
328 | ||
329 | mov ip, sp | |
330 | [stmfd sp!, {a1,a2,a3,a4}] | |
331 | stmfd sp!, {...,fp,ip,lr,pc} | |
332 | [stfe f7, [sp, #-12]!] | |
333 | [stfe f6, [sp, #-12]!] | |
334 | [stfe f5, [sp, #-12]!] | |
335 | [stfe f4, [sp, #-12]!] | |
336 | sub fp, ip, #nn // nn == 20 or 4 depending on second ins | |
337 | */ | |
338 | ||
339 | CORE_ADDR | |
340 | skip_prologue(pc) | |
341 | CORE_ADDR pc; | |
342 | { | |
bd5635a1 | 343 | CORE_ADDR skip_pc = pc; |
54af8e6e SC |
344 | #if 0 |
345 | union insn_fmt op; | |
bd5635a1 RP |
346 | |
347 | op.ins = read_memory_integer(skip_pc, 4); | |
348 | /* look for the "mov ip,sp" */ | |
349 | if (op.generic.type != TYPE_ARITHMETIC || | |
350 | op.arith.opcode != OPCODE_MOV || | |
351 | op.arith.dest != SPTEMP || | |
352 | op.arith.operand2 != SP) return pc; | |
353 | skip_pc += 4; | |
354 | /* skip the "stmfd sp!,{a1,a2,a3,a4}" if its there */ | |
355 | op.ins = read_memory_integer(skip_pc, 4); | |
356 | if (op.generic.type == TYPE_BLOCK_BRANCH && | |
357 | op.generic.subtype == SUBTYPE_BLOCK && | |
358 | op.block.mask == 0xf && | |
359 | op.block.base == SP && | |
360 | op.block.is_load == 0 && | |
361 | op.block.writeback == 1 && | |
362 | op.block.increment == 0 && | |
363 | op.block.before == 1) skip_pc += 4; | |
364 | /* skip the "stmfd sp!,{...,fp,ip,lr,pc} */ | |
365 | op.ins = read_memory_integer(skip_pc, 4); | |
366 | if (op.generic.type != TYPE_BLOCK_BRANCH || | |
367 | op.generic.subtype != SUBTYPE_BLOCK || | |
368 | /* the mask should look like 110110xxxxxx0000 */ | |
369 | (op.block.mask & 0xd800) != 0xd800 || | |
370 | op.block.base != SP || | |
371 | op.block.is_load != 0 || | |
372 | op.block.writeback != 1 || | |
373 | op.block.increment != 0 || | |
374 | op.block.before != 1) return pc; | |
375 | skip_pc += 4; | |
376 | /* check for "sub fp,ip,#nn" */ | |
377 | op.ins = read_memory_integer(skip_pc, 4); | |
378 | if (op.generic.type != TYPE_ARITHMETIC || | |
379 | op.arith.opcode != OPCODE_SUB || | |
380 | op.arith.dest != FP || | |
381 | op.arith.operand1 != SPTEMP) return pc; | |
54af8e6e | 382 | #endif |
bd5635a1 RP |
383 | return skip_pc + 4; |
384 | } | |
385 | ||
54af8e6e SC |
386 | void |
387 | arm_frame_find_saved_regs (frame_info, saved_regs_addr) | |
388 | struct frame_info *frame_info; | |
389 | struct frame_saved_regs *saved_regs_addr; | |
390 | { | |
391 | register int regnum; | |
392 | register int frame; | |
393 | register int next_addr; | |
394 | register int return_data_save; | |
395 | register int saved_register_mask; | |
396 | ||
397 | memset (saved_regs_addr, '\0', sizeof (*saved_regs_addr)); | |
398 | frame = frame_info->frame; | |
399 | return_data_save = read_memory_integer (frame, 4) & 0x03fffffc - 12; | |
400 | saved_register_mask = read_memory_integer (return_data_save, 4); | |
401 | next_addr = frame - 12; | |
402 | for (regnum = 4; regnum < 10; regnum++) | |
403 | if (saved_register_mask & (1 << regnum)) | |
404 | { | |
405 | next_addr -= 4; | |
406 | saved_regs_addr->regs[regnum] = next_addr; | |
407 | } | |
408 | if (read_memory_integer (return_data_save + 4, 4) == 0xed6d7103) | |
409 | { | |
410 | next_addr -= 12; | |
411 | saved_regs_addr->regs[F0_REGNUM + 7] = next_addr; | |
412 | } | |
413 | if (read_memory_integer (return_data_save + 8, 4) == 0xed6d6103) | |
414 | { | |
415 | next_addr -= 12; | |
416 | saved_regs_addr->regs[F0_REGNUM + 6] = next_addr; | |
417 | } | |
418 | if (read_memory_integer (return_data_save + 12, 4) == 0xed6d5103) | |
419 | { | |
420 | next_addr -= 12; | |
421 | saved_regs_addr->regs[F0_REGNUM + 5] = next_addr; | |
422 | } | |
423 | if (read_memory_integer(return_data_save + 16, 4) == 0xed6d4103) | |
424 | { | |
425 | next_addr -= 12; | |
426 | saved_regs_addr->regs[F0_REGNUM + 4] = next_addr; | |
427 | } | |
428 | saved_regs_addr->regs[SP_REGNUM] = next_addr; | |
429 | saved_regs_addr->regs[PC_REGNUM] = frame - 4; | |
430 | saved_regs_addr->regs[PS_REGNUM] = frame - 4; | |
431 | saved_regs_addr->regs[FP_REGNUM] = frame - 12; | |
432 | } | |
433 | ||
bd5635a1 RP |
434 | static void |
435 | print_fpu_flags(flags) | |
436 | int flags; | |
437 | { | |
438 | if (flags & (1 << 0)) fputs("IVO ", stdout); | |
439 | if (flags & (1 << 1)) fputs("DVZ ", stdout); | |
440 | if (flags & (1 << 2)) fputs("OFL ", stdout); | |
441 | if (flags & (1 << 3)) fputs("UFL ", stdout); | |
442 | if (flags & (1 << 4)) fputs("INX ", stdout); | |
443 | putchar('\n'); | |
444 | } | |
445 | ||
446 | void | |
447 | arm_float_info() | |
448 | { | |
449 | register unsigned long status = read_register(FPS_REGNUM); | |
450 | int type; | |
451 | ||
452 | type = (status >> 24) & 127; | |
453 | printf("%s FPU type %d\n", | |
454 | (status & (1<<31)) ? "Hardware" : "Software", | |
455 | type); | |
456 | fputs("mask: ", stdout); | |
457 | print_fpu_flags(status >> 16); | |
458 | fputs("flags: ", stdout); | |
459 | print_fpu_flags(status); | |
460 | } | |
54af8e6e SC |
461 | |
462 | void | |
463 | _initialize_arm_tdep () | |
464 | { | |
2b576293 | 465 | tm_print_insn = print_insn_little_arm; |
54af8e6e SC |
466 | } |
467 | ||
468 | ||
469 | /* FIXME: Fill in with the 'right thing', see asm | |
470 | template in arm-convert.s */ | |
471 | ||
472 | void | |
473 | convert_from_extended (ptr, dbl) | |
474 | void *ptr; | |
475 | double *dbl; | |
476 | { | |
477 | *dbl = *(double*)ptr; | |
478 | } | |
479 | ||
480 | ||
481 | void | |
482 | convert_to_extended (dbl, ptr) | |
483 | void *ptr; | |
484 | double *dbl; | |
485 | { | |
486 | *(double*)ptr = *dbl; | |
487 | } | |
488 |