Commit | Line | Data |
---|---|---|
611cb4a5 | 1 | /* Memory breakpoint operations for the remote server for GDB. |
4c38e0a4 | 2 | Copyright (C) 2002, 2003, 2005, 2007, 2008, 2009, 2010 |
0fb0cc75 | 3 | Free Software Foundation, Inc. |
611cb4a5 DJ |
4 | |
5 | Contributed by MontaVista Software. | |
6 | ||
7 | This file is part of GDB. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
a9762ec7 | 11 | the Free Software Foundation; either version 3 of the License, or |
611cb4a5 DJ |
12 | (at your option) any later version. |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
a9762ec7 | 20 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
611cb4a5 DJ |
21 | |
22 | #include "server.h" | |
23 | ||
f450004a | 24 | const unsigned char *breakpoint_data; |
611cb4a5 DJ |
25 | int breakpoint_len; |
26 | ||
27 | #define MAX_BREAKPOINT_LEN 8 | |
28 | ||
29 | struct breakpoint | |
30 | { | |
31 | struct breakpoint *next; | |
32 | CORE_ADDR pc; | |
33 | unsigned char old_data[MAX_BREAKPOINT_LEN]; | |
34 | ||
d50171e4 PA |
35 | /* Non-zero if this breakpoint is currently inserted in the |
36 | inferior. */ | |
37 | int inserted; | |
611cb4a5 | 38 | |
b65d95c5 | 39 | /* Function to call when we hit this breakpoint. If it returns 1, |
d50171e4 | 40 | the breakpoint shall be deleted; 0, it will be left inserted. */ |
b65d95c5 | 41 | int (*handler) (CORE_ADDR); |
611cb4a5 DJ |
42 | }; |
43 | ||
d50171e4 PA |
44 | static struct breakpoint * |
45 | set_raw_breakpoint_at (CORE_ADDR where) | |
611cb4a5 | 46 | { |
95954743 | 47 | struct process_info *proc = current_process (); |
611cb4a5 | 48 | struct breakpoint *bp; |
d50171e4 | 49 | int err; |
611cb4a5 DJ |
50 | |
51 | if (breakpoint_data == NULL) | |
52 | error ("Target does not support breakpoints."); | |
53 | ||
d50171e4 PA |
54 | bp = xcalloc (1, sizeof (*bp)); |
55 | bp->pc = where; | |
611cb4a5 | 56 | |
d50171e4 PA |
57 | err = (*the_target->read_memory) (where, bp->old_data, |
58 | breakpoint_len); | |
59 | if (err != 0) | |
60 | { | |
61 | if (debug_threads) | |
62 | fprintf (stderr, | |
63 | "Failed to read shadow memory of" | |
64 | " breakpoint at 0x%s (%s).\n", | |
65 | paddress (where), strerror (err)); | |
66 | free (bp); | |
67 | return NULL; | |
68 | } | |
611cb4a5 | 69 | |
d50171e4 PA |
70 | err = (*the_target->write_memory) (where, breakpoint_data, |
71 | breakpoint_len); | |
72 | if (err != 0) | |
73 | { | |
74 | if (debug_threads) | |
75 | fprintf (stderr, | |
76 | "Failed to insert breakpoint at 0x%s (%s).\n", | |
77 | paddress (where), strerror (err)); | |
78 | free (bp); | |
79 | return NULL; | |
80 | } | |
81 | ||
82 | /* Link the breakpoint in. */ | |
83 | bp->inserted = 1; | |
84 | bp->next = proc->breakpoints; | |
85 | proc->breakpoints = bp; | |
86 | return bp; | |
87 | } | |
88 | ||
89 | void | |
90 | set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) | |
91 | { | |
92 | struct process_info *proc = current_process (); | |
93 | struct breakpoint *bp; | |
94 | ||
95 | bp = set_raw_breakpoint_at (where); | |
96 | ||
97 | if (bp == NULL) | |
98 | { | |
99 | /* warn? */ | |
100 | return; | |
101 | } | |
102 | ||
103 | bp = xcalloc (1, sizeof (struct breakpoint)); | |
611cb4a5 DJ |
104 | bp->handler = handler; |
105 | ||
95954743 PA |
106 | bp->next = proc->breakpoints; |
107 | proc->breakpoints = bp; | |
611cb4a5 DJ |
108 | } |
109 | ||
110 | static void | |
111 | delete_breakpoint (struct breakpoint *bp) | |
112 | { | |
95954743 | 113 | struct process_info *proc = current_process (); |
611cb4a5 DJ |
114 | struct breakpoint *cur; |
115 | ||
95954743 | 116 | if (proc->breakpoints == bp) |
611cb4a5 | 117 | { |
95954743 | 118 | proc->breakpoints = bp->next; |
611cb4a5 DJ |
119 | (*the_target->write_memory) (bp->pc, bp->old_data, |
120 | breakpoint_len); | |
121 | free (bp); | |
122 | return; | |
123 | } | |
95954743 | 124 | cur = proc->breakpoints; |
611cb4a5 DJ |
125 | while (cur->next) |
126 | { | |
127 | if (cur->next == bp) | |
128 | { | |
129 | cur->next = bp->next; | |
130 | (*the_target->write_memory) (bp->pc, bp->old_data, | |
131 | breakpoint_len); | |
132 | free (bp); | |
133 | return; | |
134 | } | |
135 | } | |
136 | warning ("Could not find breakpoint in list."); | |
137 | } | |
138 | ||
139 | static struct breakpoint * | |
140 | find_breakpoint_at (CORE_ADDR where) | |
141 | { | |
95954743 PA |
142 | struct process_info *proc = current_process (); |
143 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
144 | |
145 | while (bp != NULL) | |
146 | { | |
147 | if (bp->pc == where) | |
148 | return bp; | |
149 | bp = bp->next; | |
150 | } | |
151 | ||
152 | return NULL; | |
153 | } | |
154 | ||
68070c10 PA |
155 | void |
156 | delete_breakpoint_at (CORE_ADDR addr) | |
157 | { | |
158 | struct breakpoint *bp = find_breakpoint_at (addr); | |
159 | if (bp != NULL) | |
160 | delete_breakpoint (bp); | |
161 | } | |
162 | ||
d50171e4 PA |
163 | void |
164 | set_reinsert_breakpoint (CORE_ADDR stop_at) | |
611cb4a5 | 165 | { |
d50171e4 | 166 | set_breakpoint_at (stop_at, NULL); |
611cb4a5 DJ |
167 | } |
168 | ||
169 | void | |
d50171e4 | 170 | delete_reinsert_breakpoints (void) |
611cb4a5 | 171 | { |
d50171e4 PA |
172 | struct process_info *proc = current_process (); |
173 | struct breakpoint *bp, **bp_link; | |
611cb4a5 | 174 | |
d50171e4 PA |
175 | bp = proc->breakpoints; |
176 | bp_link = &proc->breakpoints; | |
611cb4a5 | 177 | |
d50171e4 PA |
178 | while (bp) |
179 | { | |
180 | *bp_link = bp->next; | |
181 | delete_breakpoint (bp); | |
182 | bp = *bp_link; | |
183 | } | |
184 | } | |
b65d95c5 | 185 | |
d50171e4 PA |
186 | static void |
187 | uninsert_breakpoint (struct breakpoint *bp) | |
188 | { | |
189 | if (bp->inserted) | |
190 | { | |
191 | int err; | |
192 | ||
193 | bp->inserted = 0; | |
194 | err = (*the_target->write_memory) (bp->pc, bp->old_data, | |
195 | breakpoint_len); | |
196 | if (err != 0) | |
197 | { | |
198 | bp->inserted = 1; | |
611cb4a5 | 199 | |
d50171e4 PA |
200 | if (debug_threads) |
201 | fprintf (stderr, | |
202 | "Failed to uninsert raw breakpoint at 0x%s (%s).\n", | |
203 | paddress (bp->pc), strerror (err)); | |
204 | } | |
205 | } | |
611cb4a5 DJ |
206 | } |
207 | ||
208 | void | |
d50171e4 | 209 | uninsert_breakpoints_at (CORE_ADDR pc) |
611cb4a5 DJ |
210 | { |
211 | struct breakpoint *bp; | |
212 | ||
d50171e4 | 213 | bp = find_breakpoint_at (pc); |
611cb4a5 | 214 | if (bp == NULL) |
d50171e4 PA |
215 | { |
216 | /* This can happen when we remove all breakpoints while handling | |
217 | a step-over. */ | |
218 | if (debug_threads) | |
219 | fprintf (stderr, | |
220 | "Could not find breakpoint at 0x%s " | |
221 | "in list (uninserting).\n", | |
222 | paddress (pc)); | |
223 | return; | |
224 | } | |
611cb4a5 | 225 | |
d50171e4 PA |
226 | if (bp->inserted) |
227 | uninsert_breakpoint (bp); | |
611cb4a5 DJ |
228 | } |
229 | ||
d50171e4 PA |
230 | static void |
231 | reinsert_breakpoint (struct breakpoint *bp) | |
611cb4a5 | 232 | { |
d50171e4 | 233 | int err; |
611cb4a5 | 234 | |
d50171e4 | 235 | if (bp->inserted) |
611cb4a5 DJ |
236 | error ("Breakpoint already inserted at reinsert time."); |
237 | ||
d50171e4 PA |
238 | err = (*the_target->write_memory) (bp->pc, breakpoint_data, |
239 | breakpoint_len); | |
240 | if (err == 0) | |
241 | bp->inserted = 1; | |
242 | else if (debug_threads) | |
243 | fprintf (stderr, | |
244 | "Failed to reinsert breakpoint at 0x%s (%s).\n", | |
245 | paddress (bp->pc), strerror (err)); | |
611cb4a5 DJ |
246 | } |
247 | ||
d50171e4 PA |
248 | void |
249 | reinsert_breakpoints_at (CORE_ADDR pc) | |
611cb4a5 DJ |
250 | { |
251 | struct breakpoint *bp; | |
252 | ||
d50171e4 | 253 | bp = find_breakpoint_at (pc); |
611cb4a5 | 254 | if (bp == NULL) |
611cb4a5 | 255 | { |
d50171e4 PA |
256 | /* This can happen when we remove all breakpoints while handling |
257 | a step-over. */ | |
258 | if (debug_threads) | |
259 | fprintf (stderr, | |
260 | "Could not find breakpoint at 0x%s " | |
261 | "in list (reinserting).\n", | |
262 | paddress (pc)); | |
263 | return; | |
611cb4a5 DJ |
264 | } |
265 | ||
d50171e4 PA |
266 | reinsert_breakpoint (bp); |
267 | } | |
268 | ||
269 | void | |
270 | check_breakpoints (CORE_ADDR stop_pc) | |
271 | { | |
272 | struct process_info *proc = current_process (); | |
273 | struct breakpoint *bp, **bp_link; | |
274 | ||
275 | bp = proc->breakpoints; | |
276 | bp_link = &proc->breakpoints; | |
277 | ||
278 | while (bp) | |
b65d95c5 | 279 | { |
d50171e4 PA |
280 | if (bp->pc == stop_pc) |
281 | { | |
282 | if (!bp->inserted) | |
283 | { | |
284 | warning ("Hit a removed breakpoint?"); | |
285 | return; | |
286 | } | |
287 | ||
288 | if (bp->handler != NULL && (*bp->handler) (stop_pc)) | |
289 | { | |
290 | *bp_link = bp->next; | |
291 | ||
292 | delete_breakpoint (bp); | |
293 | ||
294 | bp = *bp_link; | |
295 | continue; | |
296 | } | |
297 | } | |
298 | ||
299 | bp_link = &bp->next; | |
300 | bp = *bp_link; | |
b65d95c5 | 301 | } |
611cb4a5 DJ |
302 | } |
303 | ||
304 | void | |
f450004a | 305 | set_breakpoint_data (const unsigned char *bp_data, int bp_len) |
611cb4a5 DJ |
306 | { |
307 | breakpoint_data = bp_data; | |
308 | breakpoint_len = bp_len; | |
309 | } | |
310 | ||
d50171e4 PA |
311 | int |
312 | breakpoint_here (CORE_ADDR addr) | |
313 | { | |
314 | struct process_info *proc = current_process (); | |
315 | struct breakpoint *bp; | |
316 | ||
317 | for (bp = proc->breakpoints; bp != NULL; bp = bp->next) | |
318 | if (bp->pc == addr) | |
319 | return 1; | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
324 | int | |
325 | breakpoint_inserted_here (CORE_ADDR addr) | |
326 | { | |
327 | struct process_info *proc = current_process (); | |
328 | struct breakpoint *bp; | |
329 | ||
330 | for (bp = proc->breakpoints; bp != NULL; bp = bp->next) | |
331 | if (bp->pc == addr && bp->inserted) | |
332 | return 1; | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
611cb4a5 | 337 | void |
f450004a | 338 | check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) |
611cb4a5 | 339 | { |
95954743 PA |
340 | struct process_info *proc = current_process (); |
341 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
342 | CORE_ADDR mem_end = mem_addr + mem_len; |
343 | ||
344 | for (; bp != NULL; bp = bp->next) | |
345 | { | |
346 | CORE_ADDR bp_end = bp->pc + breakpoint_len; | |
347 | CORE_ADDR start, end; | |
348 | int copy_offset, copy_len, buf_offset; | |
349 | ||
350 | if (mem_addr >= bp_end) | |
351 | continue; | |
352 | if (bp->pc >= mem_end) | |
353 | continue; | |
354 | ||
355 | start = bp->pc; | |
356 | if (mem_addr > start) | |
357 | start = mem_addr; | |
358 | ||
359 | end = bp_end; | |
360 | if (end > mem_end) | |
361 | end = mem_end; | |
362 | ||
363 | copy_len = end - start; | |
364 | copy_offset = start - bp->pc; | |
365 | buf_offset = start - mem_addr; | |
366 | ||
367 | memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); | |
368 | } | |
369 | } | |
370 | ||
371 | void | |
f450004a | 372 | check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) |
611cb4a5 | 373 | { |
95954743 PA |
374 | struct process_info *proc = current_process (); |
375 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
376 | CORE_ADDR mem_end = mem_addr + mem_len; |
377 | ||
378 | for (; bp != NULL; bp = bp->next) | |
379 | { | |
380 | CORE_ADDR bp_end = bp->pc + breakpoint_len; | |
381 | CORE_ADDR start, end; | |
382 | int copy_offset, copy_len, buf_offset; | |
383 | ||
384 | if (mem_addr >= bp_end) | |
385 | continue; | |
386 | if (bp->pc >= mem_end) | |
387 | continue; | |
388 | ||
389 | start = bp->pc; | |
390 | if (mem_addr > start) | |
391 | start = mem_addr; | |
392 | ||
393 | end = bp_end; | |
394 | if (end > mem_end) | |
395 | end = mem_end; | |
396 | ||
397 | copy_len = end - start; | |
398 | copy_offset = start - bp->pc; | |
399 | buf_offset = start - mem_addr; | |
400 | ||
401 | memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len); | |
d50171e4 | 402 | if (bp->inserted) |
611cb4a5 DJ |
403 | memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len); |
404 | } | |
405 | } | |
ae13219e | 406 | |
95954743 | 407 | /* Delete all breakpoints, and un-insert them from the inferior. */ |
ae13219e DJ |
408 | |
409 | void | |
410 | delete_all_breakpoints (void) | |
411 | { | |
95954743 PA |
412 | struct process_info *proc = current_process (); |
413 | ||
414 | while (proc->breakpoints) | |
415 | delete_breakpoint (proc->breakpoints); | |
416 | } | |
417 | ||
418 | /* Release all breakpoints, but do not try to un-insert them from the | |
419 | inferior. */ | |
420 | ||
421 | void | |
422 | free_all_breakpoints (struct process_info *proc) | |
423 | { | |
424 | struct breakpoint *bp; | |
425 | ||
426 | while (proc->breakpoints) | |
427 | { | |
428 | bp = proc->breakpoints; | |
429 | proc->breakpoints = bp->next; | |
430 | free (bp); | |
431 | } | |
ae13219e | 432 | } |