-/* Native-dependent code for Linux/x86.
- Copyright 1999, 2000, 2001 Free Software Foundation, Inc.
+/* Native-dependent code for GNU/Linux i386.
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GDB.
#include "inferior.h"
#include "gdbcore.h"
#include "regcache.h"
+#include "linux-nat.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
/* Prototypes for supply_gregset etc. */
#include "gregset.h"
-/* Prototypes for i387_supply_fsave etc. */
-#include "i387-nat.h"
-
-/* Prototypes for local functions. */
-static void dummy_sse_values (void);
+#include "i387-tdep.h"
+#include "i386-tdep.h"
+#include "i386-linux-tdep.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
+/* Defines ps_err_e, struct ps_prochandle. */
+#include "gdb_proc_service.h"
\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)
-#define GETFPREGS_SUPPLIES(regno) \
- (FP0_REGNUM <= (regno) && (regno) <= LAST_FPU_CTRL_REGNUM)
+ ((0 <= (regno) && (regno) <= 15) || (regno) == I386_LINUX_ORIG_EAX_REGNUM)
+
#define GETFPXREGS_SUPPLIES(regno) \
- (FP0_REGNUM <= (regno) && (regno) <= MXCSR_REGNUM)
+ (I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS)
/* Does the current host support the GETREGS request? */
int have_ptrace_getregs =
}
\f
-/* Fetching registers directly from the U area, one at a time. */
-
-/* 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. */
-
-#ifndef PT_READ_U
-#define PT_READ_U PTRACE_PEEKUSR
-#endif
-#ifndef PT_WRITE_U
-#define PT_WRITE_U PTRACE_POKEUSR
-#endif
-
-/* Default the type of the ptrace transfer to int. */
-#ifndef PTRACE_XFER_TYPE
-#define PTRACE_XFER_TYPE int
-#endif
-
-/* Registers we shouldn't try to fetch. */
-#define OLD_CANNOT_FETCH_REGISTER(regno) ((regno) >= NUM_GREGS)
+/* 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);
+ regcache_raw_supply (current_regcache, 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 */
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
- offset = U_REGS_OFFSET;
+ 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));
- 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);
+ regcache_raw_supply (current_regcache, regno, &val);
}
-/* 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). */
-
-void
-old_fetch_inferior_registers (int regno)
-{
- if (regno >= 0)
- {
- fetch_register (regno);
- }
- else
- {
- for (regno = 0; regno < NUM_REGS; regno++)
- {
- fetch_register (regno);
- }
- }
-}
-
-/* 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;
+ int val;
- 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 */
+ gdb_assert (!have_ptrace_getregs);
+ if (cannot_store_register (regno))
+ return;
- offset = U_REGS_OFFSET;
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 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 (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 < NUM_REGS; regno++)
- {
- store_register (regno);
- }
- }
+ errno = 0;
+ regcache_raw_collect (current_regcache, regno, &val);
+ ptrace (PTRACE_POKEUSER, tid, register_addr (regno, 0), val);
+ if (errno != 0)
+ error ("Couldn't write register %s (#%d): %s.", REGISTER_NAME (regno),
+ regno, safe_strerror (errno));
}
\f
elf_greg_t *regp = (elf_greg_t *) gregsetp;
int i;
- for (i = 0; i < NUM_GREGS; i++)
- supply_register (i, (char *) (regp + regmap[i]));
+ for (i = 0; i < I386_NUM_GREGS; i++)
+ regcache_raw_supply (current_regcache, i, regp + regmap[i]);
+
+ if (I386_LINUX_ORIG_EAX_REGNUM < NUM_REGS)
+ regcache_raw_supply (current_regcache, I386_LINUX_ORIG_EAX_REGNUM,
+ 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_raw_collect (current_regcache, i, regp + regmap[i]);
+
+ if ((regno == -1 || regno == I386_LINUX_ORIG_EAX_REGNUM)
+ && I386_LINUX_ORIG_EAX_REGNUM < NUM_REGS)
+ regcache_raw_collect (current_regcache, I386_LINUX_ORIG_EAX_REGNUM,
+ regp + ORIG_EAX);
}
#ifdef HAVE_PTRACE_GETREGS
void
supply_fpregset (elf_fpregset_t *fpregsetp)
{
- i387_supply_fsave ((char *) fpregsetp);
- dummy_sse_values ();
+ i387_supply_fsave (current_regcache, -1, fpregsetp);
}
/* 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);
+ i387_supply_fxsave (current_regcache, -1, fpxregsetp);
}
/* Fill register REGNO (if it is a floating-point or SSE register) in
*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 1;
}
-/* Fill the XMM registers in the register array with dummy values. For
- cases where we don't have access to the XMM registers. I think
- this is cleaner than printing a warning. For a cleaner solution,
- we should gdbarchify the i386 family. */
-
-static void
-dummy_sse_values (void)
-{
- /* 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++)
- supply_register (XMM0_REGNUM + reg, (char *) dummy);
- supply_register (MXCSR_REGNUM, (char *) &mxcsr);
-}
-
#else
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 */
\f
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. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 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;
}
/* 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. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 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
}
\f
-static long
+/* Support for debug registers. */
+
+static unsigned long
i386_linux_dr_get (int regnum)
{
int tid;
- long value;
+ 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_pid);
+ 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
+ stuff to the target vector. For now, just return zero if the
ptrace call fails. */
errno = 0;
- value = ptrace (PT_READ_U, tid,
+ value = ptrace (PTRACE_PEEKUSER, tid,
offsetof (struct user, u_debugreg[regnum]), 0);
if (errno != 0)
#if 0
}
static void
-i386_linux_dr_set (int regnum, long value)
+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_pid);
+ tid = PIDGET (inferior_ptid);
errno = 0;
- ptrace (PT_WRITE_U, tid,
+ 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 (long control)
+i386_linux_dr_set_control (unsigned long control)
{
i386_linux_dr_set (DR_CONTROL, control);
}
i386_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
}
-long
+unsigned long
i386_linux_dr_get_status (void)
{
return i386_linux_dr_get (DR_STATUS);
}
\f
-/* Interpreting register set info found in core files. */
-
-/* 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
- register set notes. core-regset.c would have to call
- supply_fpxregset, which most platforms don't have.)
-
- CORE_REG_SECT points to an array of bytes, which are the contents
- of a `note' from a core file which BFD thinks might contain
- register contents. CORE_REG_SIZE is its size.
-
- WHICH says which register set corelow suspects this is:
- 0 --- the general-purpose register set, in elf_gregset_t format
- 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. */
-
-static void
-fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
- int which, CORE_ADDR reg_addr)
-{
- elf_gregset_t gregset;
- elf_fpregset_t fpregset;
-
- switch (which)
- {
- case 0:
- if (core_reg_size != sizeof (gregset))
- warning ("Wrong size gregset in core file.");
- else
- {
- memcpy (&gregset, core_reg_sect, sizeof (gregset));
- supply_gregset (&gregset);
- }
- break;
-
- case 2:
- if (core_reg_size != sizeof (fpregset))
- warning ("Wrong size fpregset in core file.");
- else
- {
- memcpy (&fpregset, core_reg_sect, sizeof (fpregset));
- supply_fpregset (&fpregset);
- }
- break;
-
-#ifdef HAVE_PTRACE_GETFPXREGS
- {
- elf_fpxregset_t fpxregset;
-
- case 3:
- if (core_reg_size != sizeof (fpxregset))
- warning ("Wrong size fpxregset in core file.");
- else
- {
- memcpy (&fpxregset, core_reg_sect, sizeof (fpxregset));
- supply_fpxregset (&fpxregset);
- }
- break;
- }
+/* Called by libthread_db. Returns a pointer to the thread local
+ storage (or its descriptor). */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+ /* NOTE: cagney/2003-08-26: The definition of this buffer is found
+ in the kernel header <asm-i386/ldt.h>. It, after padding, is 4 x
+ 4 byte integers in size: `entry_number', `base_addr', `limit',
+ and a bunch of status bits.
+
+ The values returned by this ptrace call should be part of the
+ regcache buffer, and ps_get_thread_area should channel its
+ request through the regcache. That way remote targets could
+ provide the value using the remote protocol and not this direct
+ call.
+
+ Is this function needed? I'm guessing that the `base' is the
+ address of a a descriptor that libthread_db uses to find the
+ thread local address base that GDB needs. Perhaps that
+ descriptor is defined by the ABI. Anyway, given that
+ libthread_db calls this function without prompting (gdb
+ requesting tls base) I guess it needs info in there anyway. */
+ unsigned int desc[4];
+ gdb_assert (sizeof (int) == 4);
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
#endif
- default:
- /* We've covered all the kinds of registers we know about here,
- so this must be something we wouldn't know what to do with
- anyway. Just ignore it. */
- break;
- }
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
+ (void *) idx, (unsigned long) &desc) < 0)
+ return PS_ERR;
+
+ *(int *)base = desc[1];
+ return PS_OK;
}
\f
-/* The instruction for a Linux system call is:
+/* The instruction for a GNU/Linux system call is:
int $0x80
or 0xcd 0x80. */
#define LINUX_SYSCALL_LEN (sizeof linux_syscall)
/* The system call number is stored in the %eax register. */
-#define LINUX_SYSCALL_REGNUM 0 /* %eax */
+#define LINUX_SYSCALL_REGNUM I386_EAX_REGNUM
/* We are specifically interested in the sigreturn and rt_sigreturn
system calls. */
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;
that's about to be restored, and set the trace flag there. */
/* First check if PC is at a system call. */
- if (read_memory_nobpt (pc, (char *) buf, LINUX_SYSCALL_LEN) == 0
+ if (deprecated_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 sp = read_register (I386_ESP_REGNUM);
CORE_ADDR addr = sp;
unsigned long int eflags;
-
+
if (syscall == SYS_rt_sigreturn)
addr = read_memory_integer (sp + 8, 4) + 20;
if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1)
perror_with_name ("ptrace");
}
-\f
-
-/* Register that we are able to handle Linux ELF core file formats. */
-
-static struct core_fns linux_elf_core_fns =
-{
- bfd_target_elf_flavour, /* core_flavour */
- default_check_format, /* check_format */
- default_core_sniffer, /* core_sniffer */
- fetch_core_registers, /* core_read_registers */
- NULL /* next */
-};
void
-_initialize_i386_linux_nat (void)
+child_post_startup_inferior (ptid_t ptid)
{
- add_core_fns (&linux_elf_core_fns);
+ i386_cleanup_dregs ();
+ linux_child_post_startup_inferior (ptid);
}