Commit | Line | Data |
---|---|---|
f2b9069b WJ |
1 | /* Low level interface to ptrace, for GDB when running on the Intel 386. |
2 | Copyright (C) 1988, 1989 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GDB. | |
5 | ||
6 | GDB 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 1, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GDB 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 GDB; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | ||
20 | #include <stdio.h> | |
21 | #include "defs.h" | |
22 | #include "param.h" | |
23 | #include "frame.h" | |
24 | #include "inferior.h" | |
25 | ||
26 | #ifdef USG | |
27 | #include <sys/types.h> | |
28 | #endif | |
29 | ||
30 | #include <sys/param.h> | |
31 | #include <sys/dir.h> | |
32 | #include <signal.h> | |
33 | #include <sys/user.h> | |
34 | #include <sys/ioctl.h> | |
35 | #include <fcntl.h> | |
36 | ||
37 | #ifdef COFF_ENCAPSULATE | |
38 | #include "a.out.encap.h" | |
39 | #else | |
40 | #include <a.out.h> | |
41 | #endif | |
42 | ||
43 | #ifndef N_SET_MAGIC | |
44 | #ifdef COFF_FORMAT | |
45 | #define N_SET_MAGIC(exec, val) ((exec).magic = (val)) | |
46 | #else | |
47 | #define N_SET_MAGIC(exec, val) ((exec).a_magic = (val)) | |
48 | #endif | |
49 | #endif | |
50 | ||
51 | #include <sys/file.h> | |
52 | #include <sys/stat.h> | |
53 | ||
54 | #include <sys/reg.h> | |
55 | ||
56 | extern int errno; | |
57 | \f | |
58 | /* This function simply calls ptrace with the given arguments. | |
59 | It exists so that all calls to ptrace are isolated in this | |
60 | machine-dependent file. */ | |
61 | int | |
62 | call_ptrace (request, pid, arg3, arg4) | |
63 | int request, pid, arg3, arg4; | |
64 | { | |
65 | return ptrace (request, pid, arg3, arg4); | |
66 | } | |
67 | ||
68 | kill_inferior () | |
69 | { | |
70 | if (remote_debugging) | |
71 | return; | |
72 | if (inferior_pid == 0) | |
73 | return; | |
74 | ptrace (8, inferior_pid, 0, 0); | |
75 | wait (0); | |
76 | inferior_died (); | |
77 | } | |
78 | ||
79 | /* This is used when GDB is exiting. It gives less chance of error.*/ | |
80 | ||
81 | kill_inferior_fast () | |
82 | { | |
83 | if (remote_debugging) | |
84 | return; | |
85 | if (inferior_pid == 0) | |
86 | return; | |
87 | ptrace (8, inferior_pid, 0, 0); | |
88 | wait (0); | |
89 | } | |
90 | ||
91 | /* Resume execution of the inferior process. | |
92 | If STEP is nonzero, single-step it. | |
93 | If SIGNAL is nonzero, give it that signal. */ | |
94 | ||
95 | void | |
96 | resume (step, signal) | |
97 | int step; | |
98 | int signal; | |
99 | { | |
100 | errno = 0; | |
101 | if (remote_debugging) | |
102 | remote_resume (step, signal); | |
103 | else | |
104 | { | |
105 | ptrace (step ? 9 : 7, inferior_pid, 1, signal); | |
106 | if (errno) | |
107 | perror_with_name ("ptrace"); | |
108 | } | |
109 | } | |
110 | \f | |
111 | void | |
112 | fetch_inferior_registers () | |
113 | { | |
114 | register int regno; | |
115 | register unsigned int regaddr; | |
116 | char buf[MAX_REGISTER_RAW_SIZE]; | |
117 | register int i; | |
118 | ||
119 | struct user u; | |
120 | unsigned int offset = (char *) &u.u_ar0 - (char *) &u; | |
121 | offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; | |
122 | ||
123 | for (regno = 0; regno < NUM_REGS; regno++) | |
124 | { | |
125 | regaddr = register_addr (regno, offset); | |
126 | for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int)) | |
127 | { | |
128 | *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0); | |
129 | regaddr += sizeof (int); | |
130 | } | |
131 | supply_register (regno, buf); | |
132 | } | |
133 | } | |
134 | ||
135 | /* Store our register values back into the inferior. | |
136 | If REGNO is -1, do this for all registers. | |
137 | Otherwise, REGNO specifies which register (so we can save time). */ | |
138 | ||
139 | store_inferior_registers (regno) | |
140 | int regno; | |
141 | { | |
142 | register unsigned int regaddr; | |
143 | char buf[80]; | |
144 | ||
145 | struct user u; | |
146 | unsigned int offset = (char *) &u.u_ar0 - (char *) &u; | |
147 | offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR; | |
148 | ||
149 | if (regno >= 0) | |
150 | { | |
151 | regaddr = register_addr (regno, offset); | |
152 | errno = 0; | |
153 | ptrace (6, inferior_pid, regaddr, read_register (regno)); | |
154 | if (errno != 0) | |
155 | { | |
156 | sprintf (buf, "writing register number %d", regno); | |
157 | perror_with_name (buf); | |
158 | } | |
159 | } | |
160 | else for (regno = 0; regno < NUM_REGS; regno++) | |
161 | { | |
162 | regaddr = register_addr (regno, offset); | |
163 | errno = 0; | |
164 | ptrace (6, inferior_pid, regaddr, read_register (regno)); | |
165 | if (errno != 0) | |
166 | { | |
167 | sprintf (buf, "writing register number %d", regno); | |
168 | perror_with_name (buf); | |
169 | } | |
170 | } | |
171 | } | |
172 | \f | |
173 | /* Copy LEN bytes from inferior's memory starting at MEMADDR | |
174 | to debugger memory starting at MYADDR. | |
175 | On failure (cannot read from inferior, usually because address is out | |
176 | of bounds) returns the value of errno. */ | |
177 | ||
178 | int | |
179 | read_inferior_memory (memaddr, myaddr, len) | |
180 | CORE_ADDR memaddr; | |
181 | char *myaddr; | |
182 | int len; | |
183 | { | |
184 | register int i; | |
185 | /* Round starting address down to longword boundary. */ | |
186 | register CORE_ADDR addr = memaddr & - sizeof (int); | |
187 | /* Round ending address up; get number of longwords that makes. */ | |
188 | register int count | |
189 | = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); | |
190 | /* Allocate buffer of that many longwords. */ | |
191 | register int *buffer = (int *) alloca (count * sizeof (int)); | |
192 | extern int errno; | |
193 | ||
194 | /* Read all the longwords */ | |
195 | for (i = 0; i < count; i++, addr += sizeof (int)) | |
196 | { | |
197 | errno = 0; | |
198 | if (remote_debugging) | |
199 | buffer[i] = remote_fetch_word (addr); | |
200 | else | |
201 | buffer[i] = ptrace (1, inferior_pid, addr, 0); | |
202 | if (errno) | |
203 | return errno; | |
204 | } | |
205 | ||
206 | /* Copy appropriate bytes out of the buffer. */ | |
207 | bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | /* Copy LEN bytes of data from debugger memory at MYADDR | |
212 | to inferior's memory at MEMADDR. | |
213 | On failure (cannot write the inferior) | |
214 | returns the value of errno. */ | |
215 | ||
216 | int | |
217 | write_inferior_memory (memaddr, myaddr, len) | |
218 | CORE_ADDR memaddr; | |
219 | char *myaddr; | |
220 | int len; | |
221 | { | |
222 | register int i; | |
223 | /* Round starting address down to longword boundary. */ | |
224 | register CORE_ADDR addr = memaddr & - sizeof (int); | |
225 | /* Round ending address up; get number of longwords that makes. */ | |
226 | register int count | |
227 | = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); | |
228 | /* Allocate buffer of that many longwords. */ | |
229 | register int *buffer = (int *) alloca (count * sizeof (int)); | |
230 | extern int errno; | |
231 | ||
232 | /* Fill start and end extra bytes of buffer with existing memory data. */ | |
233 | ||
234 | if (remote_debugging) | |
235 | buffer[0] = remote_fetch_word (addr); | |
236 | else | |
237 | buffer[0] = ptrace (1, inferior_pid, addr, 0); | |
238 | ||
239 | if (count > 1) | |
240 | { | |
241 | if (remote_debugging) | |
242 | buffer[count - 1] | |
243 | = remote_fetch_word (addr + (count - 1) * sizeof (int)); | |
244 | else | |
245 | buffer[count - 1] | |
246 | = ptrace (1, inferior_pid, | |
247 | addr + (count - 1) * sizeof (int), 0); | |
248 | } | |
249 | ||
250 | /* Copy data to be written over corresponding part of buffer */ | |
251 | ||
252 | bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); | |
253 | ||
254 | /* Write the entire buffer. */ | |
255 | ||
256 | for (i = 0; i < count; i++, addr += sizeof (int)) | |
257 | { | |
258 | errno = 0; | |
259 | if (remote_debugging) | |
260 | remote_store_word (addr, buffer[i]); | |
261 | else | |
262 | ptrace (4, inferior_pid, addr, buffer[i]); | |
263 | if (errno) | |
264 | return errno; | |
265 | } | |
266 | ||
267 | return 0; | |
268 | } | |
269 | \f | |
270 | /* Work with core dump and executable files, for GDB. | |
271 | This code would be in core.c if it weren't machine-dependent. */ | |
272 | ||
273 | #ifndef N_TXTADDR | |
274 | #define N_TXTADDR(hdr) 0 | |
275 | #endif /* no N_TXTADDR */ | |
276 | ||
277 | #ifndef N_DATADDR | |
278 | #define N_DATADDR(hdr) hdr.a_text | |
279 | #endif /* no N_DATADDR */ | |
280 | ||
281 | /* Make COFF and non-COFF names for things a little more compatible | |
282 | to reduce conditionals later. */ | |
283 | ||
284 | #ifndef COFF_FORMAT | |
285 | #ifndef AOUTHDR | |
286 | #define AOUTHDR struct exec | |
287 | #endif | |
288 | #endif | |
289 | ||
290 | extern char *sys_siglist[]; | |
291 | ||
292 | ||
293 | /* Hook for `exec_file_command' command to call. */ | |
294 | ||
295 | extern void (*exec_file_display_hook) (); | |
296 | ||
297 | /* File names of core file and executable file. */ | |
298 | ||
299 | extern char *corefile; | |
300 | extern char *execfile; | |
301 | ||
302 | /* Descriptors on which core file and executable file are open. | |
303 | Note that the execchan is closed when an inferior is created | |
304 | and reopened if the inferior dies or is killed. */ | |
305 | ||
306 | extern int corechan; | |
307 | extern int execchan; | |
308 | ||
309 | /* Last modification time of executable file. | |
310 | Also used in source.c to compare against mtime of a source file. */ | |
311 | ||
312 | extern int exec_mtime; | |
313 | ||
314 | /* Virtual addresses of bounds of the two areas of memory in the core file. */ | |
315 | ||
316 | extern CORE_ADDR data_start; | |
317 | extern CORE_ADDR data_end; | |
318 | extern CORE_ADDR stack_start; | |
319 | extern CORE_ADDR stack_end; | |
320 | ||
321 | /* Virtual addresses of bounds of two areas of memory in the exec file. | |
322 | Note that the data area in the exec file is used only when there is no core file. */ | |
323 | ||
324 | extern CORE_ADDR text_start; | |
325 | extern CORE_ADDR text_end; | |
326 | ||
327 | extern CORE_ADDR exec_data_start; | |
328 | extern CORE_ADDR exec_data_end; | |
329 | ||
330 | /* Address in executable file of start of text area data. */ | |
331 | ||
332 | extern int text_offset; | |
333 | ||
334 | /* Address in executable file of start of data area data. */ | |
335 | ||
336 | extern int exec_data_offset; | |
337 | ||
338 | /* Address in core file of start of data area data. */ | |
339 | ||
340 | extern int data_offset; | |
341 | ||
342 | /* Address in core file of start of stack area data. */ | |
343 | ||
344 | extern int stack_offset; | |
345 | ||
346 | #ifdef COFF_FORMAT | |
347 | /* various coff data structures */ | |
348 | ||
349 | extern FILHDR file_hdr; | |
350 | extern SCNHDR text_hdr; | |
351 | extern SCNHDR data_hdr; | |
352 | ||
353 | #endif /* not COFF_FORMAT */ | |
354 | ||
355 | /* a.out header saved in core file. */ | |
356 | ||
357 | extern AOUTHDR core_aouthdr; | |
358 | ||
359 | /* a.out header of exec file. */ | |
360 | ||
361 | extern AOUTHDR exec_aouthdr; | |
362 | ||
363 | extern void validate_files (); | |
364 | \f | |
365 | core_file_command (filename, from_tty) | |
366 | char *filename; | |
367 | int from_tty; | |
368 | { | |
369 | int val; | |
370 | extern char registers[]; | |
371 | ||
372 | /* Discard all vestiges of any previous core file | |
373 | and mark data and stack spaces as empty. */ | |
374 | ||
375 | if (corefile) | |
376 | free (corefile); | |
377 | corefile = 0; | |
378 | ||
379 | if (corechan >= 0) | |
380 | close (corechan); | |
381 | corechan = -1; | |
382 | ||
383 | data_start = 0; | |
384 | data_end = 0; | |
385 | stack_start = STACK_END_ADDR; | |
386 | stack_end = STACK_END_ADDR; | |
387 | ||
388 | /* Now, if a new core file was specified, open it and digest it. */ | |
389 | ||
390 | if (filename) | |
391 | { | |
392 | filename = tilde_expand (filename); | |
393 | make_cleanup (free, filename); | |
394 | ||
395 | if (have_inferior_p ()) | |
396 | error ("To look at a core file, you must kill the inferior with \"kill\"."); | |
397 | corechan = open (filename, O_RDONLY, 0); | |
398 | if (corechan < 0) | |
399 | perror_with_name (filename); | |
400 | /* 4.2-style (and perhaps also sysV-style) core dump file. */ | |
401 | { | |
402 | struct user u; | |
403 | ||
404 | int reg_offset; | |
405 | ||
406 | val = myread (corechan, &u, sizeof u); | |
407 | if (val < 0) | |
408 | perror_with_name (filename); | |
409 | data_start = exec_data_start; | |
410 | ||
411 | data_end = data_start + NBPG * u.u_dsize; | |
412 | stack_start = stack_end - NBPG * u.u_ssize; | |
413 | data_offset = NBPG * UPAGES; | |
414 | stack_offset = NBPG * (UPAGES + u.u_dsize); | |
415 | reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR; | |
416 | ||
417 | /* I don't know where to find this info. | |
418 | So, for now, mark it as not available. */ | |
419 | /* N_SET_MAGIC (core_aouthdr, 0); */ | |
420 | bzero ((char *) &core_aouthdr, sizeof core_aouthdr); | |
421 | ||
422 | /* Read the register values out of the core file and store | |
423 | them where `read_register' will find them. */ | |
424 | ||
425 | { | |
426 | register int regno; | |
427 | ||
428 | for (regno = 0; regno < NUM_REGS; regno++) | |
429 | { | |
430 | char buf[MAX_REGISTER_RAW_SIZE]; | |
431 | ||
432 | val = lseek (corechan, register_addr (regno, reg_offset), 0); | |
433 | if (val < 0) | |
434 | perror_with_name (filename); | |
435 | ||
436 | val = myread (corechan, buf, sizeof buf); | |
437 | if (val < 0) | |
438 | perror_with_name (filename); | |
439 | supply_register (regno, buf); | |
440 | } | |
441 | } | |
442 | } | |
443 | if (filename[0] == '/') | |
444 | corefile = savestring (filename, strlen (filename)); | |
445 | else | |
446 | { | |
447 | corefile = concat (current_directory, "/", filename); | |
448 | } | |
449 | ||
450 | set_current_frame ( create_new_frame (read_register (FP_REGNUM), | |
451 | read_pc ())); | |
452 | select_frame (get_current_frame (), 0); | |
453 | validate_files (); | |
454 | } | |
455 | else if (from_tty) | |
456 | printf ("No core file now.\n"); | |
457 | } | |
458 | \f | |
459 | exec_file_command (filename, from_tty) | |
460 | char *filename; | |
461 | int from_tty; | |
462 | { | |
463 | int val; | |
464 | ||
465 | /* Eliminate all traces of old exec file. | |
466 | Mark text segment as empty. */ | |
467 | ||
468 | if (execfile) | |
469 | free (execfile); | |
470 | execfile = 0; | |
471 | data_start = 0; | |
472 | data_end -= exec_data_start; | |
473 | text_start = 0; | |
474 | text_end = 0; | |
475 | exec_data_start = 0; | |
476 | exec_data_end = 0; | |
477 | if (execchan >= 0) | |
478 | close (execchan); | |
479 | execchan = -1; | |
480 | ||
481 | /* Now open and digest the file the user requested, if any. */ | |
482 | ||
483 | if (filename) | |
484 | { | |
485 | filename = tilde_expand (filename); | |
486 | make_cleanup (free, filename); | |
487 | ||
488 | execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, | |
489 | &execfile); | |
490 | if (execchan < 0) | |
491 | perror_with_name (filename); | |
492 | ||
493 | #ifdef COFF_FORMAT | |
494 | { | |
495 | int aout_hdrsize; | |
496 | int num_sections; | |
497 | ||
498 | if (read_file_hdr (execchan, &file_hdr) < 0) | |
499 | error ("\"%s\": not in executable format.", execfile); | |
500 | ||
501 | aout_hdrsize = file_hdr.f_opthdr; | |
502 | num_sections = file_hdr.f_nscns; | |
503 | ||
504 | if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0) | |
505 | error ("\"%s\": can't read optional aouthdr", execfile); | |
506 | ||
507 | if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections, | |
508 | aout_hdrsize) < 0) | |
509 | error ("\"%s\": can't read text section header", execfile); | |
510 | ||
511 | if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections, | |
512 | aout_hdrsize) < 0) | |
513 | error ("\"%s\": can't read data section header", execfile); | |
514 | ||
515 | text_start = exec_aouthdr.text_start; | |
516 | text_end = text_start + exec_aouthdr.tsize; | |
517 | text_offset = text_hdr.s_scnptr; | |
518 | exec_data_start = exec_aouthdr.data_start; | |
519 | exec_data_end = exec_data_start + exec_aouthdr.dsize; | |
520 | exec_data_offset = data_hdr.s_scnptr; | |
521 | data_start = exec_data_start; | |
522 | data_end += exec_data_start; | |
523 | exec_mtime = file_hdr.f_timdat; | |
524 | } | |
525 | #else /* not COFF_FORMAT */ | |
526 | { | |
527 | struct stat st_exec; | |
528 | ||
529 | #ifdef HEADER_SEEK_FD | |
530 | HEADER_SEEK_FD (execchan); | |
531 | #endif | |
532 | ||
533 | val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR)); | |
534 | ||
535 | if (val < 0) | |
536 | perror_with_name (filename); | |
537 | ||
538 | text_start = N_TXTADDR (exec_aouthdr); | |
539 | exec_data_start = N_DATADDR (exec_aouthdr); | |
540 | ||
541 | text_offset = N_TXTOFF (exec_aouthdr); | |
542 | exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; | |
543 | ||
544 | text_end = text_start + exec_aouthdr.a_text; | |
545 | exec_data_end = exec_data_start + exec_aouthdr.a_data; | |
546 | data_start = exec_data_start; | |
547 | data_end += exec_data_start; | |
548 | ||
549 | fstat (execchan, &st_exec); | |
550 | exec_mtime = st_exec.st_mtime; | |
551 | } | |
552 | #endif /* not COFF_FORMAT */ | |
553 | ||
554 | validate_files (); | |
555 | } | |
556 | else if (from_tty) | |
557 | printf ("No exec file now.\n"); | |
558 | ||
559 | /* Tell display code (if any) about the changed file name. */ | |
560 | if (exec_file_display_hook) | |
561 | (*exec_file_display_hook) (filename); | |
562 | } | |
563 | ||
564 | /* helper functions for m-i386.h */ | |
565 | ||
566 | /* stdio style buffering to minimize calls to ptrace */ | |
567 | static CORE_ADDR codestream_next_addr; | |
568 | static CORE_ADDR codestream_addr; | |
569 | static unsigned char codestream_buf[sizeof (int)]; | |
570 | static int codestream_off; | |
571 | static int codestream_cnt; | |
572 | ||
573 | #define codestream_tell() (codestream_addr + codestream_off) | |
574 | #define codestream_peek() (codestream_cnt == 0 ? \ | |
575 | codestream_fill(1): codestream_buf[codestream_off]) | |
576 | #define codestream_get() (codestream_cnt-- == 0 ? \ | |
577 | codestream_fill(0) : codestream_buf[codestream_off++]) | |
578 | ||
579 | static unsigned char | |
580 | codestream_fill (peek_flag) | |
581 | { | |
582 | codestream_addr = codestream_next_addr; | |
583 | codestream_next_addr += sizeof (int); | |
584 | codestream_off = 0; | |
585 | codestream_cnt = sizeof (int); | |
586 | read_memory (codestream_addr, | |
587 | (unsigned char *)codestream_buf, | |
588 | sizeof (int)); | |
589 | ||
590 | if (peek_flag) | |
591 | return (codestream_peek()); | |
592 | else | |
593 | return (codestream_get()); | |
594 | } | |
595 | ||
596 | static void | |
597 | codestream_seek (place) | |
598 | { | |
599 | codestream_next_addr = place & -sizeof (int); | |
600 | codestream_cnt = 0; | |
601 | codestream_fill (1); | |
602 | while (codestream_tell() != place) | |
603 | codestream_get (); | |
604 | } | |
605 | ||
606 | static void | |
607 | codestream_read (buf, count) | |
608 | unsigned char *buf; | |
609 | { | |
610 | unsigned char *p; | |
611 | int i; | |
612 | p = buf; | |
613 | for (i = 0; i < count; i++) | |
614 | *p++ = codestream_get (); | |
615 | } | |
616 | ||
617 | /* next instruction is a jump, move to target */ | |
618 | static | |
619 | i386_follow_jump () | |
620 | { | |
621 | int long_delta; | |
622 | short short_delta; | |
623 | char byte_delta; | |
624 | int data16; | |
625 | int pos; | |
626 | ||
627 | pos = codestream_tell (); | |
628 | ||
629 | data16 = 0; | |
630 | if (codestream_peek () == 0x66) | |
631 | { | |
632 | codestream_get (); | |
633 | data16 = 1; | |
634 | } | |
635 | ||
636 | switch (codestream_get ()) | |
637 | { | |
638 | case 0xe9: | |
639 | /* relative jump: if data16 == 0, disp32, else disp16 */ | |
640 | if (data16) | |
641 | { | |
642 | codestream_read ((unsigned char *)&short_delta, 2); | |
643 | pos += short_delta + 3; /* include size of jmp inst */ | |
644 | } | |
645 | else | |
646 | { | |
647 | codestream_read ((unsigned char *)&long_delta, 4); | |
648 | pos += long_delta + 5; | |
649 | } | |
650 | break; | |
651 | case 0xeb: | |
652 | /* relative jump, disp8 (ignore data16) */ | |
653 | codestream_read ((unsigned char *)&byte_delta, 1); | |
654 | pos += byte_delta + 2; | |
655 | break; | |
656 | } | |
657 | codestream_seek (pos + data16); | |
658 | } | |
659 | ||
660 | /* | |
661 | * find & return amound a local space allocated, and advance codestream to | |
662 | * first register push (if any) | |
663 | * | |
664 | * if entry sequence doesn't make sense, return -1, and leave | |
665 | * codestream pointer random | |
666 | */ | |
667 | static long | |
668 | i386_get_frame_setup (pc) | |
669 | { | |
670 | unsigned char op; | |
671 | ||
672 | codestream_seek (pc); | |
673 | ||
674 | i386_follow_jump (); | |
675 | ||
676 | op = codestream_get (); | |
677 | ||
678 | if (op == 0x58) /* popl %eax */ | |
679 | { | |
680 | /* | |
681 | * this function must start with | |
682 | * | |
683 | * popl %eax 0x58 | |
684 | * xchgl %eax, (%esp) 0x87 0x04 0x24 | |
685 | * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00 | |
686 | * | |
687 | * (the system 5 compiler puts out the second xchg | |
688 | * inst, and the assembler doesn't try to optimize it, | |
689 | * so the 'sib' form gets generated) | |
690 | * | |
691 | * this sequence is used to get the address of the return | |
692 | * buffer for a function that returns a structure | |
693 | */ | |
694 | int pos; | |
695 | unsigned char buf[4]; | |
696 | static unsigned char proto1[3] = { 0x87,0x04,0x24 }; | |
697 | static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 }; | |
698 | pos = codestream_tell (); | |
699 | codestream_read (buf, 4); | |
700 | if (bcmp (buf, proto1, 3) == 0) | |
701 | pos += 3; | |
702 | else if (bcmp (buf, proto2, 4) == 0) | |
703 | pos += 4; | |
704 | ||
705 | codestream_seek (pos); | |
706 | op = codestream_get (); /* update next opcode */ | |
707 | } | |
708 | ||
709 | if (op == 0x55) /* pushl %esp */ | |
710 | { | |
711 | /* check for movl %esp, %ebp - can be written two ways */ | |
712 | switch (codestream_get ()) | |
713 | { | |
714 | case 0x8b: | |
715 | if (codestream_get () != 0xec) | |
716 | return (-1); | |
717 | break; | |
718 | case 0x89: | |
719 | if (codestream_get () != 0xe5) | |
720 | return (-1); | |
721 | break; | |
722 | default: | |
723 | return (-1); | |
724 | } | |
725 | /* check for stack adjustment | |
726 | * | |
727 | * subl $XXX, %esp | |
728 | * | |
729 | * note: you can't subtract a 16 bit immediate | |
730 | * from a 32 bit reg, so we don't have to worry | |
731 | * about a data16 prefix | |
732 | */ | |
733 | op = codestream_peek (); | |
734 | if (op == 0x83) | |
735 | { | |
736 | /* subl with 8 bit immed */ | |
737 | codestream_get (); | |
738 | if (codestream_get () != 0xec) | |
739 | return (-1); | |
740 | /* subl with signed byte immediate | |
741 | * (though it wouldn't make sense to be negative) | |
742 | */ | |
743 | return (codestream_get()); | |
744 | } | |
745 | else if (op == 0x81) | |
746 | { | |
747 | /* subl with 32 bit immed */ | |
748 | int locals; | |
749 | codestream_get(); | |
750 | if (codestream_get () != 0xec) | |
751 | return (-1); | |
752 | /* subl with 32 bit immediate */ | |
753 | codestream_read ((unsigned char *)&locals, 4); | |
754 | return (locals); | |
755 | } | |
756 | else | |
757 | { | |
758 | return (0); | |
759 | } | |
760 | } | |
761 | else if (op == 0xc8) | |
762 | { | |
763 | /* enter instruction: arg is 16 bit unsigned immed */ | |
764 | unsigned short slocals; | |
765 | codestream_read ((unsigned char *)&slocals, 2); | |
766 | codestream_get (); /* flush final byte of enter instruction */ | |
767 | return (slocals); | |
768 | } | |
769 | return (-1); | |
770 | } | |
771 | ||
772 | /* Return number of args passed to a frame. | |
773 | Can return -1, meaning no way to tell. */ | |
774 | ||
775 | /* on the 386, the instruction following the call could be: | |
776 | * popl %ecx - one arg | |
777 | * addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits | |
778 | * anything else - zero args | |
779 | */ | |
780 | ||
781 | int | |
782 | i386_frame_num_args (fi) | |
783 | struct frame_info fi; | |
784 | { | |
785 | int retpc; | |
786 | unsigned char op; | |
787 | struct frame_info *pfi; | |
788 | ||
789 | pfi = get_prev_frame_info ((fi)); | |
790 | if (pfi == 0) | |
791 | { | |
792 | /* Note: this can happen if we are looking at the frame for | |
793 | main, because FRAME_CHAIN_VALID won't let us go into | |
794 | start. If we have debugging symbols, that's not really | |
795 | a big deal; it just means it will only show as many arguments | |
796 | to main as are declared. */ | |
797 | return -1; | |
798 | } | |
799 | else | |
800 | { | |
801 | retpc = pfi->pc; | |
802 | op = read_memory_integer (retpc, 1); | |
803 | if (op == 0x59) | |
804 | /* pop %ecx */ | |
805 | return 1; | |
806 | else if (op == 0x83) | |
807 | { | |
808 | op = read_memory_integer (retpc+1, 1); | |
809 | if (op == 0xc4) | |
810 | /* addl $<signed imm 8 bits>, %esp */ | |
811 | return (read_memory_integer (retpc+2,1)&0xff)/4; | |
812 | else | |
813 | return 0; | |
814 | } | |
815 | else if (op == 0x81) | |
816 | { /* add with 32 bit immediate */ | |
817 | op = read_memory_integer (retpc+1, 1); | |
818 | if (op == 0xc4) | |
819 | /* addl $<imm 32>, %esp */ | |
820 | return read_memory_integer (retpc+2, 4) / 4; | |
821 | else | |
822 | return 0; | |
823 | } | |
824 | else | |
825 | { | |
826 | return 0; | |
827 | } | |
828 | } | |
829 | } | |
830 | ||
831 | /* | |
832 | * parse the first few instructions of the function to see | |
833 | * what registers were stored. | |
834 | * | |
835 | * We handle these cases: | |
836 | * | |
837 | * The startup sequence can be at the start of the function, | |
838 | * or the function can start with a branch to startup code at the end. | |
839 | * | |
840 | * %ebp can be set up with either the 'enter' instruction, or | |
841 | * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful, | |
842 | * but was once used in the sys5 compiler) | |
843 | * | |
844 | * Local space is allocated just below the saved %ebp by either the | |
845 | * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has | |
846 | * a 16 bit unsigned argument for space to allocate, and the | |
847 | * 'addl' instruction could have either a signed byte, or | |
848 | * 32 bit immediate. | |
849 | * | |
850 | * Next, the registers used by this function are pushed. In | |
851 | * the sys5 compiler they will always be in the order: %edi, %esi, %ebx | |
852 | * (and sometimes a harmless bug causes it to also save but not restore %eax); | |
853 | * however, the code below is willing to see the pushes in any order, | |
854 | * and will handle up to 8 of them. | |
855 | * | |
856 | * If the setup sequence is at the end of the function, then the | |
857 | * next instruction will be a branch back to the start. | |
858 | */ | |
859 | ||
860 | i386_frame_find_saved_regs (fip, fsrp) | |
861 | struct frame_info *fip; | |
862 | struct frame_saved_regs *fsrp; | |
863 | { | |
864 | unsigned long locals; | |
865 | unsigned char *p; | |
866 | unsigned char op; | |
867 | CORE_ADDR dummy_bottom; | |
868 | CORE_ADDR adr; | |
869 | int i; | |
870 | ||
871 | bzero (fsrp, sizeof *fsrp); | |
872 | ||
873 | /* if frame is the end of a dummy, compute where the | |
874 | * beginning would be | |
875 | */ | |
876 | dummy_bottom = fip->frame - 4 - NUM_REGS*4 - CALL_DUMMY_LENGTH; | |
877 | ||
878 | /* check if the PC is in the stack, in a dummy frame */ | |
879 | if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) | |
880 | { | |
881 | /* all regs were saved by push_call_dummy () */ | |
882 | adr = fip->frame - 4; | |
883 | for (i = 0; i < NUM_REGS; i++) | |
884 | { | |
885 | fsrp->regs[i] = adr; | |
886 | adr -= 4; | |
887 | } | |
888 | return; | |
889 | } | |
890 | ||
891 | locals = i386_get_frame_setup (get_pc_function_start (fip->pc)); | |
892 | ||
893 | if (locals >= 0) | |
894 | { | |
895 | adr = fip->frame - 4 - locals; | |
896 | for (i = 0; i < 8; i++) | |
897 | { | |
898 | op = codestream_get (); | |
899 | if (op < 0x50 || op > 0x57) | |
900 | break; | |
901 | fsrp->regs[op - 0x50] = adr; | |
902 | adr -= 4; | |
903 | } | |
904 | } | |
905 | ||
906 | fsrp->regs[PC_REGNUM] = fip->frame + 4; | |
907 | fsrp->regs[FP_REGNUM] = fip->frame; | |
908 | } | |
909 | ||
910 | /* return pc of first real instruction */ | |
911 | i386_skip_prologue (pc) | |
912 | { | |
913 | unsigned char op; | |
914 | int i; | |
915 | ||
916 | if (i386_get_frame_setup (pc) < 0) | |
917 | return (pc); | |
918 | ||
919 | /* found valid frame setup - codestream now points to | |
920 | * start of push instructions for saving registers | |
921 | */ | |
922 | ||
923 | /* skip over register saves */ | |
924 | for (i = 0; i < 8; i++) | |
925 | { | |
926 | op = codestream_peek (); | |
927 | /* break if not pushl inst */ | |
928 | if (op < 0x50 || op > 0x57) | |
929 | break; | |
930 | codestream_get (); | |
931 | } | |
932 | ||
933 | i386_follow_jump (); | |
934 | ||
935 | return (codestream_tell ()); | |
936 | } | |
937 | ||
938 | i386_push_dummy_frame () | |
939 | { | |
940 | CORE_ADDR sp = read_register (SP_REGNUM); | |
941 | int regnum; | |
942 | ||
943 | sp = push_word (sp, read_register (PC_REGNUM)); | |
944 | sp = push_word (sp, read_register (FP_REGNUM)); | |
945 | write_register (FP_REGNUM, sp); | |
946 | for (regnum = 0; regnum < NUM_REGS; regnum++) | |
947 | sp = push_word (sp, read_register (regnum)); | |
948 | write_register (SP_REGNUM, sp); | |
949 | } | |
950 | ||
951 | i386_pop_frame () | |
952 | { | |
953 | FRAME frame = get_current_frame (); | |
954 | CORE_ADDR fp; | |
955 | int regnum; | |
956 | struct frame_saved_regs fsr; | |
957 | struct frame_info *fi; | |
958 | ||
959 | fi = get_frame_info (frame); | |
960 | fp = fi->frame; | |
961 | get_frame_saved_regs (fi, &fsr); | |
962 | for (regnum = 0; regnum < NUM_REGS; regnum++) | |
963 | { | |
964 | CORE_ADDR adr; | |
965 | adr = fsr.regs[regnum]; | |
966 | if (adr) | |
967 | write_register (regnum, read_memory_integer (adr, 4)); | |
968 | } | |
969 | write_register (FP_REGNUM, read_memory_integer (fp, 4)); | |
970 | write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); | |
971 | write_register (SP_REGNUM, fp + 8); | |
972 | flush_cached_frames (); | |
973 | set_current_frame ( create_new_frame (read_register (FP_REGNUM), | |
974 | read_pc ())); | |
975 | } | |
976 | ||
977 | /* this table must line up with REGISTER_NAMES in m-i386.h */ | |
978 | /* symbols like 'EAX' come from <sys/reg.h> */ | |
979 | static int regmap[] = | |
980 | { | |
981 | EAX, ECX, EDX, EBX, | |
982 | UESP, EBP, ESI, EDI, | |
983 | EIP, EFL, CS, SS, | |
984 | DS, ES, FS, GS, | |
985 | }; | |
986 | ||
987 | /* blockend is the value of u.u_ar0, and points to the | |
988 | * place where GS is stored | |
989 | */ | |
990 | i386_register_u_addr (blockend, regnum) | |
991 | { | |
992 | #if 0 | |
993 | /* this will be needed if fp registers are reinstated */ | |
994 | /* for now, you can look at them with 'info float' | |
995 | * sys5 wont let you change them with ptrace anyway | |
996 | */ | |
997 | if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM) | |
998 | { | |
999 | int ubase, fpstate; | |
1000 | struct user u; | |
1001 | ubase = blockend + 4 * (SS + 1) - KSTKSZ; | |
1002 | fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u); | |
1003 | return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM)); | |
1004 | } | |
1005 | else | |
1006 | #endif | |
1007 | return (blockend + 4 * regmap[regnum]); | |
1008 | ||
1009 | } | |
1010 | ||
1011 | i387_to_double (from, to) | |
1012 | char *from; | |
1013 | char *to; | |
1014 | { | |
1015 | long *lp; | |
1016 | /* push extended mode on 387 stack, then pop in double mode | |
1017 | * | |
1018 | * first, set exception masks so no error is generated - | |
1019 | * number will be rounded to inf or 0, if necessary | |
1020 | */ | |
1021 | asm ("pushl %eax"); /* grab a stack slot */ | |
1022 | asm ("fstcw (%esp)"); /* get 387 control word */ | |
1023 | asm ("movl (%esp),%eax"); /* save old value */ | |
1024 | asm ("orl $0x3f,%eax"); /* mask all exceptions */ | |
1025 | asm ("pushl %eax"); | |
1026 | asm ("fldcw (%esp)"); /* load new value into 387 */ | |
1027 | ||
1028 | asm ("movl 8(%ebp),%eax"); | |
1029 | asm ("fldt (%eax)"); /* push extended number on 387 stack */ | |
1030 | asm ("fwait"); | |
1031 | asm ("movl 12(%ebp),%eax"); | |
1032 | asm ("fstpl (%eax)"); /* pop double */ | |
1033 | asm ("fwait"); | |
1034 | ||
1035 | asm ("popl %eax"); /* flush modified control word */ | |
1036 | asm ("fnclex"); /* clear exceptions */ | |
1037 | asm ("fldcw (%esp)"); /* restore original control word */ | |
1038 | asm ("popl %eax"); /* flush saved copy */ | |
1039 | } | |
1040 | ||
1041 | double_to_i387 (from, to) | |
1042 | char *from; | |
1043 | char *to; | |
1044 | { | |
1045 | /* push double mode on 387 stack, then pop in extended mode | |
1046 | * no errors are possible because every 64-bit pattern | |
1047 | * can be converted to an extended | |
1048 | */ | |
1049 | asm ("movl 8(%ebp),%eax"); | |
1050 | asm ("fldl (%eax)"); | |
1051 | asm ("fwait"); | |
1052 | asm ("movl 12(%ebp),%eax"); | |
1053 | asm ("fstpt (%eax)"); | |
1054 | asm ("fwait"); | |
1055 | } | |
1056 | ||
1057 | struct env387 | |
1058 | { | |
1059 | unsigned short control; | |
1060 | unsigned short r0; | |
1061 | unsigned short status; | |
1062 | unsigned short r1; | |
1063 | unsigned short tag; | |
1064 | unsigned short r2; | |
1065 | unsigned long eip; | |
1066 | unsigned short code_seg; | |
1067 | unsigned short opcode; | |
1068 | unsigned long operand; | |
1069 | unsigned short operand_seg; | |
1070 | unsigned short r3; | |
1071 | unsigned char regs[8][10]; | |
1072 | }; | |
1073 | ||
1074 | static | |
1075 | print_387_control_word (control) | |
1076 | unsigned short control; | |
1077 | { | |
1078 | printf ("control 0x%04x: ", control); | |
1079 | printf ("compute to "); | |
1080 | switch ((control >> 8) & 3) | |
1081 | { | |
1082 | case 0: printf ("24 bits; "); break; | |
1083 | case 1: printf ("(bad); "); break; | |
1084 | case 2: printf ("53 bits; "); break; | |
1085 | case 3: printf ("64 bits; "); break; | |
1086 | } | |
1087 | printf ("round "); | |
1088 | switch ((control >> 10) & 3) | |
1089 | { | |
1090 | case 0: printf ("NEAREST; "); break; | |
1091 | case 1: printf ("DOWN; "); break; | |
1092 | case 2: printf ("UP; "); break; | |
1093 | case 3: printf ("CHOP; "); break; | |
1094 | } | |
1095 | if (control & 0x3f) | |
1096 | { | |
1097 | printf ("mask:"); | |
1098 | if (control & 0x0001) printf (" INVALID"); | |
1099 | if (control & 0x0002) printf (" DENORM"); | |
1100 | if (control & 0x0004) printf (" DIVZ"); | |
1101 | if (control & 0x0008) printf (" OVERF"); | |
1102 | if (control & 0x0010) printf (" UNDERF"); | |
1103 | if (control & 0x0020) printf (" LOS"); | |
1104 | printf (";"); | |
1105 | } | |
1106 | printf ("\n"); | |
1107 | if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n", | |
1108 | control & 0xe080); | |
1109 | } | |
1110 | ||
1111 | static | |
1112 | print_387_status_word (status) | |
1113 | unsigned short status; | |
1114 | { | |
1115 | printf ("status 0x%04x: ", status); | |
1116 | if (status & 0xff) | |
1117 | { | |
1118 | printf ("exceptions:"); | |
1119 | if (status & 0x0001) printf (" INVALID"); | |
1120 | if (status & 0x0002) printf (" DENORM"); | |
1121 | if (status & 0x0004) printf (" DIVZ"); | |
1122 | if (status & 0x0008) printf (" OVERF"); | |
1123 | if (status & 0x0010) printf (" UNDERF"); | |
1124 | if (status & 0x0020) printf (" LOS"); | |
1125 | if (status & 0x0040) printf (" FPSTACK"); | |
1126 | printf ("; "); | |
1127 | } | |
1128 | printf ("flags: %d%d%d%d; ", | |
1129 | (status & 0x4000) != 0, | |
1130 | (status & 0x0400) != 0, | |
1131 | (status & 0x0200) != 0, | |
1132 | (status & 0x0100) != 0); | |
1133 | ||
1134 | printf ("top %d\n", (status >> 11) & 7); | |
1135 | } | |
1136 | ||
1137 | static | |
1138 | print_387_status (status, ep) | |
1139 | unsigned short status; | |
1140 | struct env387 *ep; | |
1141 | { | |
1142 | int i; | |
1143 | int bothstatus; | |
1144 | int top; | |
1145 | int fpreg; | |
1146 | unsigned char *p; | |
1147 | ||
1148 | bothstatus = ((status != 0) && (ep->status != 0)); | |
1149 | if (status != 0) | |
1150 | { | |
1151 | if (bothstatus) | |
1152 | printf ("u: "); | |
1153 | print_387_status_word (status); | |
1154 | } | |
1155 | ||
1156 | if (ep->status != 0) | |
1157 | { | |
1158 | if (bothstatus) | |
1159 | printf ("e: "); | |
1160 | print_387_status_word (ep->status); | |
1161 | } | |
1162 | ||
1163 | print_387_control_word (ep->control); | |
1164 | printf ("last exception: "); | |
1165 | printf ("opcode 0x%x; ", ep->opcode); | |
1166 | printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip); | |
1167 | printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand); | |
1168 | ||
1169 | top = (ep->status >> 11) & 7; | |
1170 | ||
1171 | printf ("regno tag msb lsb value\n"); | |
1172 | for (fpreg = 7; fpreg >= 0; fpreg--) | |
1173 | { | |
1174 | double val; | |
1175 | ||
1176 | printf ("%s %d: ", fpreg == top ? "=>" : " ", fpreg); | |
1177 | ||
1178 | switch ((ep->tag >> (fpreg * 2)) & 3) | |
1179 | { | |
1180 | case 0: printf ("valid "); break; | |
1181 | case 1: printf ("zero "); break; | |
1182 | case 2: printf ("trap "); break; | |
1183 | case 3: printf ("empty "); break; | |
1184 | } | |
1185 | for (i = 9; i >= 0; i--) | |
1186 | printf ("%02x", ep->regs[fpreg][i]); | |
1187 | ||
1188 | i387_to_double (ep->regs[fpreg], (char *)&val); | |
1189 | printf (" %g\n", val); | |
1190 | } | |
1191 | if (ep->r0) | |
1192 | printf ("warning: reserved0 is 0x%x\n", ep->r0); | |
1193 | if (ep->r1) | |
1194 | printf ("warning: reserved1 is 0x%x\n", ep->r1); | |
1195 | if (ep->r2) | |
1196 | printf ("warning: reserved2 is 0x%x\n", ep->r2); | |
1197 | if (ep->r3) | |
1198 | printf ("warning: reserved3 is 0x%x\n", ep->r3); | |
1199 | } | |
1200 | ||
1201 | #ifndef U_FPSTATE | |
1202 | #define U_FPSTATE(u) u.u_fpstate | |
1203 | #endif | |
1204 | ||
1205 | i386_float_info () | |
1206 | { | |
1207 | struct user u; /* just for address computations */ | |
1208 | int i; | |
1209 | /* fpstate defined in <sys/user.h> */ | |
1210 | struct fpstate *fpstatep; | |
1211 | char buf[sizeof (struct fpstate) + 2 * sizeof (int)]; | |
1212 | unsigned int uaddr; | |
1213 | char fpvalid; | |
1214 | unsigned int rounded_addr; | |
1215 | unsigned int rounded_size; | |
1216 | extern int corechan; | |
1217 | int skip; | |
1218 | ||
1219 | uaddr = (char *)&u.u_fpvalid - (char *)&u; | |
1220 | if (have_inferior_p()) | |
1221 | { | |
1222 | unsigned int data; | |
1223 | unsigned int mask; | |
1224 | ||
1225 | rounded_addr = uaddr & -sizeof (int); | |
1226 | data = ptrace (3, inferior_pid, rounded_addr, 0); | |
1227 | mask = 0xff << ((uaddr - rounded_addr) * 8); | |
1228 | ||
1229 | fpvalid = ((data & mask) != 0); | |
1230 | } | |
1231 | else | |
1232 | { | |
1233 | if (lseek (corechan, uaddr, 0) < 0) | |
1234 | perror ("seek on core file"); | |
1235 | if (myread (corechan, &fpvalid, 1) < 0) | |
1236 | perror ("read on core file"); | |
1237 | ||
1238 | } | |
1239 | ||
1240 | if (fpvalid == 0) | |
1241 | { | |
1242 | printf ("no floating point status saved\n"); | |
1243 | return; | |
1244 | } | |
1245 | ||
1246 | uaddr = (char *)&U_FPSTATE(u) - (char *)&u; | |
1247 | if (have_inferior_p ()) | |
1248 | { | |
1249 | int *ip; | |
1250 | ||
1251 | rounded_addr = uaddr & -sizeof (int); | |
1252 | rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) + | |
1253 | sizeof (int) - 1) / sizeof (int); | |
1254 | skip = uaddr - rounded_addr; | |
1255 | ||
1256 | ip = (int *)buf; | |
1257 | for (i = 0; i < rounded_size; i++) | |
1258 | { | |
1259 | *ip++ = ptrace (3, inferior_pid, rounded_addr, 0); | |
1260 | rounded_addr += sizeof (int); | |
1261 | } | |
1262 | } | |
1263 | else | |
1264 | { | |
1265 | if (lseek (corechan, uaddr, 0) < 0) | |
1266 | perror_with_name ("seek on core file"); | |
1267 | if (myread (corechan, buf, sizeof (struct fpstate)) < 0) | |
1268 | perror_with_name ("read from core file"); | |
1269 | skip = 0; | |
1270 | } | |
1271 | ||
1272 | fpstatep = (struct fpstate *)(buf + skip); | |
1273 | print_387_status (fpstatep->status, (struct env387 *)fpstatep->state); | |
1274 | } | |
1275 |