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