diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/gdbserver/ChangeLog | 24 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 8 | ||||
-rw-r--r-- | gdb/gdbserver/configure.srv | 7 | ||||
-rw-r--r-- | gdb/gdbserver/linux-i386-low.c | 210 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 108 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 12 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-64-low.c | 184 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-low.c | 644 |
8 files changed, 790 insertions, 407 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 022f266..798d4de 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,27 @@ +2009-05-12 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. + 2009-05-07 Doug Evans <dje@google.com> * linux-low.c (regsets_fetch_inferior_registers): Fix memory leak. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index a62b25b..f395d9e 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -103,7 +103,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c \ $(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)/inferiors.c \ $(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_low_h) $(server_h) \ $(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 $(linux_low_h) $(server_h) \ 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) diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index 924ac66..e4252af 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -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 ;; diff --git a/gdb/gdbserver/linux-i386-low.c b/gdb/gdbserver/linux-i386-low.c deleted file mode 100644 index a94ce69..0000000 --- a/gdb/gdbserver/linux-i386-low.c +++ /dev/null @@ -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, -}; diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 46969b7..d7fd141 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -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, -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) { @@ -2458,6 +2523,8 @@ linux_test_for_tracefork (void) linux_supports_tracefork_flag = 0; + return; + /* Use CLONE_VM instead of fork, to support uClinux (no MMU). */ #ifdef __ia64__ child_pid = __clone2 (linux_tracefork_child, stack, STACK_SIZE, @@ -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, unsigned char *readbuf, 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, unsigned char *readbuf, 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; } diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index fc93a0e..a23945a 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -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); diff --git a/gdb/gdbserver/linux-x86-64-low.c b/gdb/gdbserver/linux-x86-64-low.c deleted file mode 100644 index e61daff..0000000 --- a/gdb/gdbserver/linux-x86-64-low.c +++ /dev/null @@ -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, -}; diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c new file mode 100644 index 0000000..ffb70dc --- /dev/null +++ b/gdb/gdbserver/linux-x86-low.c @@ -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, +}; |