/* GNU/Linux/x86-64 specific low level interface, for the remote server
for GDB.
- Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
+ Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include <assert.h>
+#include <stddef.h>
#include <signal.h>
#include "server.h"
#include "linux-low.h"
#include "i387-fp.h"
+#include "i386-low.h"
#include "gdb_proc_service.h"
-/* NOTE: gdb_proc_service.h may include linux/elf.h.
- We need Elf32_Phdr. If we don't get linux/elf.h we could include
- elf.h like linux-ppc-low.c does. */
-
-/* Defined in auto-generated file reg-i386-linux.c. */
+/* Defined in auto-generated file i386-linux.c. */
void init_registers_i386_linux (void);
-/* Defined in auto-generated file reg-x86-64-linux.c. */
-void init_registers_x86_64_linux (void);
+/* Defined in auto-generated file amd64-linux.c. */
+void init_registers_amd64_linux (void);
#include <sys/reg.h>
#include <sys/procfs.h>
#define ARCH_GET_GS 0x1004
#endif
+/* Per-process arch-specific data we want to keep. */
+
+struct arch_process_info
+{
+ struct i386_debug_reg_state debug_reg_state;
+};
+
+/* Per-thread arch-specific data we want to keep. */
+
+struct arch_lwp_info
+{
+ /* Non-zero if our copy differs from what's recorded in the thread. */
+ int debug_registers_changed;
+};
+
#ifdef __x86_64__
/* Mapping between the general-purpose registers in `struct user'
}
static void
-x86_fill_gregset (void *buf)
+x86_fill_gregset (struct regcache *regcache, void *buf)
{
int i;
{
for (i = 0; i < X86_64_NUM_REGS; i++)
if (x86_64_regmap[i] != -1)
- collect_register (i, ((char *) buf) + x86_64_regmap[i]);
+ collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
return;
}
#endif
for (i = 0; i < I386_NUM_REGS; i++)
- collect_register (i, ((char *) buf) + i386_regmap[i]);
+ collect_register (regcache, i, ((char *) buf) + i386_regmap[i]);
- collect_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+ collect_register_by_name (regcache, "orig_eax",
+ ((char *) buf) + ORIG_EAX * 4);
}
static void
-x86_store_gregset (const void *buf)
+x86_store_gregset (struct regcache *regcache, const void *buf)
{
int i;
{
for (i = 0; i < X86_64_NUM_REGS; i++)
if (x86_64_regmap[i] != -1)
- supply_register (i, ((char *) buf) + x86_64_regmap[i]);
+ supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
return;
}
#endif
for (i = 0; i < I386_NUM_REGS; i++)
- supply_register (i, ((char *) buf) + i386_regmap[i]);
+ supply_register (regcache, i, ((char *) buf) + i386_regmap[i]);
- supply_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+ supply_register_by_name (regcache, "orig_eax",
+ ((char *) buf) + ORIG_EAX * 4);
}
static void
-x86_fill_fpregset (void *buf)
+x86_fill_fpregset (struct regcache *regcache, void *buf)
{
#ifdef __x86_64__
- i387_cache_to_fxsave (buf);
+ i387_cache_to_fxsave (regcache, buf);
#else
- i387_cache_to_fsave (buf);
+ i387_cache_to_fsave (regcache, buf);
#endif
}
static void
-x86_store_fpregset (const void *buf)
+x86_store_fpregset (struct regcache *regcache, const void *buf)
{
#ifdef __x86_64__
- i387_fxsave_to_cache (buf);
+ i387_fxsave_to_cache (regcache, buf);
#else
- i387_fsave_to_cache (buf);
+ i387_fsave_to_cache (regcache, buf);
#endif
}
#ifndef __x86_64__
static void
-x86_fill_fpxregset (void *buf)
+x86_fill_fpxregset (struct regcache *regcache, void *buf)
{
- i387_cache_to_fxsave (buf);
+ i387_cache_to_fxsave (regcache, buf);
}
static void
-x86_store_fpxregset (const void *buf)
+x86_store_fpxregset (struct regcache *regcache, const void *buf)
{
- i387_fxsave_to_cache (buf);
+ i387_fxsave_to_cache (regcache, buf);
}
#endif
};
static CORE_ADDR
-x86_get_pc (void)
+x86_get_pc (struct regcache *regcache)
{
int use_64bit = register_size (0) == 8;
if (use_64bit)
{
unsigned long pc;
- collect_register_by_name ("rip", &pc);
+ collect_register_by_name (regcache, "rip", &pc);
return (CORE_ADDR) pc;
}
else
{
unsigned int pc;
- collect_register_by_name ("eip", &pc);
+ collect_register_by_name (regcache, "eip", &pc);
return (CORE_ADDR) pc;
}
}
static void
-x86_set_pc (CORE_ADDR pc)
+x86_set_pc (struct regcache *regcache, CORE_ADDR pc)
{
int use_64bit = register_size (0) == 8;
if (use_64bit)
{
unsigned long newpc = pc;
- supply_register_by_name ("rip", &newpc);
+ supply_register_by_name (regcache, "rip", &newpc);
}
else
{
unsigned int newpc = pc;
- supply_register_by_name ("eip", &newpc);
+ supply_register_by_name (regcache, "eip", &newpc);
}
}
\f
{
unsigned char c;
- read_inferior_memory (pc, &c, 1);
+ (*the_target->read_memory) (pc, &c, 1);
if (c == 0xCC)
return 1;
return 0;
}
\f
+/* Support for debug registers. */
+
+static unsigned long
+x86_linux_dr_get (ptid_t ptid, int regnum)
+{
+ int tid;
+ unsigned long value;
+
+ tid = ptid_get_lwp (ptid);
+
+ errno = 0;
+ value = ptrace (PTRACE_PEEKUSER, tid,
+ offsetof (struct user, u_debugreg[regnum]), 0);
+ if (errno != 0)
+ error ("Couldn't read debug register");
+
+ return value;
+}
+
+static void
+x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
+{
+ int tid;
+
+ tid = ptid_get_lwp (ptid);
+
+ errno = 0;
+ ptrace (PTRACE_POKEUSER, tid,
+ offsetof (struct user, u_debugreg[regnum]), value);
+ if (errno != 0)
+ error ("Couldn't write debug register");
+}
+
+/* Update the inferior's debug register REGNUM from STATE. */
+
+void
+i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum)
+{
+ struct inferior_list_entry *lp;
+ CORE_ADDR addr;
+ /* Only need to update the threads of this process. */
+ int pid = pid_of (get_thread_lwp (current_inferior));
+
+ if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR))
+ fatal ("Invalid debug register %d", regnum);
+
+ addr = state->dr_mirror[regnum];
+
+ for (lp = all_lwps.head; lp; lp = lp->next)
+ {
+ struct lwp_info *lwp = (struct lwp_info *) lp;
+
+ /* The actual update is done later, we just mark that the register
+ needs updating. */
+ if (pid_of (lwp) == pid)
+ lwp->arch_private->debug_registers_changed = 1;
+ }
+}
+
+/* Update the inferior's DR7 debug control register from STATE. */
+
+void
+i386_dr_low_set_control (const struct i386_debug_reg_state *state)
+{
+ struct inferior_list_entry *lp;
+ /* Only need to update the threads of this process. */
+ int pid = pid_of (get_thread_lwp (current_inferior));
+
+ for (lp = all_lwps.head; lp; lp = lp->next)
+ {
+ struct lwp_info *lwp = (struct lwp_info *) lp;
+
+ /* The actual update is done later, we just mark that the register
+ needs updating. */
+ if (pid_of (lwp) == pid)
+ lwp->arch_private->debug_registers_changed = 1;
+ }
+}
+
+/* Get the value of the DR6 debug status register from the inferior
+ and record it in STATE. */
+
+void
+i386_dr_low_get_status (struct i386_debug_reg_state *state)
+{
+ struct lwp_info *lwp = get_thread_lwp (current_inferior);
+ ptid_t ptid = ptid_of (lwp);
+
+ state->dr_status_mirror = x86_linux_dr_get (ptid, DR_STATUS);
+}
+\f
+/* Watchpoint support. */
+
+static int
+x86_insert_point (char type, CORE_ADDR addr, int len)
+{
+ struct process_info *proc = current_process ();
+ switch (type)
+ {
+ case '0':
+ return set_gdb_breakpoint_at (addr);
+ case '2':
+ case '3':
+ case '4':
+ return i386_low_insert_watchpoint (&proc->private->arch_private->debug_reg_state,
+ type, addr, len);
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+x86_remove_point (char type, CORE_ADDR addr, int len)
+{
+ struct process_info *proc = current_process ();
+ switch (type)
+ {
+ case '0':
+ return delete_gdb_breakpoint_at (addr);
+ case '2':
+ case '3':
+ case '4':
+ return i386_low_remove_watchpoint (&proc->private->arch_private->debug_reg_state,
+ type, addr, len);
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
+
+static int
+x86_stopped_by_watchpoint (void)
+{
+ struct process_info *proc = current_process ();
+ return i386_low_stopped_by_watchpoint (&proc->private->arch_private->debug_reg_state);
+}
+
+static CORE_ADDR
+x86_stopped_data_address (void)
+{
+ struct process_info *proc = current_process ();
+ CORE_ADDR addr;
+ if (i386_low_stopped_data_address (&proc->private->arch_private->debug_reg_state,
+ &addr))
+ return addr;
+ return 0;
+}
+\f
+/* Called when a new process is created. */
+
+static struct arch_process_info *
+x86_linux_new_process (void)
+{
+ struct arch_process_info *info = xcalloc (1, sizeof (*info));
+
+ i386_low_init_dregs (&info->debug_reg_state);
+
+ return info;
+}
+
+/* Called when a new thread is detected. */
+
+static struct arch_lwp_info *
+x86_linux_new_thread (void)
+{
+ struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+
+ info->debug_registers_changed = 1;
+
+ return info;
+}
+
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
+
+static void
+x86_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+ ptid_t ptid = ptid_of (lwp);
+
+ if (lwp->arch_private->debug_registers_changed)
+ {
+ int i;
+ int pid = ptid_get_pid (ptid);
+ struct process_info *proc = find_process_pid (pid);
+ struct i386_debug_reg_state *state = &proc->private->arch_private->debug_reg_state;
+
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+ x86_linux_dr_set (ptid, i, state->dr_mirror[i]);
+
+ x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror);
+
+ lwp->arch_private->debug_registers_changed = 0;
+ }
+
+ if (lwp->stopped_by_watchpoint)
+ x86_linux_dr_set (ptid, DR_STATUS, 0);
+}
+\f
/* When GDBSERVER is built as a 64-bit application on linux, the
PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since
debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same
/* Is the inferior 32-bit? If so, then fixup the siginfo object. */
if (register_size (0) == 4)
{
- assert (sizeof (struct siginfo) == sizeof (compat_siginfo_t));
+ if (sizeof (struct siginfo) != sizeof (compat_siginfo_t))
+ fatal ("unexpected difference in siginfo");
if (direction == 0)
compat_siginfo_from_siginfo ((struct compat_siginfo *) inf, native);
return 0;
}
\f
-/* Return non-zero if the target is 64-bit. */
+/* Initialize gdbserver for the architecture of the inferior. */
static void
x86_arch_setup (void)
}
else if (use_64bit)
{
- init_registers_x86_64_linux ();
+ init_registers_amd64_linux ();
/* Amd64 doesn't have HAVE_LINUX_USRREGS. */
the_low_target.num_regs = -1;
NULL,
1,
x86_breakpoint_at,
- NULL,
- NULL,
- NULL,
- NULL,
+ x86_insert_point,
+ x86_remove_point,
+ x86_stopped_by_watchpoint,
+ x86_stopped_data_address,
/* collect_ptrace_register/supply_ptrace_register are not needed in the
native i386 case (no registers smaller than an xfer unit), and are not
used in the biarch case (HAVE_LINUX_USRREGS is not defined). */
NULL,
/* need to fix up i386 siginfo if host is amd64 */
x86_siginfo_fixup,
+ x86_linux_new_process,
+ x86_linux_new_thread,
+ x86_linux_prepare_to_resume
};