This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC] i386/amd64 biarch gdbserver
- From: dje at google dot com (Doug Evans)
- To: gdb-patches at sourceware dot org
- Date: Sat, 9 May 2009 12:12:25 -0700 (PDT)
- Subject: [RFC] i386/amd64 biarch gdbserver
Hi.
This patch implements biarch i386/amd64 gdbserver.
I haven't done anything with xml target descriptions, but there
are no regressions so it seems like a good stopping place.
This patch also fixes a bug where current amd64 gdbserver
doesn't handle xmm[8-15]. Testcase to follow later.
Tested on
- i386-linux
- x86_64-linux with gcc -m64
- x86_64-linux with gcc -m32 (and hacking site.exp to specify i386-linux)
using --target_board=native-gdbserver
[ref http://sourceware.org/gdb/wiki/Native_gdbserver_testing]
Comments?
2009-05-09 Doug Evans <dje@google.com>
Biarch support for i386/amd64 gdbserver.
* Makefile.in (SFILES): Remove linux-i386-low.c, linux-x86-64-low.c.
Add linux-x86-low.c.
(linux-i386-low.o, linux-x86-64-low.o): Delete.
(linux-x86-low.o): Add.
* linux-x86-64-low.c: Delete.
* linux-i386-low.c: Delete.
* linux-x86-low.c: New file.
* configure.srv (i?86-linux srv_tgtobj): Replace linux-i386-low.o with
linux-x86-low.o.
(x86_64-linux srv_tgtobj): Replace linux-x86-64-low.o with
linux-x86-low.o.
(x86_64-linux srv_regobj): Add reg-i386-linux.o.
* linux-low.c: Include ansidecl.h, elf/common.h, elf/external.h.
(linux_child_pid_to_exec_file): New function.
(elf_64_header_p, elf_64_file_p): New functions.
(siginfo_fixup): New function.
(linux_xfer_siginfo): New local inf_siginfo. Call siginfo_fixup to
give target a chance to convert layout.
* linux-low.h (linux_target_ops): New member siginfo_fixup.
(linux_child_pid_to_exec_file, elf_64_file_p): Declare.
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v
retrieving revision 1.73
diff -u -p -r1.73 Makefile.in
--- Makefile.in 1 Apr 2009 22:48:05 -0000 1.73
+++ Makefile.in 9 May 2009 19:08:23 -0000
@@ -103,7 +103,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/
$(srcdir)/remote-utils.c $(srcdir)/server.c $(srcdir)/target.c \
$(srcdir)/thread-db.c $(srcdir)/utils.c \
$(srcdir)/linux-arm-low.c $(srcdir)/linux-cris-low.c \
- $(srcdir)/linux-crisv32-low.c $(srcdir)/linux-i386-low.c \
+ $(srcdir)/linux-crisv32-low.c \
$(srcdir)/i387-fp.c \
$(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \
$(srcdir)/linux-m32r-low.c \
@@ -111,7 +111,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/
$(srcdir)/linux-ppc-low.c \
$(srcdir)/linux-s390-low.c \
$(srcdir)/linux-sh-low.c $(srcdir)/linux-sparc-low.c \
- $(srcdir)/linux-x86-64-low.c \
+ $(srcdir)/linux-x86-low.c \
$(srcdir)/linux-xtensa-low.c \
$(srcdir)/win32-arm-low.c $(srcdir)/win32-i386-low.c \
$(srcdir)/win32-low.c $(srcdir)/wincecompat.c \
@@ -296,8 +296,6 @@ linux-arm-low.o: linux-arm-low.c $(linux
$(gdb_proc_service_h)
linux-cris-low.o: linux-cris-low.c $(linux_low_h) $(server_h)
linux-crisv32-low.o: linux-crisv32-low.c $(linux_low_h) $(server_h)
-linux-i386-low.o: linux-i386-low.c $(linux_low_h) $(server_h) \
- $(gdb_proc_service_h)
linux-ia64-low.o: linux-ia64-low.c $(linux_low_h) $(server_h)
linux-m32r-low.o: linux-m32r-low.c $(linux_low_h) $(server_h)
linux-mips-low.o: linux-mips-low.c $(linux_low_h) $(server_h) \
@@ -305,7 +303,7 @@ linux-mips-low.o: linux-mips-low.c $(lin
linux-ppc-low.o: linux-ppc-low.c $(linux_low_h) $(server_h)
linux-s390-low.o: linux-s390-low.c $(linux_low_h) $(server_h)
linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h)
-linux-x86-64-low.o: linux-x86-64-low.c $(linux_low_h) $(server_h) \
+linux-x86-low.o: linux-x86-low.c $(linux_low_h) $(server_h) \
$(gdb_proc_service_h)
linux-xtensa-low.o: linux-xtensa-low.c xtensa-xtregs.c $(linux_low_h) $(server_h)
Index: configure.srv
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.srv,v
retrieving revision 1.40
diff -u -p -r1.40 configure.srv
--- configure.srv 19 Apr 2009 17:54:52 -0000 1.40
+++ configure.srv 9 May 2009 18:49:29 -0000
@@ -56,7 +56,7 @@ case "${target}" in
srv_tgtobj="win32-low.o win32-i386-low.o"
;;
i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o
- srv_tgtobj="linux-low.o linux-i386-low.o i387-fp.o"
+ srv_tgtobj="linux-low.o linux-x86-low.o i387-fp.o"
srv_linux_usrregs=yes
srv_linux_regsets=yes
srv_linux_thread_db=yes
@@ -169,8 +169,9 @@ case "${target}" in
spu*-*-*) srv_regobj=reg-spu.o
srv_tgtobj="spu-low.o"
;;
- x86_64-*-linux*) srv_regobj=reg-x86-64-linux.o
- srv_tgtobj="linux-low.o linux-x86-64-low.o i387-fp.o"
+ x86_64-*-linux*) srv_regobj="reg-x86-64-linux.o reg-i386-linux.o"
+ srv_tgtobj="linux-low.o linux-x86-low.o i387-fp.o"
+ srv_linux_usrregs=yes # This is for i386 progs.
srv_linux_regsets=yes
srv_linux_thread_db=yes
;;
Index: linux-i386-low.c
===================================================================
RCS file: linux-i386-low.c
diff -N linux-i386-low.c
--- linux-i386-low.c 22 Mar 2009 23:57:10 -0000 1.19
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,210 +0,0 @@
-/* GNU/Linux/i386 specific low level interface, for the remote server for GDB.
- Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
- 2007, 2008, 2009 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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 "server.h"
-#include "linux-low.h"
-#include "i387-fp.h"
-
-#include "gdb_proc_service.h"
-
-#include <sys/ptrace.h>
-
-#ifdef HAVE_SYS_REG_H
-#include <sys/reg.h>
-#endif
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-/* Defined in auto-generated file reg-i386-linux.c. */
-void init_registers_i386_linux (void);
-
-
-/* This module only supports access to the general purpose registers. */
-
-#define i386_num_regs 16
-
-/* This stuff comes from i386-linux-nat.c. */
-
-/* Mapping between the general-purpose registers in `struct user'
- format and GDB's register array layout. */
-static int i386_regmap[] =
-{
- EAX * 4, ECX * 4, EDX * 4, EBX * 4,
- UESP * 4, EBP * 4, ESI * 4, EDI * 4,
- EIP * 4, EFL * 4, CS * 4, SS * 4,
- DS * 4, ES * 4, FS * 4, GS * 4
-};
-
-/* Called by libthread_db. */
-
-ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- unsigned int desc[4];
-
- if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
- (void *) idx, (unsigned long) &desc) < 0)
- return PS_ERR;
-
- *(int *)base = desc[1];
- return PS_OK;
-}
-
-static int
-i386_cannot_store_register (int regno)
-{
- return (regno >= i386_num_regs);
-}
-
-static int
-i386_cannot_fetch_register (int regno)
-{
- return (regno >= i386_num_regs);
-}
-
-
-#ifdef HAVE_PTRACE_GETREGS
-#include <sys/procfs.h>
-#include <sys/ptrace.h>
-
-static void
-i386_fill_gregset (void *buf)
-{
- int i;
-
- for (i = 0; i < i386_num_regs; i++)
- collect_register (i, ((char *) buf) + i386_regmap[i]);
-
- collect_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
-}
-
-static void
-i386_store_gregset (const void *buf)
-{
- int i;
-
- for (i = 0; i < i386_num_regs; i++)
- supply_register (i, ((char *) buf) + i386_regmap[i]);
-
- supply_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
-}
-
-static void
-i386_fill_fpregset (void *buf)
-{
- i387_cache_to_fsave (buf);
-}
-
-static void
-i386_store_fpregset (const void *buf)
-{
- i387_fsave_to_cache (buf);
-}
-
-static void
-i386_fill_fpxregset (void *buf)
-{
- i387_cache_to_fxsave (buf);
-}
-
-static void
-i386_store_fpxregset (const void *buf)
-{
- i387_fxsave_to_cache (buf);
-}
-
-#endif /* HAVE_PTRACE_GETREGS */
-
-struct regset_info target_regsets[] = {
-#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
- GENERAL_REGS,
- i386_fill_gregset, i386_store_gregset },
-# ifdef HAVE_PTRACE_GETFPXREGS
- { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, sizeof (elf_fpxregset_t),
- EXTENDED_REGS,
- i386_fill_fpxregset, i386_store_fpxregset },
-# endif
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
- FP_REGS,
- i386_fill_fpregset, i386_store_fpregset },
-#endif /* HAVE_PTRACE_GETREGS */
- { 0, 0, -1, -1, NULL, NULL }
-};
-
-static const unsigned char i386_breakpoint[] = { 0xCC };
-#define i386_breakpoint_len 1
-
-extern int debug_threads;
-
-static CORE_ADDR
-i386_get_pc ()
-{
- unsigned long pc;
-
- collect_register_by_name ("eip", &pc);
-
- if (debug_threads)
- fprintf (stderr, "stop pc (before any decrement) is %08lx\n", pc);
- return pc;
-}
-
-static void
-i386_set_pc (CORE_ADDR newpc)
-{
- if (debug_threads)
- fprintf (stderr, "set pc to %08lx\n", (long) newpc);
- supply_register_by_name ("eip", &newpc);
-}
-
-static int
-i386_breakpoint_at (CORE_ADDR pc)
-{
- unsigned char c;
-
- read_inferior_memory (pc, &c, 1);
- if (c == 0xCC)
- return 1;
-
- return 0;
-}
-
-struct linux_target_ops the_low_target = {
- init_registers_i386_linux,
- i386_num_regs,
- i386_regmap,
- i386_cannot_fetch_register,
- i386_cannot_store_register,
- i386_get_pc,
- i386_set_pc,
- i386_breakpoint,
- i386_breakpoint_len,
- NULL,
- 1,
- i386_breakpoint_at,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
-};
Index: linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.100
diff -u -p -r1.100 linux-low.c
--- linux-low.c 7 May 2009 23:29:25 -0000 1.100
+++ linux-low.c 9 May 2009 18:49:30 -0000
@@ -19,6 +19,9 @@
#include "server.h"
#include "linux-low.h"
+#include "ansidecl.h" /* For ATTRIBUTE_PACKED, must be bug in external.h. */
+#include "elf/common.h"
+#include "elf/external.h"
#include <sys/wait.h>
#include <stdio.h>
@@ -154,6 +157,68 @@ static int linux_event_pipe[2] = { -1, -
static void send_sigstop (struct inferior_list_entry *entry);
static void wait_for_sigstop (struct inferior_list_entry *entry);
+/* Accepts an integer PID; Returns a string representing a file that
+ can be opened to get info for the child process.
+ Space for the result is malloc'd, caller must free. */
+
+char *
+linux_child_pid_to_exec_file (int pid)
+{
+ char *name1, *name2;
+
+ name1 = xmalloc (MAXPATHLEN);
+ name2 = xmalloc (MAXPATHLEN);
+ memset (name2, 0, MAXPATHLEN);
+
+ sprintf (name1, "/proc/%d/exe", pid);
+ if (readlink (name1, name2, MAXPATHLEN) > 0)
+ {
+ free (name1);
+ return name2;
+ }
+ else
+ {
+ free (name2);
+ return name1;
+ }
+}
+
+/* Return non-zero if HEADER is a 64-bit ELF file. */
+
+static int
+elf_64_header_p (const Elf64_External_Ehdr *header)
+{
+ return (header->e_ident[EI_MAG0] == ELFMAG0
+ && header->e_ident[EI_MAG1] == ELFMAG1
+ && header->e_ident[EI_MAG2] == ELFMAG2
+ && header->e_ident[EI_MAG3] == ELFMAG3
+ && header->e_ident[EI_CLASS] == ELFCLASS64);
+}
+
+/* Return non-zero if FILE is a 64-bit ELF file,
+ zero if the file is not a 64-bit ELF file,
+ and -1 if the file is not accessible or doesn't exist. */
+
+int
+elf_64_file_p (const char *file)
+{
+ Elf64_External_Ehdr header;
+ int fd;
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (read (fd, &header, sizeof (header)) != sizeof (header))
+ {
+ close (fd);
+ return 0;
+ }
+ close (fd);
+
+ return elf_64_header_p (&header);
+}
+
static void
delete_lwp (struct lwp_info *lwp)
{
@@ -2786,12 +2853,35 @@ linux_qxfer_osdata (const char *annex,
return len;
}
+/* Convert a native/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. */
+
+static void
+siginfo_fixup (struct siginfo *siginfo, void *inf_siginfo, int direction)
+{
+ int done = 0;
+
+ if (the_low_target.siginfo_fixup != NULL)
+ done = the_low_target.siginfo_fixup (siginfo, inf_siginfo, direction);
+
+ /* If there was no callback, or the callback didn't do anything,
+ then just do a straight memcpy. */
+ if (!done)
+ {
+ if (direction == 1)
+ memcpy (siginfo, inf_siginfo, sizeof (struct siginfo));
+ else
+ memcpy (inf_siginfo, siginfo, sizeof (struct siginfo));
+ }
+}
+
static int
linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
unsigned const char *writebuf, CORE_ADDR offset, int len)
{
+ int pid;
struct siginfo siginfo;
- long pid = -1;
+ char inf_siginfo[sizeof (struct siginfo)];
if (current_inferior == NULL)
return -1;
@@ -2799,7 +2889,7 @@ linux_xfer_siginfo (const char *annex, u
pid = lwpid_of (get_thread_lwp (current_inferior));
if (debug_threads)
- fprintf (stderr, "%s siginfo for lwp %ld.\n",
+ fprintf (stderr, "%s siginfo for lwp %d.\n",
readbuf != NULL ? "Reading" : "Writing",
pid);
@@ -2809,14 +2899,24 @@ linux_xfer_siginfo (const char *annex, u
if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo) != 0)
return -1;
+ /* When GDBSERVER is built as a 64-bit application, ptrace writes into
+ SIGINFO an object with 64-bit layout. Since debugging a 32-bit
+ inferior with a 64-bit GDBSERVER should look the same as debugging it
+ with a 32-bit GDBSERVER, we need to convert it. */
+ siginfo_fixup (&siginfo, inf_siginfo, 0);
+
if (offset + len > sizeof (siginfo))
len = sizeof (siginfo) - offset;
if (readbuf != NULL)
- memcpy (readbuf, (char *) &siginfo + offset, len);
+ memcpy (readbuf, inf_siginfo + offset, len);
else
{
- memcpy ((char *) &siginfo + offset, writebuf, len);
+ memcpy (inf_siginfo + offset, writebuf, len);
+
+ /* Convert back to ptrace layout before flushing it out. */
+ siginfo_fixup (&siginfo, inf_siginfo, 1);
+
if (ptrace (PTRACE_SETSIGINFO, pid, 0, &siginfo) != 0)
return -1;
}
Index: linux-low.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v
retrieving revision 1.29
diff -u -p -r1.29 linux-low.h
--- linux-low.h 6 May 2009 17:32:59 -0000 1.29
+++ linux-low.h 9 May 2009 18:49:30 -0000
@@ -43,6 +43,8 @@ struct regset_info
extern struct regset_info target_regsets[];
#endif
+struct siginfo;
+
struct process_info_private
{
/* True if this process has loaded thread_db, and it is active. */
@@ -75,7 +77,6 @@ struct linux_target_ops
int breakpoint_len;
CORE_ADDR (*breakpoint_reinsert_addr) (void);
-
int decr_pc_after_break;
int (*breakpoint_at) (CORE_ADDR pc);
@@ -89,6 +90,12 @@ struct linux_target_ops
for registers smaller than an xfer unit). */
void (*collect_ptrace_register) (int regno, char *buf);
void (*supply_ptrace_register) (int regno, const char *buf);
+
+ /* Hook to convert from target format to ptrace format and back.
+ Returns true if any conversion was done; false otherwise.
+ If DIRECTION is 1, then copy from INF to NATIVE.
+ If DIRECTION is 0, copy from NATIVE to INF. */
+ int (*siginfo_fixup) (struct siginfo *native, void *inf, int direction);
};
extern struct linux_target_ops the_low_target;
@@ -169,6 +176,9 @@ struct lwp_info
extern struct inferior_list all_lwps;
+char *linux_child_pid_to_exec_file (int pid);
+int elf_64_file_p (const char *file);
+
void linux_attach_lwp (unsigned long pid);
int thread_db_init (int use_events);
Index: linux-x86-64-low.c
===================================================================
RCS file: linux-x86-64-low.c
diff -N linux-x86-64-low.c
--- linux-x86-64-low.c 22 Mar 2009 23:57:10 -0000 1.22
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,184 +0,0 @@
-/* GNU/Linux/x86-64 specific low level interface, for the remote server
- for GDB.
- Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
- Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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 "server.h"
-#include "linux-low.h"
-#include "i387-fp.h"
-
-#include "gdb_proc_service.h"
-
-/* Defined in auto-generated file reg-x86-64-linux.c. */
-void init_registers_x86_64_linux (void);
-
-#include <sys/reg.h>
-#include <sys/procfs.h>
-#include <sys/ptrace.h>
-
-/* This definition comes from prctl.h, but some kernels may not have it. */
-#ifndef PTRACE_ARCH_PRCTL
-#define PTRACE_ARCH_PRCTL 30
-#endif
-
-/* The following definitions come from prctl.h, but may be absent
- for certain configurations. */
-#ifndef ARCH_GET_FS
-#define ARCH_SET_GS 0x1001
-#define ARCH_SET_FS 0x1002
-#define ARCH_GET_FS 0x1003
-#define ARCH_GET_GS 0x1004
-#endif
-
-static int x86_64_regmap[] = {
- RAX * 8, RBX * 8, RCX * 8, RDX * 8,
- RSI * 8, RDI * 8, RBP * 8, RSP * 8,
- R8 * 8, R9 * 8, R10 * 8, R11 * 8,
- R12 * 8, R13 * 8, R14 * 8, R15 * 8,
- RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
- DS * 8, ES * 8, FS * 8, GS * 8,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1,
- ORIG_RAX * 8
-};
-
-#define X86_64_NUM_GREGS (sizeof(x86_64_regmap)/sizeof(int))
-
-/* Called by libthread_db. */
-
-ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
- lwpid_t lwpid, int idx, void **base)
-{
- switch (idx)
- {
- case FS:
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
- return PS_OK;
- break;
- case GS:
- if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
- return PS_OK;
- break;
- default:
- return PS_BADADDR;
- }
- return PS_ERR;
-}
-
-static void
-x86_64_fill_gregset (void *buf)
-{
- int i;
-
- for (i = 0; i < X86_64_NUM_GREGS; i++)
- if (x86_64_regmap[i] != -1)
- collect_register (i, ((char *) buf) + x86_64_regmap[i]);
-}
-
-static void
-x86_64_store_gregset (const void *buf)
-{
- int i;
-
- for (i = 0; i < X86_64_NUM_GREGS; i++)
- if (x86_64_regmap[i] != -1)
- supply_register (i, ((char *) buf) + x86_64_regmap[i]);
-}
-
-static void
-x86_64_fill_fpregset (void *buf)
-{
- i387_cache_to_fxsave (buf);
-}
-
-static void
-x86_64_store_fpregset (const void *buf)
-{
- i387_fxsave_to_cache (buf);
-}
-
-struct regset_info target_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
- GENERAL_REGS,
- x86_64_fill_gregset, x86_64_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
- FP_REGS,
- x86_64_fill_fpregset, x86_64_store_fpregset },
- { 0, 0, -1, -1, NULL, NULL }
-};
-
-static const unsigned char x86_64_breakpoint[] = { 0xCC };
-#define x86_64_breakpoint_len 1
-
-extern int debug_threads;
-
-static CORE_ADDR
-x86_64_get_pc ()
-{
- unsigned long pc;
-
- collect_register_by_name ("rip", &pc);
-
- if (debug_threads)
- fprintf (stderr, "stop pc (before any decrement) is %08lx\n", pc);
- return pc;
-}
-
-static void
-x86_64_set_pc (CORE_ADDR newpc)
-{
- if (debug_threads)
- fprintf (stderr, "set pc to %08lx\n", (long) newpc);
- supply_register_by_name ("rip", &newpc);
-}
-
-static int
-x86_64_breakpoint_at (CORE_ADDR pc)
-{
- unsigned char c;
-
- read_inferior_memory (pc, &c, 1);
- if (c == 0xCC)
- return 1;
-
- return 0;
-}
-
-struct linux_target_ops the_low_target = {
- init_registers_x86_64_linux,
- -1,
- NULL,
- NULL,
- NULL,
- x86_64_get_pc,
- x86_64_set_pc,
- x86_64_breakpoint,
- x86_64_breakpoint_len,
- NULL,
- 1,
- x86_64_breakpoint_at,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
-};
Index: linux-x86-low.c
===================================================================
RCS file: linux-x86-low.c
diff -N linux-x86-low.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ linux-x86-low.c 9 May 2009 18:49:30 -0000
@@ -0,0 +1,644 @@
+/* GNU/Linux/x86-64 specific low level interface, for the remote server
+ for GDB.
+ Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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 <signal.h>
+#include "server.h"
+#include "linux-low.h"
+#include "i387-fp.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. */
+void init_registers_i386_linux (void);
+/* Defined in auto-generated file reg-x86-64-linux.c. */
+void init_registers_x86_64_linux (void);
+
+#include <sys/reg.h>
+#include <sys/procfs.h>
+#include <sys/ptrace.h>
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+/* This definition comes from prctl.h, but some kernels may not have it. */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL 30
+#endif
+
+/* The following definitions come from prctl.h, but may be absent
+ for certain configurations. */
+#ifndef ARCH_GET_FS
+#define ARCH_SET_GS 0x1001
+#define ARCH_SET_FS 0x1002
+#define ARCH_GET_FS 0x1003
+#define ARCH_GET_GS 0x1004
+#endif
+
+#ifdef __x86_64__
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout.
+ Note that the transfer layout uses 64-bit regs. */
+static /*const*/ int i386_regmap[] =
+{
+ RAX * 8, RCX * 8, RDX * 8, RBX * 8,
+ RSP * 8, RBP * 8, RSI * 8, RDI * 8,
+ RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
+ DS * 8, ES * 8, FS * 8, GS * 8
+};
+
+#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
+
+/* So code below doesn't have to care, i386 or amd64. */
+#define ORIG_EAX ORIG_RAX
+
+static const int x86_64_regmap[] =
+{
+ RAX * 8, RBX * 8, RCX * 8, RDX * 8,
+ RSI * 8, RDI * 8, RBP * 8, RSP * 8,
+ R8 * 8, R9 * 8, R10 * 8, R11 * 8,
+ R12 * 8, R13 * 8, R14 * 8, R15 * 8,
+ RIP * 8, EFLAGS * 8, CS * 8, SS * 8,
+ DS * 8, ES * 8, FS * 8, GS * 8,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ ORIG_RAX * 8
+};
+
+#define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
+
+#else /* ! __x86_64__ */
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout. */
+static /*const*/ int i386_regmap[] =
+{
+ EAX * 4, ECX * 4, EDX * 4, EBX * 4,
+ UESP * 4, EBP * 4, ESI * 4, EDI * 4,
+ EIP * 4, EFL * 4, CS * 4, SS * 4,
+ DS * 4, ES * 4, FS * 4, GS * 4
+};
+
+#define I386_NUM_REGS (sizeof (i386_regmap) / sizeof (i386_regmap[0]))
+
+#endif
+
+/* Called by libthread_db. */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+ lwpid_t lwpid, int idx, void **base)
+{
+#ifdef __x86_64__
+ int use_64bit = register_size (0) == 8;
+
+ if (use_64bit)
+ {
+ switch (idx)
+ {
+ case FS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
+ return PS_OK;
+ break;
+ case GS:
+ if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
+ return PS_OK;
+ break;
+ default:
+ return PS_BADADDR;
+ }
+ return PS_ERR;
+ }
+#endif
+
+ {
+ unsigned int desc[4];
+
+ if (ptrace (PTRACE_GET_THREAD_AREA, lwpid,
+ (void *) (intptr_t) idx, (unsigned long) &desc) < 0)
+ return PS_ERR;
+
+ *(int *)base = desc[1];
+ return PS_OK;
+ }
+}
+
+static int
+i386_cannot_store_register (int regno)
+{
+ return regno >= I386_NUM_REGS;
+}
+
+static int
+i386_cannot_fetch_register (int regno)
+{
+ return regno >= I386_NUM_REGS;
+}
+
+static void
+x86_fill_gregset (void *buf)
+{
+ int i;
+
+#ifdef __x86_64__
+ if (register_size (0) == 8)
+ {
+ for (i = 0; i < X86_64_NUM_REGS; i++)
+ if (x86_64_regmap[i] != -1)
+ collect_register (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_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+}
+
+static void
+x86_store_gregset (const void *buf)
+{
+ int i;
+
+#ifdef __x86_64__
+ if (register_size (0) == 8)
+ {
+ for (i = 0; i < X86_64_NUM_REGS; i++)
+ if (x86_64_regmap[i] != -1)
+ supply_register (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_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+}
+
+static void
+x86_fill_fpregset (void *buf)
+{
+#ifdef __x86_64__
+ i387_cache_to_fxsave (buf);
+#else
+ i387_cache_to_fsave (buf);
+#endif
+}
+
+static void
+x86_store_fpregset (const void *buf)
+{
+#ifdef __x86_64__
+ i387_fxsave_to_cache (buf);
+#else
+ i387_fsave_to_cache (buf);
+#endif
+}
+
+#ifndef __x86_64__
+
+static void
+x86_fill_fpxregset (void *buf)
+{
+ i387_cache_to_fxsave (buf);
+}
+
+static void
+x86_store_fpxregset (const void *buf)
+{
+ i387_fxsave_to_cache (buf);
+}
+
+#endif
+
+/* ??? The non-biarch i386 case stores all the i387 regs twice.
+ Once in i387_.*fsave.* and once in i387_.*fxsave.*.
+ This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
+ doesn't work. IWBN to avoid the duplication in the case where it
+ does work. Maybe the arch_setup routine could check whether it works
+ and update target_regsets accordingly, maybe by moving target_regsets
+ to linux_target_ops and set the right one there, rather than having to
+ modify the target_regsets global. */
+
+struct regset_info target_regsets[] =
+{
+#ifdef HAVE_PTRACE_GETREGS
+ { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ GENERAL_REGS,
+ x86_fill_gregset, x86_store_gregset },
+# ifndef __x86_64__
+# ifdef HAVE_PTRACE_GETFPXREGS
+ { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, sizeof (elf_fpxregset_t),
+ EXTENDED_REGS,
+ x86_fill_fpxregset, x86_store_fpxregset },
+# endif
+# endif
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
+ FP_REGS,
+ x86_fill_fpregset, x86_store_fpregset },
+#endif /* HAVE_PTRACE_GETREGS */
+ { 0, 0, -1, -1, NULL, NULL }
+};
+
+static CORE_ADDR
+x86_get_pc (void)
+{
+ int use_64bit = register_size (0) == 8;
+
+ if (use_64bit)
+ {
+ unsigned long pc;
+ collect_register_by_name ("rip", &pc);
+ return (CORE_ADDR) pc;
+ }
+ else
+ {
+ unsigned int pc;
+ collect_register_by_name ("eip", &pc);
+ return (CORE_ADDR) pc;
+ }
+}
+
+static void
+x86_set_pc (CORE_ADDR pc)
+{
+ int use_64bit = register_size (0) == 8;
+
+ if (use_64bit)
+ {
+ unsigned long newpc = pc;
+ supply_register_by_name ("rip", &newpc);
+ }
+ else
+ {
+ unsigned int newpc = pc;
+ supply_register_by_name ("eip", &newpc);
+ }
+}
+
+static const unsigned char x86_breakpoint[] = { 0xCC };
+#define x86_breakpoint_len 1
+
+static int
+x86_breakpoint_at (CORE_ADDR pc)
+{
+ unsigned char c;
+
+ read_inferior_memory (pc, &c, 1);
+ if (c == 0xCC)
+ return 1;
+
+ return 0;
+}
+
+/* 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
+ as debugging it with a 32-bit GDBSERVER, we do the 32-bit <-> 64-bit
+ conversion in-place ourselves. */
+
+/* These types below (compat_*) define a siginfo type that is layout
+ compatible with the siginfo type exported by the 32-bit userspace
+ support. */
+
+#ifdef __x86_64__
+
+typedef int compat_int_t;
+typedef unsigned int compat_uptr_t;
+
+typedef int compat_time_t;
+typedef int compat_timer_t;
+typedef int compat_clock_t;
+
+struct compat_timeval
+{
+ compat_time_t tv_sec;
+ int tv_usec;
+};
+
+typedef union compat_sigval
+{
+ compat_int_t sival_int;
+ compat_uptr_t sival_ptr;
+} compat_sigval_t;
+
+typedef struct compat_siginfo
+{
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ union
+ {
+ int _pad[((128 / sizeof (int)) - 3)];
+
+ /* kill() */
+ struct
+ {
+ unsigned int _pid;
+ unsigned int _uid;
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct
+ {
+ compat_timer_t _tid;
+ int _overrun;
+ compat_sigval_t _sigval;
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct
+ {
+ unsigned int _pid;
+ unsigned int _uid;
+ compat_sigval_t _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct
+ {
+ unsigned int _pid;
+ unsigned int _uid;
+ int _status;
+ compat_clock_t _utime;
+ compat_clock_t _stime;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ struct
+ {
+ unsigned int _addr;
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct
+ {
+ int _band;
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} compat_siginfo_t;
+
+#define cpt_si_pid _sifields._kill._pid
+#define cpt_si_uid _sifields._kill._uid
+#define cpt_si_timerid _sifields._timer._tid
+#define cpt_si_overrun _sifields._timer._overrun
+#define cpt_si_status _sifields._sigchld._status
+#define cpt_si_utime _sifields._sigchld._utime
+#define cpt_si_stime _sifields._sigchld._stime
+#define cpt_si_ptr _sifields._rt._sigval.sival_ptr
+#define cpt_si_addr _sifields._sigfault._addr
+#define cpt_si_band _sifields._sigpoll._band
+#define cpt_si_fd _sifields._sigpoll._fd
+
+/* glibc at least up to 2.3.2 doesn't have si_timerid, si_overrun.
+ In their place is si_timer1,si_timer2. */
+#ifndef si_timerid
+#define si_timerid si_timer1
+#endif
+#ifndef si_overrun
+#define si_overrun si_timer2
+#endif
+
+static void
+compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from)
+{
+ memset (to, 0, sizeof (*to));
+
+ to->si_signo = from->si_signo;
+ to->si_errno = from->si_errno;
+ to->si_code = from->si_code;
+
+ if (to->si_code < 0)
+ {
+ to->cpt_si_ptr = (intptr_t) from->si_ptr;
+ }
+ else if (to->si_code == SI_USER)
+ {
+ to->cpt_si_pid = from->si_pid;
+ to->cpt_si_uid = from->si_uid;
+ }
+ else if (to->si_code == SI_TIMER)
+ {
+ to->cpt_si_timerid = from->si_timerid;
+ to->cpt_si_overrun = from->si_overrun;
+ to->cpt_si_ptr = (intptr_t) from->si_ptr;
+ }
+ else
+ {
+ switch (to->si_signo)
+ {
+ case SIGCHLD:
+ to->cpt_si_pid = from->si_pid;
+ to->cpt_si_uid = from->si_uid;
+ to->cpt_si_status = from->si_status;
+ to->cpt_si_utime = from->si_utime;
+ to->cpt_si_stime = from->si_stime;
+ break;
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ to->cpt_si_addr = (intptr_t) from->si_addr;
+ break;
+ case SIGPOLL:
+ to->cpt_si_band = from->si_band;
+ to->cpt_si_fd = from->si_fd;
+ break;
+ default:
+ to->cpt_si_pid = from->si_pid;
+ to->cpt_si_uid = from->si_uid;
+ to->cpt_si_ptr = (intptr_t) from->si_ptr;
+ break;
+ }
+ }
+}
+
+static void
+siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
+{
+ memset (to, 0, sizeof (*to));
+
+ to->si_signo = from->si_signo;
+ to->si_errno = from->si_errno;
+ to->si_code = from->si_code;
+
+ if (to->si_code < 0)
+ {
+ to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
+ }
+ else if (to->si_code == SI_USER)
+ {
+ to->si_pid = from->cpt_si_pid;
+ to->si_uid = from->cpt_si_uid;
+ }
+ else if (to->si_code == SI_TIMER)
+ {
+ to->si_timerid = from->cpt_si_timerid;
+ to->si_overrun = from->cpt_si_overrun;
+ to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
+ }
+ else
+ {
+ switch (to->si_signo)
+ {
+ case SIGCHLD:
+ to->si_pid = from->cpt_si_pid;
+ to->si_uid = from->cpt_si_uid;
+ to->si_status = from->cpt_si_status;
+ to->si_utime = from->cpt_si_utime;
+ to->si_stime = from->cpt_si_stime;
+ break;
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ to->si_addr = (void *) (intptr_t) from->cpt_si_addr;
+ break;
+ case SIGPOLL:
+ to->si_band = from->cpt_si_band;
+ to->si_fd = from->cpt_si_fd;
+ break;
+ default:
+ to->si_pid = from->cpt_si_pid;
+ to->si_uid = from->cpt_si_uid;
+ to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr;
+ break;
+ }
+ }
+}
+
+#endif /* __x86_64__ */
+
+/* Convert a native/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. Returns true if any
+ conversion was done; false otherwise. If DIRECTION is 1, then copy
+ from INF to NATIVE. If DIRECTION is 0, copy from NATIVE to
+ INF. */
+
+static int
+x86_siginfo_fixup (struct siginfo *native, void *inf, int direction)
+{
+#ifdef __x86_64__
+ /* 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 (direction == 0)
+ compat_siginfo_from_siginfo ((struct compat_siginfo *) inf, native);
+ else
+ siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf);
+
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+/* Return non-zero if the target is 64-bit. */
+
+static void
+x86_arch_setup (void)
+{
+#ifdef __x86_64__
+ int pid = pid_of (get_thread_lwp (current_inferior));
+ char *file = linux_child_pid_to_exec_file (pid);
+ int use_64bit = elf_64_file_p (file);
+
+ free (file);
+
+ if (use_64bit < 0)
+ {
+ /* This can only happen if /proc/<pid>/exe is unreadable,
+ but "that can't happen" if we've gotten this far.
+ Fall through and assume this is a 32-bit program. */
+ }
+ else if (use_64bit)
+ {
+ init_registers_x86_64_linux ();
+
+ /* Amd64 doesn't have HAVE_LINUX_USRREGS. */
+ the_low_target.num_regs = -1;
+ the_low_target.regmap = NULL;
+ the_low_target.cannot_fetch_register = NULL;
+ the_low_target.cannot_store_register = NULL;
+
+ /* Amd64 has 16 xmm regs. */
+ num_xmm_registers = 16;
+
+ return;
+ }
+#endif
+
+ /* Ok we have a 32-bit inferior. */
+
+ init_registers_i386_linux ();
+
+ the_low_target.num_regs = I386_NUM_REGS;
+ the_low_target.regmap = i386_regmap;
+ the_low_target.cannot_fetch_register = i386_cannot_fetch_register;
+ the_low_target.cannot_store_register = i386_cannot_store_register;
+
+ /* I386 has 8 xmm regs. */
+ num_xmm_registers = 8;
+}
+
+/* This is initialized assuming an amd64 target.
+ x86_arch_setup will correct it for i386 or amd64 targets. */
+
+struct linux_target_ops the_low_target =
+{
+ x86_arch_setup,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ x86_get_pc,
+ x86_set_pc,
+ x86_breakpoint,
+ x86_breakpoint_len,
+ NULL,
+ 1,
+ x86_breakpoint_at,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 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,
+ NULL,
+ /* need to fix up i386 siginfo if host is amd64 */
+ x86_siginfo_fixup,
+};