-/* 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"
-/* For i386_linux_skip_solib_resolver. */
-#include "symtab.h"
-#include "symfile.h"
-#include "objfiles.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
+#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"
-
-/* 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
+#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);
+
\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.
/* 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
+/* Support for the user struct. */
+
+/* Return the address of register REGNUM. BLOCKEND is the value of
+ u.u_ar0, which should point to the registers. */
+
+CORE_ADDR
+register_u_addr (CORE_ADDR blockend, int regnum)
+{
+ return (blockend + 4 * regmap[regnum]);
+}
+
+/* Return the size of the user struct. */
+
+int
+kernel_u_size (void)
+{
+ return (sizeof (struct user));
+}
+\f
+
/* Fetching registers directly from the U area, one at a time. */
/* FIXME: kettenis/2000-03-05: This duplicates code from `inptrace.c'.
#endif
/* Registers we shouldn't try to fetch. */
-#if !defined (CANNOT_FETCH_REGISTER)
-#define CANNOT_FETCH_REGISTER(regno) 0
-#endif
+#define OLD_CANNOT_FETCH_REGISTER(regno) ((regno) >= I386_NUM_GREGS)
/* Fetch one register. */
char buf[MAX_REGISTER_RAW_SIZE];
int tid;
- if (CANNOT_FETCH_REGISTER (regno))
+ if (OLD_CANNOT_FETCH_REGISTER (regno))
{
memset (buf, '\0', REGISTER_RAW_SIZE (regno)); /* Supply zeroes */
supply_register (regno, buf);
}
/* Overload thread id onto process id */
- if ((tid = TIDGET (inferior_pid)) == 0)
- tid = inferior_pid; /* no thread id, just use process id */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* no thread id, just use process id */
offset = U_REGS_OFFSET;
}
else
{
- for (regno = 0; regno < ARCH_NUM_REGS; regno++)
+ for (regno = 0; regno < NUM_REGS; regno++)
{
fetch_register (regno);
}
}
/* Registers we shouldn't try to store. */
-#if !defined (CANNOT_STORE_REGISTER)
-#define CANNOT_STORE_REGISTER(regno) 0
-#endif
+#define OLD_CANNOT_STORE_REGISTER(regno) ((regno) >= I386_NUM_GREGS)
/* Store one register. */
unsigned int offset; /* Offset of registers within the u area. */
int tid;
- if (CANNOT_STORE_REGISTER (regno))
+ 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 */
+ if ((tid = TIDGET (inferior_ptid)) == 0)
+ tid = PIDGET (inferior_ptid); /* no thread id, just use process id */
offset = U_REGS_OFFSET;
}
else
{
- for (regno = 0; regno < ARCH_NUM_REGS; regno++)
+ for (regno = 0; regno < NUM_REGS; regno++)
{
store_register (regno);
}
/* 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
supply_fpregset (elf_fpregset_t *fpregsetp)
{
i387_supply_fsave ((char *) fpregsetp);
+ dummy_sse_values ();
}
/* Fill register REGNO (if it is a floating-point register) in
/* 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);
return 0;
if (ptrace (PTRACE_GETFPXREGS, tid, 0, &fpxregs) == -1)
- perror_with_name ("Couldn't read floating-point and SSE registers");
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_getfpxregs = 0;
+ return 0;
+ }
+
+ perror_with_name ("Couldn't read floating-point and SSE registers");
+ }
fill_fpxregset (&fpxregs, 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
-static int fetch_fpxregs (int tid) {}
-static int store_fpxregs (int tid, int regno) {}
+static int fetch_fpxregs (int tid) { return 0; }
+static int store_fpxregs (int tid, int regno) { return 0; }
static void dummy_sse_values (void) {}
#endif /* HAVE_PTRACE_GETFPXREGS */
/* Transferring arbitrary registers between GDB and inferior. */
+/* Check if register REGNO in the child process is accessible.
+ If we are accessing registers directly via the U area, only the
+ general-purpose registers are available.
+ All registers should be accessible if we have GETREGS support. */
+
+int
+cannot_fetch_register (int regno)
+{
+ if (! have_ptrace_getregs)
+ return OLD_CANNOT_FETCH_REGISTER (regno);
+ return 0;
+}
+int
+cannot_store_register (int regno)
+{
+ if (! have_ptrace_getregs)
+ return OLD_CANNOT_STORE_REGISTER (regno);
+ return 0;
+}
+
/* Fetch register REGNO from the child process. If REGNO is -1, do
this for all registers (including the floating point and SSE
registers). */
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
gdbarch. Until then, this will at least make things work
plausibly. */
fetch_fpregs (tid);
- dummy_sse_values ();
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,
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 (PT_READ_U, 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 (PT_WRITE_U, 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
-/* Calling functions in shared libraries. */
-/* FIXME: kettenis/2000-03-05: Doesn't this belong in a
- target-dependent file? The function
- `i386_linux_skip_solib_resolver' is mentioned in
- `config/i386/tm-linux.h'. */
-
-/* Find the minimal symbol named NAME, and return both the minsym
- struct and its objfile. This probably ought to be in minsym.c, but
- everything there is trying to deal with things like C++ and
- SOFUN_ADDRESS_MAYBE_TURQUOISE, ... Since this is so simple, it may
- be considered too special-purpose for general consumption. */
-
-static struct minimal_symbol *
-find_minsym_and_objfile (char *name, struct objfile **objfile_p)
-{
- struct objfile *objfile;
-
- ALL_OBJFILES (objfile)
- {
- struct minimal_symbol *msym;
-
- ALL_OBJFILE_MSYMBOLS (objfile, msym)
- {
- if (SYMBOL_NAME (msym)
- && STREQ (SYMBOL_NAME (msym), name))
- {
- *objfile_p = objfile;
- return msym;
- }
- }
- }
-
- return 0;
-}
-
-static CORE_ADDR
-skip_hurd_resolver (CORE_ADDR pc)
-{
- /* The HURD dynamic linker is part of the GNU C library, so many
- GNU/Linux distributions use it. (All ELF versions, as far as I
- know.) An unresolved PLT entry points to "_dl_runtime_resolve",
- which calls "fixup" to patch the PLT, and then passes control to
- the function.
-
- We look for the symbol `_dl_runtime_resolve', and find `fixup' in
- the same objfile. If we are at the entry point of `fixup', then
- we set a breakpoint at the return address (at the top of the
- stack), and continue.
-
- It's kind of gross to do all these checks every time we're
- called, since they don't change once the executable has gotten
- started. But this is only a temporary hack --- upcoming versions
- of Linux will provide a portable, efficient interface for
- debugging programs that use shared libraries. */
-
- struct objfile *objfile;
- struct minimal_symbol *resolver
- = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile);
-
- if (resolver)
- {
- struct minimal_symbol *fixup
- = lookup_minimal_symbol ("fixup", 0, objfile);
-
- if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc)
- return (SAVED_PC_AFTER_CALL (get_current_frame ()));
- }
-
- return 0;
-}
-
-/* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
- This function:
- 1) decides whether a PLT has sent us into the linker to resolve
- a function reference, and
- 2) if so, tells us where to set a temporary breakpoint that will
- trigger when the dynamic linker is done. */
-
-CORE_ADDR
-i386_linux_skip_solib_resolver (CORE_ADDR pc)
-{
- CORE_ADDR result;
-
- /* Plug in functions for other kinds of resolvers here. */
- result = skip_hurd_resolver (pc);
- if (result)
- return result;
-
- return 0;
-}
-\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 =
{