Commit | Line | Data |
---|---|---|
8e5d4070 GB |
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" | |
5826e159 | 21 | #include "nat/gdb_ptrace.h" |
8e5d4070 GB |
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 | } |