| 1 | /* Low-level debug register code for GNU/Linux x86 (i386 and x86-64). |
| 2 | |
| 3 | Copyright (C) 1999-2015 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of GDB. |
| 6 | |
| 7 | This program is free software; you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation; either version 3 of the License, or |
| 10 | (at your option) any later version. |
| 11 | |
| 12 | This program is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | GNU General Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License |
| 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 19 | |
| 20 | #include "common-defs.h" |
| 21 | #include "nat/gdb_ptrace.h" |
| 22 | #include <sys/user.h> |
| 23 | #include "target/waitstatus.h" |
| 24 | #include "nat/x86-linux.h" |
| 25 | #include "nat/x86-dregs.h" |
| 26 | #include "nat/x86-linux-dregs.h" |
| 27 | |
| 28 | /* Return the offset of REGNUM in the u_debugreg field of struct |
| 29 | user. */ |
| 30 | |
| 31 | static int |
| 32 | u_debugreg_offset (int regnum) |
| 33 | { |
| 34 | return (offsetof (struct user, u_debugreg) |
| 35 | + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); |
| 36 | } |
| 37 | |
| 38 | /* Get debug register REGNUM value from the LWP specified by PTID. */ |
| 39 | |
| 40 | static unsigned long |
| 41 | x86_linux_dr_get (ptid_t ptid, int regnum) |
| 42 | { |
| 43 | int tid; |
| 44 | unsigned long value; |
| 45 | |
| 46 | gdb_assert (ptid_lwp_p (ptid)); |
| 47 | tid = ptid_get_lwp (ptid); |
| 48 | |
| 49 | errno = 0; |
| 50 | value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0); |
| 51 | if (errno != 0) |
| 52 | perror_with_name (_("Couldn't read debug register")); |
| 53 | |
| 54 | return value; |
| 55 | } |
| 56 | |
| 57 | /* Set debug register REGNUM to VALUE in the LWP specified by PTID. */ |
| 58 | |
| 59 | static void |
| 60 | x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) |
| 61 | { |
| 62 | int tid; |
| 63 | |
| 64 | gdb_assert (ptid_lwp_p (ptid)); |
| 65 | tid = ptid_get_lwp (ptid); |
| 66 | |
| 67 | errno = 0; |
| 68 | ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value); |
| 69 | if (errno != 0) |
| 70 | perror_with_name (_("Couldn't write debug register")); |
| 71 | } |
| 72 | |
| 73 | /* Callback for iterate_over_lwps. Mark that our local mirror of |
| 74 | LWP's debug registers has been changed, and cause LWP to stop if |
| 75 | it isn't already. Values are written from our local mirror to |
| 76 | the actual debug registers immediately prior to LWP resuming. */ |
| 77 | |
| 78 | static int |
| 79 | update_debug_registers_callback (struct lwp_info *lwp, void *arg) |
| 80 | { |
| 81 | lwp_set_debug_registers_changed (lwp, 1); |
| 82 | |
| 83 | if (!lwp_is_stopped (lwp)) |
| 84 | linux_stop_lwp (lwp); |
| 85 | |
| 86 | /* Continue the iteration. */ |
| 87 | return 0; |
| 88 | } |
| 89 | |
| 90 | /* See nat/x86-linux-dregs.h. */ |
| 91 | |
| 92 | CORE_ADDR |
| 93 | x86_linux_dr_get_addr (int regnum) |
| 94 | { |
| 95 | gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); |
| 96 | |
| 97 | return x86_linux_dr_get (current_lwp_ptid (), regnum); |
| 98 | } |
| 99 | |
| 100 | /* See nat/x86-linux-dregs.h. */ |
| 101 | |
| 102 | void |
| 103 | x86_linux_dr_set_addr (int regnum, CORE_ADDR addr) |
| 104 | { |
| 105 | ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); |
| 106 | |
| 107 | gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); |
| 108 | |
| 109 | iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); |
| 110 | } |
| 111 | |
| 112 | /* See nat/x86-linux-dregs.h. */ |
| 113 | |
| 114 | unsigned long |
| 115 | x86_linux_dr_get_control (void) |
| 116 | { |
| 117 | return x86_linux_dr_get (current_lwp_ptid (), DR_CONTROL); |
| 118 | } |
| 119 | |
| 120 | /* See nat/x86-linux-dregs.h. */ |
| 121 | |
| 122 | void |
| 123 | x86_linux_dr_set_control (unsigned long control) |
| 124 | { |
| 125 | ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); |
| 126 | |
| 127 | iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); |
| 128 | } |
| 129 | |
| 130 | /* See nat/x86-linux-dregs.h. */ |
| 131 | |
| 132 | unsigned long |
| 133 | x86_linux_dr_get_status (void) |
| 134 | { |
| 135 | return x86_linux_dr_get (current_lwp_ptid (), DR_STATUS); |
| 136 | } |
| 137 | |
| 138 | /* See nat/x86-linux-dregs.h. */ |
| 139 | |
| 140 | void |
| 141 | x86_linux_update_debug_registers (struct lwp_info *lwp) |
| 142 | { |
| 143 | ptid_t ptid = ptid_of_lwp (lwp); |
| 144 | int clear_status = 0; |
| 145 | |
| 146 | gdb_assert (lwp_is_stopped (lwp)); |
| 147 | |
| 148 | if (lwp_debug_registers_changed (lwp)) |
| 149 | { |
| 150 | struct x86_debug_reg_state *state |
| 151 | = x86_debug_reg_state (ptid_get_pid (ptid)); |
| 152 | int i; |
| 153 | |
| 154 | /* Prior to Linux kernel 2.6.33 commit |
| 155 | 72f674d203cd230426437cdcf7dd6f681dad8b0d, setting DR0-3 to |
| 156 | a value that did not match what was enabled in DR_CONTROL |
| 157 | resulted in EINVAL. To avoid this we zero DR_CONTROL before |
| 158 | writing address registers, only writing DR_CONTROL's actual |
| 159 | value once all the addresses are in place. */ |
| 160 | x86_linux_dr_set (ptid, DR_CONTROL, 0); |
| 161 | |
| 162 | ALL_DEBUG_ADDRESS_REGISTERS (i) |
| 163 | if (state->dr_ref_count[i] > 0) |
| 164 | { |
| 165 | x86_linux_dr_set (ptid, i, state->dr_mirror[i]); |
| 166 | |
| 167 | /* If we're setting a watchpoint, any change the inferior |
| 168 | has made to its debug registers needs to be discarded |
| 169 | to avoid x86_stopped_data_address getting confused. */ |
| 170 | clear_status = 1; |
| 171 | } |
| 172 | |
| 173 | /* If DR_CONTROL is supposed to be zero then it's already set. */ |
| 174 | if (state->dr_control_mirror != 0) |
| 175 | x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); |
| 176 | |
| 177 | lwp_set_debug_registers_changed (lwp, 0); |
| 178 | } |
| 179 | |
| 180 | if (clear_status |
| 181 | || lwp_stop_reason (lwp) == TARGET_STOPPED_BY_WATCHPOINT) |
| 182 | x86_linux_dr_set (ptid, DR_STATUS, 0); |
| 183 | } |