-/* Native-dependent code for Linux/x86.
- Copyright 1999, 2000 Free Software Foundation, Inc.
+/* Native-dependent code for GNU/Linux x86.
+
+ Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
This file is part of GDB.
#include "defs.h"
#include "inferior.h"
#include "gdbcore.h"
+#include "regcache.h"
+#include "gdb_assert.h"
+#include "gdb_string.h"
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/procfs.h>
#include <sys/reg.h>
#endif
+#ifndef ORIG_EAX
+#define ORIG_EAX -1
+#endif
+
+#ifdef HAVE_SYS_DEBUGREG_H
+#include <sys/debugreg.h>
+#endif
+
+#ifndef DR_FIRSTADDR
+#define DR_FIRSTADDR 0
+#endif
+
+#ifndef DR_LASTADDR
+#define DR_LASTADDR 3
+#endif
+
+#ifndef DR_STATUS
+#define DR_STATUS 6
+#endif
+
+#ifndef DR_CONTROL
+#define DR_CONTROL 7
+#endif
+
/* Prototypes for supply_gregset etc. */
#include "gregset.h"
/* Prototypes for i387_supply_fsave etc. */
-#include "i387-nat.h"
+#include "i387-tdep.h"
+
+/* Defines for XMM0_REGNUM etc. */
+#include "i386-tdep.h"
+
+/* Defines I386_LINUX_ORIG_EAX_REGNUM. */
+#include "i386-linux-tdep.h"
/* Prototypes for local functions. */
static void dummy_sse_values (void);
-/* On Linux, threads are implemented as pseudo-processes, in which
- case we may be tracing more than one process at a time. In that
- case, inferior_pid will contain the main process ID and the
- individual thread (process) ID mashed together. These macros are
- used to separate them out. These definitions should be overridden
- if thread support is included. */
-
-#if !defined (PIDGET) /* Default definition for PIDGET/TIDGET. */
-#define PIDGET(PID) PID
-#define TIDGET(PID) 0
-#endif
\f
-/* The register sets used in Linux ELF core-dumps are identical to the
- register sets in `struct user' that is used for a.out core-dumps,
- and is also used by `ptrace'. The corresponding types are
- `elf_gregset_t' for the general-purpose registers (with
+/* The register sets used in GNU/Linux ELF core-dumps are identical to
+ the register sets in `struct user' that is used for a.out
+ core-dumps, and is also used by `ptrace'. The corresponding types
+ are `elf_gregset_t' for the general-purpose registers (with
`elf_greg_t' the type of a single GP register) and `elf_fpregset_t'
for the floating-point registers.
EAX, ECX, EDX, EBX,
UESP, EBP, ESI, EDI,
EIP, EFL, CS, SS,
- DS, ES, FS, GS
+ DS, ES, FS, GS,
+ -1, -1, -1, -1, /* st0, st1, st2, st3 */
+ -1, -1, -1, -1, /* st4, st5, st6, st7 */
+ -1, -1, -1, -1, /* fctrl, fstat, ftag, fiseg */
+ -1, -1, -1, -1, /* fioff, foseg, fooff, fop */
+ -1, -1, -1, -1, /* xmm0, xmm1, xmm2, xmm3 */
+ -1, -1, -1, -1, /* xmm4, xmm5, xmm6, xmm6 */
+ -1, /* mxcsr */
+ ORIG_EAX
};
/* Which ptrace request retrieves which registers?
These apply to the corresponding SET requests as well. */
#define GETREGS_SUPPLIES(regno) \
- (0 <= (regno) && (regno) <= 15)
+ ((0 <= (regno) && (regno) <= 15) || (regno) == I386_LINUX_ORIG_EAX_REGNUM)
#define GETFPREGS_SUPPLIES(regno) \
(FP0_REGNUM <= (regno) && (regno) <= LAST_FPU_CTRL_REGNUM)
#define GETFPXREGS_SUPPLIES(regno) \
;
\f
-/* Fetching registers directly from the U area, one at a time. */
+/* Support for the user struct. */
-/* FIXME: kettenis/2000-03-05: This duplicates code from `inptrace.c'.
- The problem is that we define FETCH_INFERIOR_REGISTERS since we
- want to use our own versions of {fetch,store}_inferior_registers
- that use the GETREGS request. This means that the code in
- `infptrace.c' is #ifdef'd out. But we need to fall back on that
- code when GDB is running on top of a kernel that doesn't support
- the GETREGS request. I want to avoid changing `infptrace.c' right
- now. */
+/* Return the address of register REGNUM. BLOCKEND is the value of
+ u.u_ar0, which should point to the registers. */
-#ifndef PT_READ_U
-#define PT_READ_U PTRACE_PEEKUSR
-#endif
-#ifndef PT_WRITE_U
-#define PT_WRITE_U PTRACE_POKEUSR
-#endif
+CORE_ADDR
+register_u_addr (CORE_ADDR blockend, int regnum)
+{
+ return (blockend + 4 * regmap[regnum]);
+}
-/* Default the type of the ptrace transfer to int. */
-#ifndef PTRACE_XFER_TYPE
-#define PTRACE_XFER_TYPE int
-#endif
+/* Return the size of the user struct. */
-/* Registers we shouldn't try to fetch. */
-#define OLD_CANNOT_FETCH_REGISTER(regno) ((regno) >= NUM_GREGS)
+int
+kernel_u_size (void)
+{
+ return (sizeof (struct user));
+}
+\f
+
+/* Accessing registers through the U area, one at a time. */
/* Fetch one register. */
static void
fetch_register (int regno)
{
- /* This isn't really an address. But ptrace thinks of it as one. */
- CORE_ADDR regaddr;
- char mess[128]; /* For messages */
- register int i;
- unsigned int offset; /* Offset of registers within the u area. */
- char buf[MAX_REGISTER_RAW_SIZE];
int tid;
+ int val;
- if (OLD_CANNOT_FETCH_REGISTER (regno))
+ gdb_assert (!have_ptrace_getregs);
+ if (cannot_fetch_register (regno))
{
- memset (buf, '\0', REGISTER_RAW_SIZE (regno)); /* Supply zeroes */
- supply_register (regno, buf);
+ supply_register (regno, NULL);
return;
}
- /* Overload thread id onto process id */
- if ((tid = TIDGET (inferior_pid)) == 0)
- tid = inferior_pid; /* no thread id, just use process id */
-
- offset = U_REGS_OFFSET;
+ /* GNU/Linux LWP ID's are process ID's. */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
- regaddr = register_addr (regno, offset);
- for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- *(PTRACE_XFER_TYPE *) & buf[i] = ptrace (PT_READ_U, tid,
- (PTRACE_ARG3_TYPE) regaddr, 0);
- regaddr += sizeof (PTRACE_XFER_TYPE);
- if (errno != 0)
- {
- sprintf (mess, "reading register %s (#%d)",
- REGISTER_NAME (regno), regno);
- perror_with_name (mess);
- }
- }
- supply_register (regno, buf);
-}
-
-/* Fetch register values from the inferior.
- If REGNO is negative, do this for all registers.
- Otherwise, REGNO specifies which register (so we can save time). */
+ errno = 0;
+ val = ptrace (PTRACE_PEEKUSER, tid, register_addr (regno, 0), 0);
+ if (errno != 0)
+ error ("Couldn't read register %s (#%d): %s.", REGISTER_NAME (regno),
+ regno, safe_strerror (errno));
-void
-old_fetch_inferior_registers (int regno)
-{
- if (regno >= 0)
- {
- fetch_register (regno);
- }
- else
- {
- for (regno = 0; regno < ARCH_NUM_REGS; regno++)
- {
- fetch_register (regno);
- }
- }
+ supply_register (regno, &val);
}
-/* Registers we shouldn't try to store. */
-#define OLD_CANNOT_STORE_REGISTER(regno) ((regno) >= NUM_GREGS)
-
/* Store one register. */
static void
store_register (int regno)
{
- /* This isn't really an address. But ptrace thinks of it as one. */
- CORE_ADDR regaddr;
- char mess[128]; /* For messages */
- register int i;
- unsigned int offset; /* Offset of registers within the u area. */
int tid;
-
- if (OLD_CANNOT_STORE_REGISTER (regno))
- {
- return;
- }
-
- /* Overload thread id onto process id */
- if ((tid = TIDGET (inferior_pid)) == 0)
- tid = inferior_pid; /* no thread id, just use process id */
-
- offset = U_REGS_OFFSET;
-
- regaddr = register_addr (regno, offset);
- for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr,
- *(PTRACE_XFER_TYPE *) & registers[REGISTER_BYTE (regno) + i]);
- regaddr += sizeof (PTRACE_XFER_TYPE);
- if (errno != 0)
- {
- sprintf (mess, "writing register %s (#%d)",
- REGISTER_NAME (regno), regno);
- perror_with_name (mess);
- }
- }
-}
-
-/* Store our register values back into the inferior.
- If REGNO is negative, do this for all registers.
- Otherwise, REGNO specifies which register (so we can save time). */
-
-void
-old_store_inferior_registers (int regno)
-{
- if (regno >= 0)
- {
- store_register (regno);
- }
- else
- {
- for (regno = 0; regno < ARCH_NUM_REGS; regno++)
- {
- store_register (regno);
- }
- }
+ int val;
+
+ gdb_assert (!have_ptrace_getregs);
+ if (cannot_store_register (regno))
+ return;
+
+ /* GNU/Linux LWP ID's are process ID's. */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+
+ errno = 0;
+ regcache_collect (regno, &val);
+ ptrace (PTRACE_POKEUSER, tid, register_addr (regno, 0), val);
+ if (errno != 0)
+ error ("Couldn't read register %s (#%d): %s.", REGISTER_NAME (regno),
+ regno, safe_strerror (errno));
}
\f
/* Transfering the general-purpose registers between GDB, inferiors
and core files. */
-/* Fill GDB's register array with the genereal-purpose register values
+/* Fill GDB's register array with the general-purpose register values
in *GREGSETP. */
void
elf_greg_t *regp = (elf_greg_t *) gregsetp;
int i;
- for (i = 0; i < NUM_GREGS; i++)
+ for (i = 0; i < I386_NUM_GREGS; i++)
supply_register (i, (char *) (regp + regmap[i]));
+
+ if (I386_LINUX_ORIG_EAX_REGNUM < NUM_REGS)
+ supply_register (I386_LINUX_ORIG_EAX_REGNUM, (char *) (regp + ORIG_EAX));
}
/* Fill register REGNO (if it is a general-purpose register) in
elf_greg_t *regp = (elf_greg_t *) gregsetp;
int i;
- for (i = 0; i < NUM_GREGS; i++)
- if ((regno == -1 || regno == i))
- *(regp + regmap[i]) = *(elf_greg_t *) ®isters[REGISTER_BYTE (i)];
+ for (i = 0; i < I386_NUM_GREGS; i++)
+ if (regno == -1 || regno == i)
+ regcache_collect (i, regp + regmap[i]);
+
+ if ((regno == -1 || regno == I386_LINUX_ORIG_EAX_REGNUM)
+ && I386_LINUX_ORIG_EAX_REGNUM < NUM_REGS)
+ regcache_collect (I386_LINUX_ORIG_EAX_REGNUM, regp + ORIG_EAX);
}
#ifdef HAVE_PTRACE_GETREGS
/* Fill GDB's register array with the floating-point and SSE register
values in *FPXREGSETP. */
-static void
+void
supply_fpxregset (elf_fpxregset_t *fpxregsetp)
{
i387_supply_fxsave ((char *) fpxregsetp);
*FPXREGSETP with the value in GDB's register array. If REGNO is
-1, do this for all registers. */
-static void
+void
fill_fpxregset (elf_fpxregset_t *fpxregsetp, int regno)
{
i387_fill_fxsave ((char *) fpxregsetp, regno);
static void
dummy_sse_values (void)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* C doesn't have a syntax for NaN's, so write it out as an array of
longs. */
static long dummy[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
static long mxcsr = 0x1f80;
int reg;
- for (reg = 0; reg < 8; reg++)
+ for (reg = 0; reg < tdep->num_xmm_regs; reg++)
supply_register (XMM0_REGNUM + reg, (char *) dummy);
- supply_register (MXCSR_REGNUM, (char *) &mxcsr);
+ if (tdep->num_xmm_regs > 0)
+ supply_register (MXCSR_REGNUM, (char *) &mxcsr);
}
#else
int
cannot_fetch_register (int regno)
{
- if (! have_ptrace_getregs)
- return OLD_CANNOT_FETCH_REGISTER (regno);
- return 0;
+ gdb_assert (regno >= 0 && regno < NUM_REGS);
+ return (!have_ptrace_getregs && regmap[regno] == -1);
}
+
int
cannot_store_register (int regno)
{
- if (! have_ptrace_getregs)
- return OLD_CANNOT_STORE_REGISTER (regno);
- return 0;
+ gdb_assert (regno >= 0 && regno < NUM_REGS);
+ return (!have_ptrace_getregs && regmap[regno] == -1);
}
/* Fetch register REGNO from the child process. If REGNO is -1, do
/* Use the old method of peeking around in `struct user' if the
GETREGS request isn't available. */
- if (! have_ptrace_getregs)
+ if (!have_ptrace_getregs)
{
- old_fetch_inferior_registers (regno);
+ int i;
+
+ for (i = 0; i < NUM_REGS; i++)
+ if (regno == -1 || regno == i)
+ fetch_register (i);
+
return;
}
- /* Linux LWP ID's are process ID's. */
- if ((tid = TIDGET (inferior_pid)) == 0)
- tid = inferior_pid; /* Not a threaded program. */
+ /* GNU/Linux LWP ID's are process ID's. */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
/* Use the PTRACE_GETFPXREGS request whenever possible, since it
transfers more registers in one system call, and we'll cache the
fetch_regs (tid);
/* The call above might reset `have_ptrace_getregs'. */
- if (! have_ptrace_getregs)
+ if (!have_ptrace_getregs)
{
- old_fetch_inferior_registers (-1);
+ fetch_inferior_registers (regno);
return;
}
return;
}
- internal_error ("Got request for bad register number %d.", regno);
+ internal_error (__FILE__, __LINE__,
+ "Got request for bad register number %d.", regno);
}
/* Store register REGNO back into the child process. If REGNO is -1,
/* Use the old method of poking around in `struct user' if the
SETREGS request isn't available. */
- if (! have_ptrace_getregs)
+ if (!have_ptrace_getregs)
{
- old_store_inferior_registers (regno);
+ int i;
+
+ for (i = 0; i < NUM_REGS; i++)
+ if (regno == -1 || regno == i)
+ store_register (i);
+
return;
}
- /* Linux LWP ID's are process ID's. */
- if ((tid = TIDGET (inferior_pid)) == 0)
- tid = inferior_pid; /* Not a threaded program. */
+ /* GNU/Linux LWP ID's are process ID's. */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
/* Use the PTRACE_SETFPXREGS requests whenever possible, since it
transfers more registers in one system call. But remember that
return;
}
- internal_error ("Got request to store bad register number %d.", regno);
+ internal_error (__FILE__, __LINE__,
+ "Got request to store bad register number %d.", regno);
+}
+\f
+
+static unsigned long
+i386_linux_dr_get (int regnum)
+{
+ int tid;
+ unsigned long value;
+
+ /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
+ multi-threaded processes here. For now, pretend there is just
+ one thread. */
+ tid = PIDGET (inferior_ptid);
+
+ /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the
+ ptrace call fails breaks debugging remote targets. The correct
+ way to fix this is to add the hardware breakpoint and watchpoint
+ stuff to the target vectore. For now, just return zero if the
+ ptrace call fails. */
+ errno = 0;
+ value = ptrace (PTRACE_PEEKUSER, tid,
+ offsetof (struct user, u_debugreg[regnum]), 0);
+ if (errno != 0)
+#if 0
+ perror_with_name ("Couldn't read debug register");
+#else
+ return 0;
+#endif
+
+ return value;
+}
+
+static void
+i386_linux_dr_set (int regnum, unsigned long value)
+{
+ int tid;
+
+ /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
+ multi-threaded processes here. For now, pretend there is just
+ one thread. */
+ tid = PIDGET (inferior_ptid);
+
+ errno = 0;
+ ptrace (PTRACE_POKEUSER, tid,
+ offsetof (struct user, u_debugreg[regnum]), value);
+ if (errno != 0)
+ perror_with_name ("Couldn't write debug register");
+}
+
+void
+i386_linux_dr_set_control (unsigned long control)
+{
+ i386_linux_dr_set (DR_CONTROL, control);
+}
+
+void
+i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+{
+ gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+ i386_linux_dr_set (DR_FIRSTADDR + regnum, addr);
+}
+
+void
+i386_linux_dr_reset_addr (int regnum)
+{
+ gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+ i386_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
+}
+
+unsigned long
+i386_linux_dr_get_status (void)
+{
+ return i386_linux_dr_get (DR_STATUS);
}
\f
/* Provide registers to GDB from a core file.
(We can't use the generic version of this function in
- core-regset.c, because Linux has *three* different kinds of
+ core-regset.c, because GNU/Linux has *three* different kinds of
register set notes. core-regset.c would have to call
supply_fpxregset, which most platforms don't have.)
2 --- the floating-point register set, in elf_fpregset_t format
3 --- the extended floating-point register set, in elf_fpxregset_t format
- REG_ADDR isn't used on Linux. */
+ REG_ADDR isn't used on GNU/Linux. */
static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
}
\f
-/* The instruction for a Linux system call is:
+/* The instruction for a GNU/Linux system call is:
int $0x80
or 0xcd 0x80. */
If SIGNAL is nonzero, give it that signal. */
void
-child_resume (int pid, int step, enum target_signal signal)
+child_resume (ptid_t ptid, int step, enum target_signal signal)
{
+ int pid = PIDGET (ptid);
+
int request = PTRACE_CONT;
if (pid == -1)
/* Resume all threads. */
/* I think this only gets used in the non-threaded case, where "resume
- all threads" and "resume inferior_pid" are the same. */
- pid = inferior_pid;
+ all threads" and "resume inferior_ptid" are the same. */
+ pid = PIDGET (inferior_ptid);
if (step)
{
- CORE_ADDR pc = read_pc_pid (pid);
+ CORE_ADDR pc = read_pc_pid (pid_to_ptid (pid));
unsigned char buf[LINUX_SYSCALL_LEN];
request = PTRACE_SINGLESTEP;
if (read_memory_nobpt (pc, (char *) buf, LINUX_SYSCALL_LEN) == 0
&& memcmp (buf, linux_syscall, LINUX_SYSCALL_LEN) == 0)
{
- int syscall = read_register_pid (LINUX_SYSCALL_REGNUM, pid);
+ int syscall = read_register_pid (LINUX_SYSCALL_REGNUM,
+ pid_to_ptid (pid));
/* Then check the system call number. */
if (syscall == SYS_sigreturn || syscall == SYS_rt_sigreturn)
CORE_ADDR sp = read_register (SP_REGNUM);
CORE_ADDR addr = sp;
unsigned long int eflags;
-
+
if (syscall == SYS_rt_sigreturn)
addr = read_memory_integer (sp + 8, 4) + 20;
}
\f
-/* Register that we are able to handle Linux ELF core file formats. */
+/* Register that we are able to handle GNU/Linux ELF core file
+ formats. */
static struct core_fns linux_elf_core_fns =
{