/* 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,
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 <http://www.gnu.org/licenses/>. */
#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"
#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,
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
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)
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;
}
return pc;
}
-/* Return whether the frame preceding 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);
__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);
}
/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>. */
#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)
{
CORE_ADDR sp;
- char buf[8];
+ gdb_byte buf[8];
- frame_unwind_register (next_frame, SP_REGNUM, buf);
+ get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
sp = extract_unsigned_integer (buf, 8);
/* The sigcontext structure is part of the user context. A pointer
-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_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)
{
/* 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);
}
\f