1998-08-18 Jason Molenda (jsm@bugshack.cygnus.com)
[external/binutils.git] / gdb / i386v-nat.c
1 /* Intel 386 native support for SYSV systems (pre-SVR4).
2    Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
6 This program 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 2 of the License, or
9 (at your option) any later version.
10
11 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "defs.h"
21
22 #ifdef HAVE_PTRACE_H
23 # include <ptrace.h>
24 #else
25 # ifdef HAVE_SYS_PTRACE_H
26 #  include <sys/ptrace.h>
27 # endif
28 #endif
29
30 #include "frame.h"
31 #include "inferior.h"
32 #include "language.h"
33 #include "gdbcore.h"
34
35 #ifdef USG
36 #include <sys/types.h>
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/dir.h>
41 #include <signal.h>
42 #include <sys/user.h>
43 #include <sys/ioctl.h>
44 #include <fcntl.h>
45
46 #ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
47 #include <sys/debugreg.h>
48 #endif
49
50 #include <sys/file.h>
51 #include "gdb_stat.h"
52
53 #ifdef HAVE_SYS_REG_H
54 #include <sys/reg.h>
55 #endif
56
57 #include "floatformat.h"
58
59 #include "target.h"
60
61 \f
62 /* this table must line up with REGISTER_NAMES in tm-i386v.h */
63 /* symbols like 'EAX' come from <sys/reg.h> */
64 static int regmap[] = 
65 {
66   EAX, ECX, EDX, EBX,
67   UESP, EBP, ESI, EDI,
68   EIP, EFL, CS, SS,
69   DS, ES, FS, GS,
70 };
71
72 /* blockend is the value of u.u_ar0, and points to the
73  * place where GS is stored
74  */
75
76 int
77 i386_register_u_addr (blockend, regnum)
78      int blockend;
79      int regnum;
80 {
81   struct user u;
82   int fpstate;
83   int ubase;
84
85   ubase = blockend;
86   /* FIXME:  Should have better way to test floating point range */
87   if (regnum >= FP0_REGNUM && regnum <= (FP0_REGNUM + 7)) 
88     {
89 #ifdef KSTKSZ   /* SCO, and others? */
90       ubase += 4 * (SS + 1) - KSTKSZ;
91       fpstate = ubase + ((char *)&u.u_fps.u_fpstate - (char *)&u);
92       return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
93 #else
94       fpstate = ubase + ((char *)&u.i387.st_space - (char *)&u);
95       return (fpstate + 10 * (regnum - FP0_REGNUM));
96 #endif
97     } 
98   else
99     {
100       return (ubase + 4 * regmap[regnum]);
101     }
102   
103 }
104 \f
105 int
106 kernel_u_size ()
107 {
108   return (sizeof (struct user));
109 }
110 \f
111 #ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
112
113 #if !defined (offsetof)
114 #define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
115 #endif
116
117 /* Record the value of the debug control register.  */
118 static int debug_control_mirror;
119
120 /* Record which address associates with which register.  */
121 static CORE_ADDR address_lookup[DR_LASTADDR - DR_FIRSTADDR + 1];
122
123 static int
124 i386_insert_aligned_watchpoint PARAMS ((int, CORE_ADDR, CORE_ADDR, int,
125                                            int));
126
127 static int
128 i386_insert_nonaligned_watchpoint PARAMS ((int, CORE_ADDR, CORE_ADDR, int,
129                                            int));
130
131 /* Insert a watchpoint.  */
132
133 int
134 i386_insert_watchpoint (pid, addr, len, rw)
135      int pid;
136      CORE_ADDR addr;
137      int len;
138      int rw;
139 {
140   return i386_insert_aligned_watchpoint (pid, addr, addr, len, rw);
141 }
142
143 static int
144 i386_insert_aligned_watchpoint (pid, waddr, addr, len, rw)
145      int pid;
146      CORE_ADDR waddr;
147      CORE_ADDR addr;
148      int len;
149      int rw;
150 {
151   int i;
152   int read_write_bits, len_bits;
153   int free_debug_register;
154   int register_number;
155   
156   /* Look for a free debug register.  */
157   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
158     {
159       if (address_lookup[i - DR_FIRSTADDR] == 0)
160         break;
161     }
162
163   /* No more debug registers!  */
164   if (i > DR_LASTADDR)
165     return -1;
166
167   read_write_bits = (rw & 1) ? DR_RW_READ : DR_RW_WRITE;
168
169   if (len == 1)
170     len_bits = DR_LEN_1;
171   else if (len == 2)
172     {
173       if (addr % 2)
174         return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
175       len_bits = DR_LEN_2;
176     }
177
178   else if (len == 4)
179     {
180       if (addr % 4)
181         return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
182       len_bits = DR_LEN_4;
183     }
184   else
185     return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
186   
187   free_debug_register = i;
188   register_number = free_debug_register - DR_FIRSTADDR;
189   debug_control_mirror |=
190     ((read_write_bits | len_bits)
191      << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number));
192   debug_control_mirror |=
193     (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
194   debug_control_mirror |= DR_LOCAL_SLOWDOWN;
195   debug_control_mirror &= ~DR_CONTROL_RESERVED;
196   
197   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
198           debug_control_mirror);
199   ptrace (6, pid, offsetof (struct user, u_debugreg[free_debug_register]),
200           addr);
201
202   /* Record where we came from.  */
203   address_lookup[register_number] = addr;
204   return 0;
205 }
206
207 static int
208 i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw)
209      int pid;
210      CORE_ADDR waddr;
211      CORE_ADDR addr;
212      int len;
213      int rw;
214 {
215   int align;
216   int size;
217   int rv;
218
219   static int size_try_array[16] = {
220     1, 1, 1, 1,                 /* trying size one */
221     2, 1, 2, 1,                 /* trying size two */
222     2, 1, 2, 1,                 /* trying size three */
223     4, 1, 2, 1                  /* trying size four */
224   };
225
226   rv = 0;
227   while (len > 0)
228     {
229       align = addr % 4;
230       /* Four is the maximum length for 386.  */
231       size = (len > 4) ? 3 : len - 1;
232       size = size_try_array[size * 4 + align];
233
234       rv = i386_insert_aligned_watchpoint (pid, waddr, addr, size, rw);
235       if (rv)
236         {
237           i386_remove_watchpoint (pid, waddr, size);
238           return rv;
239         }
240       addr += size;
241       len -= size;
242     }
243   return rv;
244 }
245
246 /* Remove a watchpoint.  */
247
248 int
249 i386_remove_watchpoint (pid, addr, len)
250      int pid;
251      CORE_ADDR addr;
252      int len;
253 {
254   int i;
255   int register_number;
256
257   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
258     {
259       register_number = i - DR_FIRSTADDR;
260       if (address_lookup[register_number] == addr)
261         {
262           debug_control_mirror &=
263             ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
264           address_lookup[register_number] = 0;
265         }
266     }
267   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
268           debug_control_mirror);
269   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
270
271   return 0;
272 }
273
274 /* Check if stopped by a watchpoint.  */
275
276 CORE_ADDR
277 i386_stopped_by_watchpoint (pid)
278     int pid;
279 {
280   int i;
281   int status;
282
283   status = ptrace (3, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
284   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
285
286   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
287     {
288       if (status & (1 << (i - DR_FIRSTADDR)))
289         return address_lookup[i - DR_FIRSTADDR];
290     }
291
292   return 0;
293 }
294
295 #endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */
296
297 #if 0
298 /* using FLOAT_INFO as is would be a problem.  FLOAT_INFO is called
299    via a command xxx and eventually calls ptrace without ever having
300    traversed the target vector.  This would be terribly impolite
301    behaviour for a sun4 hosted remote gdb.
302
303    A fix might be to move this code into the "info registers" command.
304    rich@cygnus.com 15 Sept 92. */
305 i386_float_info ()
306 {
307   struct user u; /* just for address computations */
308   int i;
309   /* fpstate defined in <sys/user.h> */
310   struct fpstate *fpstatep;
311   char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
312   unsigned int uaddr;
313   char fpvalid = 0;
314   unsigned int rounded_addr;
315   unsigned int rounded_size;
316   extern int corechan;
317   int skip;
318   
319   uaddr = (char *)&u.u_fpvalid - (char *)&u;
320   if (target_has_execution)
321     {
322       unsigned int data;
323       unsigned int mask;
324       
325       rounded_addr = uaddr & -sizeof (int);
326       data = ptrace (3, inferior_pid, (PTRACE_ARG3_TYPE) rounded_addr, 0);
327       mask = 0xff << ((uaddr - rounded_addr) * 8);
328       
329       fpvalid = ((data & mask) != 0);
330     } 
331 #if 0
332   else 
333     {
334       if (lseek (corechan, uaddr, 0) < 0)
335         perror ("seek on core file");
336       if (myread (corechan, &fpvalid, 1) < 0) 
337         perror ("read on core file");
338       
339     }
340 #endif  /* no core support yet */
341   
342   if (fpvalid == 0) 
343     {
344       printf_unfiltered ("no floating point status saved\n");
345       return;
346     }
347   
348   uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
349   if (target_has_execution)
350     {
351       int *ip;
352       
353       rounded_addr = uaddr & -sizeof (int);
354       rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
355                       sizeof (int) - 1) / sizeof (int);
356       skip = uaddr - rounded_addr;
357       
358       ip = (int *)buf;
359       for (i = 0; i < rounded_size; i++) 
360         {
361           *ip++ = ptrace (3, inferior_pid, (PTRACE_ARG3_TYPE) rounded_addr, 0);
362           rounded_addr += sizeof (int);
363         }
364     } 
365 #if 0
366   else 
367     {
368       if (lseek (corechan, uaddr, 0) < 0)
369         perror_with_name ("seek on core file");
370       if (myread (corechan, buf, sizeof (struct fpstate)) < 0) 
371         perror_with_name ("read from core file");
372       skip = 0;
373     }
374 #endif  /* 0 */ 
375
376   fpstatep = (struct fpstate *)(buf + skip);
377   print_387_status (fpstatep->status, (struct env387 *)fpstatep->state);
378 }
379
380 #endif /* never */