X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-x86-low.c;h=4a01750c1724f0708a6d2a079dba00c7403963ad;hb=8424cc978c8c76aca7945d50408762de65646095;hp=c376cab10d51a8d8a3fea4d55f2bcf3502f91e39;hpb=4180215b9db1549b88da2da2fcc320fe28233481;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index c376cab10d..4a01750c17 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -1,6 +1,6 @@ /* GNU/Linux/x86-64 specific low level interface, for the remote server for GDB. - Copyright (C) 2002-2015 Free Software Foundation, Inc. + Copyright (C) 2002-2016 Free Software Foundation, Inc. This file is part of GDB. @@ -25,6 +25,7 @@ #include "i387-fp.h" #include "x86-low.h" #include "x86-xstate.h" +#include "nat/gdb_ptrace.h" #include "gdb_proc_service.h" /* Don't include elf/common.h if linux/elf.h got included by @@ -37,6 +38,9 @@ #include "tdesc.h" #include "tracepoint.h" #include "ax.h" +#include "nat/linux-nat.h" +#include "nat/x86-linux.h" +#include "nat/x86-linux-dregs.h" #ifdef __x86_64__ /* Defined in auto-generated file amd64-linux.c. */ @@ -114,18 +118,9 @@ static const char *xmltarget_amd64_linux_no_xml = "@\ #include #include -#include +#include "nat/gdb_ptrace.h" #include -#ifndef PTRACE_GETREGSET -#define PTRACE_GETREGSET 0x4204 -#endif - -#ifndef PTRACE_SETREGSET -#define PTRACE_SETREGSET 0x4205 -#endif - - #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 25 #endif @@ -151,14 +146,6 @@ struct arch_process_info struct x86_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' @@ -476,7 +463,7 @@ static struct regset_info x86_regsets[] = FP_REGS, x86_fill_fpregset, x86_store_fpregset }, #endif /* HAVE_PTRACE_GETREGS */ - { 0, 0, 0, -1, -1, NULL, NULL } + NULL_REGSET }; static CORE_ADDR @@ -515,7 +502,7 @@ x86_set_pc (struct regcache *regcache, CORE_ADDR pc) } } -static const unsigned char x86_breakpoint[] = { 0xCC }; +static const gdb_byte x86_breakpoint[] = { 0xCC }; #define x86_breakpoint_len 1 static int @@ -530,138 +517,14 @@ x86_breakpoint_at (CORE_ADDR pc) return 0; } - -/* Return the offset of REGNUM in the u_debugreg field of struct - user. */ - -static int -u_debugreg_offset (int regnum) -{ - return (offsetof (struct user, u_debugreg) - + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); -} - - -/* 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, u_debugreg_offset (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, u_debugreg_offset (regnum), value); - if (errno != 0) - error ("Couldn't write debug register"); -} - -static int -update_debug_registers_callback (struct inferior_list_entry *entry, - void *pid_p) -{ - struct thread_info *thr = (struct thread_info *) entry; - struct lwp_info *lwp = get_thread_lwp (thr); - int pid = *(int *) pid_p; - - /* Only update the threads of this process. */ - if (pid_of (thr) == pid) - { - /* The actual update is done later just before resuming the lwp, - we just mark that the registers need updating. */ - lwp->arch_private->debug_registers_changed = 1; - - /* If the lwp isn't stopped, force it to momentarily pause, so - we can update its debug registers. */ - if (!lwp->stopped) - linux_stop_lwp (lwp); - } - - return 0; -} - -/* Update the inferior's debug register REGNUM from STATE. */ - -static void -x86_dr_low_set_addr (int regnum, CORE_ADDR addr) -{ - /* Only update the threads of this process. */ - int pid = pid_of (current_thread); - - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - find_inferior (&all_threads, update_debug_registers_callback, &pid); -} - -/* Return the inferior's debug register REGNUM. */ - -static CORE_ADDR -x86_dr_low_get_addr (int regnum) -{ - ptid_t ptid = ptid_of (current_thread); - - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - return x86_linux_dr_get (ptid, regnum); -} - -/* Update the inferior's DR7 debug control register from STATE. */ - -static void -x86_dr_low_set_control (unsigned long control) -{ - /* Only update the threads of this process. */ - int pid = pid_of (current_thread); - - find_inferior (&all_threads, update_debug_registers_callback, &pid); -} - -/* Return the inferior's DR7 debug control register. */ - -static unsigned long -x86_dr_low_get_control (void) -{ - ptid_t ptid = ptid_of (current_thread); - - return x86_linux_dr_get (ptid, DR_CONTROL); -} - -/* Get the value of the DR6 debug status register from the inferior - and record it in STATE. */ - -static unsigned long -x86_dr_low_get_status (void) -{ - ptid_t ptid = ptid_of (current_thread); - - return x86_linux_dr_get (ptid, DR_STATUS); -} - /* Low-level function vector. */ struct x86_dr_low_type x86_dr_low = { - x86_dr_low_set_control, - x86_dr_low_set_addr, - x86_dr_low_get_addr, - x86_dr_low_get_status, - x86_dr_low_get_control, + x86_linux_dr_set_control, + x86_linux_dr_set_addr, + x86_linux_dr_get_addr, + x86_linux_dr_get_status, + x86_linux_dr_get_control, sizeof (void *), }; @@ -690,9 +553,6 @@ x86_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, switch (type) { - case raw_bkpt_type_sw: - return insert_memory_breakpoint (bp); - case raw_bkpt_type_hw: case raw_bkpt_type_write_wp: case raw_bkpt_type_access_wp: @@ -719,9 +579,6 @@ x86_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, switch (type) { - case raw_bkpt_type_sw: - return remove_memory_breakpoint (bp); - case raw_bkpt_type_hw: case raw_bkpt_type_write_wp: case raw_bkpt_type_access_wp: @@ -769,57 +626,42 @@ x86_linux_new_process (void) return info; } -/* Called when a new thread is detected. */ - -static struct arch_lwp_info * -x86_linux_new_thread (void) -{ - struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); - - info->debug_registers_changed = 1; - - return info; -} - -/* Called when resuming a thread. - If the debug regs have changed, update the thread's copies. */ +/* Target routine for linux_new_fork. */ static void -x86_linux_prepare_to_resume (struct lwp_info *lwp) +x86_linux_new_fork (struct process_info *parent, struct process_info *child) { - ptid_t ptid = ptid_of (get_lwp_thread (lwp)); - int clear_status = 0; + /* These are allocated by linux_add_process. */ + gdb_assert (parent->priv != NULL + && parent->priv->arch_private != NULL); + gdb_assert (child->priv != NULL + && child->priv->arch_private != NULL); - if (lwp->arch_private->debug_registers_changed) - { - int i; - int pid = ptid_get_pid (ptid); - struct process_info *proc = find_process_pid (pid); - struct x86_debug_reg_state *state - = &proc->priv->arch_private->debug_reg_state; - - x86_linux_dr_set (ptid, DR_CONTROL, 0); + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. - ALL_DEBUG_ADDRESS_REGISTERS (i) - if (state->dr_ref_count[i] > 0) - { - x86_linux_dr_set (ptid, i, state->dr_mirror[i]); + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ - /* If we're setting a watchpoint, any change the inferior - had done itself to the debug registers needs to be - discarded, otherwise, x86_dr_stopped_data_address can - get confused. */ - clear_status = 1; - } + *child->priv->arch_private = *parent->priv->arch_private; +} - if (state->dr_control_mirror != 0) - x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); +/* See nat/x86-dregs.h. */ - lwp->arch_private->debug_registers_changed = 0; - } +struct x86_debug_reg_state * +x86_debug_reg_state (pid_t pid) +{ + struct process_info *proc = find_process_pid (pid); - if (clear_status || lwp->stop_reason == LWP_STOPPED_BY_WATCHPOINT) - x86_linux_dr_set (ptid, DR_STATUS, 0); + return &proc->priv->arch_private->debug_reg_state; } /* When GDBSERVER is built as a 64-bit application on linux, the @@ -1300,9 +1142,6 @@ int have_ptrace_getfpxregs = #endif ; -/* Does the current host support PTRACE_GETREGSET? */ -static int have_ptrace_getregset = -1; - /* Get Linux/x86 target description from running target. */ static const struct target_desc * @@ -1517,29 +1356,35 @@ x86_linux_update_xmltarget (void) PTRACE_GETREGSET. */ static void -x86_linux_process_qsupported (const char *query) +x86_linux_process_qsupported (char **features, int count) { + int i; + /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters=" with "i386" in qSupported query, it supports x86 XML target descriptions. */ use_xml = 0; - if (query != NULL && strncmp (query, "xmlRegisters=", 13) == 0) + for (i = 0; i < count; i++) { - char *copy = xstrdup (query + 13); - char *p; + const char *feature = features[i]; - for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ",")) + if (startswith (feature, "xmlRegisters=")) { - if (strcmp (p, "i386") == 0) + char *copy = xstrdup (feature + 13); + char *p; + + for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ",")) { - use_xml = 1; - break; + if (strcmp (p, "i386") == 0) + { + use_xml = 1; + break; + } } - } - free (copy); + free (copy); + } } - x86_linux_update_xmltarget (); } @@ -1593,6 +1438,31 @@ x86_arch_setup (void) current_process ()->tdesc = x86_linux_read_description (); } +/* Fill *SYSNO and *SYSRET with the syscall nr trapped and the syscall return + code. This should only be called if LWP got a SYSCALL_SIGTRAP. */ + +static void +x86_get_syscall_trapinfo (struct regcache *regcache, int *sysno, int *sysret) +{ + int use_64bit = register_size (regcache->tdesc, 0) == 8; + + if (use_64bit) + { + long l_sysno; + long l_sysret; + + collect_register_by_name (regcache, "orig_rax", &l_sysno); + collect_register_by_name (regcache, "rax", &l_sysret); + *sysno = (int) l_sysno; + *sysret = (int) l_sysret; + } + else + { + collect_register_by_name (regcache, "orig_eax", sysno); + collect_register_by_name (regcache, "eax", sysret); + } +} + static int x86_supports_tracepoints (void) { @@ -3404,12 +3274,30 @@ x86_emit_ops (void) return &i386_emit_ops; } +/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */ + +static const gdb_byte * +x86_sw_breakpoint_from_kind (int kind, int *size) +{ + *size = x86_breakpoint_len; + return x86_breakpoint; +} + static int x86_supports_range_stepping (void) { return 1; } +/* Implementation of linux_target_ops method "supports_hardware_single_step". + */ + +static int +x86_supports_hardware_single_step (void) +{ + return 1; +} + /* This is initialized assuming an amd64 target. x86_arch_setup will correct it for i386 or amd64 targets. */ @@ -3422,8 +3310,8 @@ struct linux_target_ops the_low_target = NULL, /* fetch_register */ x86_get_pc, x86_set_pc, - x86_breakpoint, - x86_breakpoint_len, + NULL, /* breakpoint_kind_from_pc */ + x86_sw_breakpoint_from_kind, NULL, 1, x86_breakpoint_at, @@ -3441,6 +3329,7 @@ struct linux_target_ops the_low_target = x86_siginfo_fixup, x86_linux_new_process, x86_linux_new_thread, + x86_linux_new_fork, x86_linux_prepare_to_resume, x86_linux_process_qsupported, x86_supports_tracepoints, @@ -3449,6 +3338,9 @@ struct linux_target_ops the_low_target = x86_emit_ops, x86_get_min_fast_tracepoint_insn_len, x86_supports_range_stepping, + NULL, /* breakpoint_kind_from_current_state */ + x86_supports_hardware_single_step, + x86_get_syscall_trapinfo, }; void @@ -3465,7 +3357,7 @@ initialize_low_arch (void) init_registers_x32_avx_linux (); init_registers_x32_avx512_linux (); - tdesc_amd64_linux_no_xml = xmalloc (sizeof (struct target_desc)); + tdesc_amd64_linux_no_xml = XNEW (struct target_desc); copy_target_description (tdesc_amd64_linux_no_xml, tdesc_amd64_linux); tdesc_amd64_linux_no_xml->xmltarget = xmltarget_amd64_linux_no_xml; #endif @@ -3475,7 +3367,7 @@ initialize_low_arch (void) init_registers_i386_avx512_linux (); init_registers_i386_mpx_linux (); - tdesc_i386_linux_no_xml = xmalloc (sizeof (struct target_desc)); + tdesc_i386_linux_no_xml = XNEW (struct target_desc); copy_target_description (tdesc_i386_linux_no_xml, tdesc_i386_linux); tdesc_i386_linux_no_xml->xmltarget = xmltarget_i386_linux_no_xml;