X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Famd64-linux-tdep.c;h=92f52655d57c9cb7bf443cde0d224656057b010a;hb=e17a4113357102b55cfa5b80557d590a46a43300;hp=be7ace2d065b39ef563346fa9ea47f1c9b1ad4e6;hpb=911bc6ee3f36711ed37e8b829ff1c5622a8b2082;p=deliverable%2Fbinutils-gdb.git
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index be7ace2d06..92f52655d5 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1,13 +1,14 @@
/* Target-dependent code for GNU/Linux x86-64.
- Copyright 2001, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
Contributed by Jiri Smid, SuSE Labs.
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 2 of the License, or
+ 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,
@@ -16,16 +17,19 @@
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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ along with this program. If not, see . */
#include "defs.h"
+#include "arch-utils.h"
#include "frame.h"
#include "gdbcore.h"
#include "regcache.h"
#include "osabi.h"
#include "symtab.h"
+#include "gdbtypes.h"
+#include "reggroups.h"
+#include "amd64-linux-tdep.h"
+#include "linux-tdep.h"
#include "gdb_string.h"
@@ -72,7 +76,7 @@ static int amd64_linux_gregset_reg_offset[] =
#define LINUX_SIGTRAMP_INSN1 0x0f /* syscall */
#define LINUX_SIGTRAMP_OFFSET1 7
-static const unsigned char linux_sigtramp_code[] =
+static const gdb_byte linux_sigtramp_code[] =
{
/* mov $__NR_rt_sigreturn, %rax */
LINUX_SIGTRAMP_INSN0, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00,
@@ -86,9 +90,10 @@ static const unsigned char linux_sigtramp_code[] =
the routine. Otherwise, return 0. */
static CORE_ADDR
-amd64_linux_sigtramp_start (CORE_ADDR pc)
+amd64_linux_sigtramp_start (struct frame_info *this_frame)
{
- unsigned char buf[LINUX_SIGTRAMP_LEN];
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ gdb_byte buf[LINUX_SIGTRAMP_LEN];
/* We only recognize a signal trampoline if PC is at the start of
one of the two instructions. We optimize for finding the PC at
@@ -97,7 +102,7 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
PC is not at the start of the instruction sequence, there will be
a few trailing readable bytes on the stack. */
- if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
+ if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof buf))
return 0;
if (buf[0] != LINUX_SIGTRAMP_INSN0)
@@ -106,8 +111,7 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
return 0;
pc -= LINUX_SIGTRAMP_OFFSET1;
-
- if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
+ if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof buf))
return 0;
}
@@ -117,13 +121,13 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
return pc;
}
-/* Return whether the frame preciding NEXT_FRAME corresponds to a
- GNU/Linux sigtramp routine. */
+/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
+ routine. */
static int
-amd64_linux_sigtramp_p (struct frame_info *next_frame)
+amd64_linux_sigtramp_p (struct frame_info *this_frame)
{
- CORE_ADDR pc = frame_pc_unwind (next_frame);
+ CORE_ADDR pc = get_frame_pc (this_frame);
char *name;
find_pc_partial_function (pc, &name, NULL, NULL);
@@ -135,7 +139,7 @@ amd64_linux_sigtramp_p (struct frame_info *next_frame)
__sigaction, or __libc_sigaction (all aliases to the same
function). */
if (name == NULL || strstr (name, "sigaction") != NULL)
- return (amd64_linux_sigtramp_start (pc) != 0);
+ return (amd64_linux_sigtramp_start (this_frame) != 0);
return (strcmp ("__restore_rt", name) == 0);
}
@@ -143,17 +147,19 @@ amd64_linux_sigtramp_p (struct frame_info *next_frame)
/* Offset to struct sigcontext in ucontext, from . */
#define AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 40
-/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
- routine, return the address of the associated sigcontext structure. */
+/* Assuming THIS_FRAME is a GNU/Linux sigtramp routine, return the
+ address of the associated sigcontext structure. */
static CORE_ADDR
-amd64_linux_sigcontext_addr (struct frame_info *next_frame)
+amd64_linux_sigcontext_addr (struct frame_info *this_frame)
{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR sp;
- char buf[8];
+ gdb_byte buf[8];
- frame_unwind_register (next_frame, SP_REGNUM, buf);
- sp = extract_unsigned_integer (buf, 8);
+ get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+ sp = extract_unsigned_integer (buf, 8, byte_order);
/* The sigcontext structure is part of the user context. A pointer
to the user context is passed as the third argument to the signal
@@ -199,6 +205,61 @@ static int amd64_linux_sc_reg_offset[] =
-1 /* %gs */
};
+/* Replacement register functions which know about %orig_rax. */
+
+static const char *
+amd64_linux_register_name (struct gdbarch *gdbarch, int reg)
+{
+ if (reg == AMD64_LINUX_ORIG_RAX_REGNUM)
+ return "orig_rax";
+
+ return amd64_register_name (gdbarch, reg);
+}
+
+static struct type *
+amd64_linux_register_type (struct gdbarch *gdbarch, int reg)
+{
+ if (reg == AMD64_LINUX_ORIG_RAX_REGNUM)
+ return builtin_type (gdbarch)->builtin_int64;
+
+ return amd64_register_type (gdbarch, reg);
+}
+
+static int
+amd64_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+ struct reggroup *group)
+{
+ if (regnum == AMD64_LINUX_ORIG_RAX_REGNUM)
+ return (group == system_reggroup
+ || group == save_reggroup
+ || group == restore_reggroup);
+ return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+/* Set the program counter for process PTID to PC. */
+
+static void
+amd64_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ regcache_cooked_write_unsigned (regcache, AMD64_RIP_REGNUM, pc);
+
+ /* We must be careful with modifying the program counter. If we
+ just interrupted a system call, the kernel might try to restart
+ it when we resume the inferior. On restarting the system call,
+ the kernel will try backing up the program counter even though it
+ no longer points at the system call. This typically results in a
+ SIGSEGV or SIGILL. We can prevent this by writing `-1' in the
+ "orig_rax" pseudo-register.
+
+ Note that "orig_rax" is saved when setting up a dummy call frame.
+ This means that it is properly restored when that frame is
+ popped, and that the interrupted system call will be restarted
+ when we resume the inferior on return from a function call from
+ within GDB. In all other cases the system call will not be
+ restarted. */
+ regcache_cooked_write_unsigned (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, -1);
+}
+
static void
amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -218,6 +279,28 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* GNU/Linux uses SVR4-style shared libraries. */
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_lp64_fetch_link_map_offsets);
+
+ /* Add the %orig_rax register used for syscall restarting. */
+ set_gdbarch_write_pc (gdbarch, amd64_linux_write_pc);
+ set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
+ set_gdbarch_register_name (gdbarch, amd64_linux_register_name);
+ set_gdbarch_register_type (gdbarch, amd64_linux_register_type);
+ set_gdbarch_register_reggroup_p (gdbarch, amd64_linux_register_reggroup_p);
+
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
+ /* Displaced stepping. */
+ set_gdbarch_displaced_step_copy_insn (gdbarch,
+ amd64_displaced_step_copy_insn);
+ set_gdbarch_displaced_step_fixup (gdbarch, amd64_displaced_step_fixup);
+ set_gdbarch_displaced_step_free_closure (gdbarch,
+ simple_displaced_step_free_closure);
+ set_gdbarch_displaced_step_location (gdbarch,
+ displaced_step_at_entry_point);
+
+ set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
}