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 | ||
414a389f PA |
29 | /* The type of a breakpoint. */ |
30 | enum bkpt_type | |
31 | { | |
32 | /* A basic-software-single-step breakpoint. */ | |
33 | reinsert_breakpoint, | |
34 | ||
35 | /* Any other breakpoint type that doesn't require specific | |
36 | treatment goes here. E.g., an event breakpoint. */ | |
37 | other_breakpoint, | |
38 | }; | |
39 | ||
611cb4a5 DJ |
40 | struct breakpoint |
41 | { | |
42 | struct breakpoint *next; | |
43 | CORE_ADDR pc; | |
44 | unsigned char old_data[MAX_BREAKPOINT_LEN]; | |
45 | ||
d50171e4 PA |
46 | /* Non-zero if this breakpoint is currently inserted in the |
47 | inferior. */ | |
48 | int inserted; | |
611cb4a5 | 49 | |
414a389f PA |
50 | /* The breakpoint's type. */ |
51 | enum bkpt_type type; | |
52 | ||
b65d95c5 | 53 | /* Function to call when we hit this breakpoint. If it returns 1, |
d50171e4 | 54 | the breakpoint shall be deleted; 0, it will be left inserted. */ |
b65d95c5 | 55 | int (*handler) (CORE_ADDR); |
611cb4a5 DJ |
56 | }; |
57 | ||
414a389f PA |
58 | static void uninsert_breakpoint (struct breakpoint *bp); |
59 | ||
d50171e4 PA |
60 | static struct breakpoint * |
61 | set_raw_breakpoint_at (CORE_ADDR where) | |
611cb4a5 | 62 | { |
95954743 | 63 | struct process_info *proc = current_process (); |
611cb4a5 | 64 | struct breakpoint *bp; |
d50171e4 | 65 | int err; |
611cb4a5 DJ |
66 | |
67 | if (breakpoint_data == NULL) | |
68 | error ("Target does not support breakpoints."); | |
69 | ||
d50171e4 PA |
70 | bp = xcalloc (1, sizeof (*bp)); |
71 | bp->pc = where; | |
611cb4a5 | 72 | |
d50171e4 PA |
73 | err = (*the_target->read_memory) (where, bp->old_data, |
74 | breakpoint_len); | |
75 | if (err != 0) | |
76 | { | |
77 | if (debug_threads) | |
78 | fprintf (stderr, | |
79 | "Failed to read shadow memory of" | |
80 | " breakpoint at 0x%s (%s).\n", | |
81 | paddress (where), strerror (err)); | |
82 | free (bp); | |
83 | return NULL; | |
84 | } | |
611cb4a5 | 85 | |
d50171e4 PA |
86 | err = (*the_target->write_memory) (where, breakpoint_data, |
87 | breakpoint_len); | |
88 | if (err != 0) | |
89 | { | |
90 | if (debug_threads) | |
91 | fprintf (stderr, | |
92 | "Failed to insert breakpoint at 0x%s (%s).\n", | |
93 | paddress (where), strerror (err)); | |
94 | free (bp); | |
95 | return NULL; | |
96 | } | |
97 | ||
98 | /* Link the breakpoint in. */ | |
99 | bp->inserted = 1; | |
100 | bp->next = proc->breakpoints; | |
101 | proc->breakpoints = bp; | |
102 | return bp; | |
103 | } | |
104 | ||
414a389f | 105 | struct breakpoint * |
d50171e4 PA |
106 | set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) |
107 | { | |
108 | struct process_info *proc = current_process (); | |
109 | struct breakpoint *bp; | |
110 | ||
111 | bp = set_raw_breakpoint_at (where); | |
112 | ||
113 | if (bp == NULL) | |
114 | { | |
115 | /* warn? */ | |
414a389f | 116 | return NULL; |
d50171e4 PA |
117 | } |
118 | ||
119 | bp = xcalloc (1, sizeof (struct breakpoint)); | |
414a389f | 120 | bp->type = other_breakpoint; |
611cb4a5 DJ |
121 | bp->handler = handler; |
122 | ||
95954743 PA |
123 | bp->next = proc->breakpoints; |
124 | proc->breakpoints = bp; | |
414a389f PA |
125 | |
126 | return bp; | |
611cb4a5 DJ |
127 | } |
128 | ||
129 | static void | |
414a389f | 130 | delete_breakpoint (struct breakpoint *todel) |
611cb4a5 | 131 | { |
95954743 | 132 | struct process_info *proc = current_process (); |
414a389f | 133 | struct breakpoint *bp, **bp_link; |
611cb4a5 | 134 | |
414a389f PA |
135 | bp = proc->breakpoints; |
136 | bp_link = &proc->breakpoints; | |
137 | ||
138 | while (bp) | |
611cb4a5 | 139 | { |
414a389f | 140 | if (bp == todel) |
611cb4a5 | 141 | { |
414a389f PA |
142 | *bp_link = bp->next; |
143 | ||
144 | uninsert_breakpoint (bp); | |
611cb4a5 DJ |
145 | free (bp); |
146 | return; | |
147 | } | |
414a389f PA |
148 | else |
149 | { | |
150 | bp_link = &bp->next; | |
151 | bp = *bp_link; | |
152 | } | |
611cb4a5 | 153 | } |
414a389f | 154 | |
611cb4a5 DJ |
155 | warning ("Could not find breakpoint in list."); |
156 | } | |
157 | ||
158 | static struct breakpoint * | |
159 | find_breakpoint_at (CORE_ADDR where) | |
160 | { | |
95954743 PA |
161 | struct process_info *proc = current_process (); |
162 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
163 | |
164 | while (bp != NULL) | |
165 | { | |
166 | if (bp->pc == where) | |
167 | return bp; | |
168 | bp = bp->next; | |
169 | } | |
170 | ||
171 | return NULL; | |
172 | } | |
173 | ||
68070c10 PA |
174 | void |
175 | delete_breakpoint_at (CORE_ADDR addr) | |
176 | { | |
177 | struct breakpoint *bp = find_breakpoint_at (addr); | |
178 | if (bp != NULL) | |
179 | delete_breakpoint (bp); | |
180 | } | |
181 | ||
d50171e4 PA |
182 | void |
183 | set_reinsert_breakpoint (CORE_ADDR stop_at) | |
611cb4a5 | 184 | { |
414a389f PA |
185 | struct breakpoint *bp; |
186 | ||
187 | bp = set_breakpoint_at (stop_at, NULL); | |
188 | ||
189 | bp->type = reinsert_breakpoint; | |
611cb4a5 DJ |
190 | } |
191 | ||
192 | void | |
d50171e4 | 193 | delete_reinsert_breakpoints (void) |
611cb4a5 | 194 | { |
d50171e4 PA |
195 | struct process_info *proc = current_process (); |
196 | struct breakpoint *bp, **bp_link; | |
611cb4a5 | 197 | |
d50171e4 PA |
198 | bp = proc->breakpoints; |
199 | bp_link = &proc->breakpoints; | |
611cb4a5 | 200 | |
d50171e4 PA |
201 | while (bp) |
202 | { | |
414a389f PA |
203 | if (bp->type == reinsert_breakpoint) |
204 | { | |
205 | *bp_link = bp->next; | |
206 | ||
207 | /* If something goes wrong, maybe this is a shared library | |
208 | breakpoint, and the shared library has been unmapped. | |
209 | Assume the breakpoint is gone anyway. */ | |
210 | uninsert_breakpoint (bp); | |
211 | free (bp); | |
212 | ||
213 | bp = *bp_link; | |
214 | } | |
215 | else | |
216 | { | |
217 | bp_link = &bp->next; | |
218 | bp = *bp_link; | |
219 | } | |
d50171e4 PA |
220 | } |
221 | } | |
b65d95c5 | 222 | |
d50171e4 PA |
223 | static void |
224 | uninsert_breakpoint (struct breakpoint *bp) | |
225 | { | |
226 | if (bp->inserted) | |
227 | { | |
228 | int err; | |
229 | ||
230 | bp->inserted = 0; | |
231 | err = (*the_target->write_memory) (bp->pc, bp->old_data, | |
232 | breakpoint_len); | |
233 | if (err != 0) | |
234 | { | |
235 | bp->inserted = 1; | |
611cb4a5 | 236 | |
d50171e4 PA |
237 | if (debug_threads) |
238 | fprintf (stderr, | |
239 | "Failed to uninsert raw breakpoint at 0x%s (%s).\n", | |
240 | paddress (bp->pc), strerror (err)); | |
241 | } | |
242 | } | |
611cb4a5 DJ |
243 | } |
244 | ||
245 | void | |
d50171e4 | 246 | uninsert_breakpoints_at (CORE_ADDR pc) |
611cb4a5 DJ |
247 | { |
248 | struct breakpoint *bp; | |
249 | ||
d50171e4 | 250 | bp = find_breakpoint_at (pc); |
611cb4a5 | 251 | if (bp == NULL) |
d50171e4 PA |
252 | { |
253 | /* This can happen when we remove all breakpoints while handling | |
254 | a step-over. */ | |
255 | if (debug_threads) | |
256 | fprintf (stderr, | |
257 | "Could not find breakpoint at 0x%s " | |
258 | "in list (uninserting).\n", | |
259 | paddress (pc)); | |
260 | return; | |
261 | } | |
611cb4a5 | 262 | |
d50171e4 PA |
263 | if (bp->inserted) |
264 | uninsert_breakpoint (bp); | |
611cb4a5 DJ |
265 | } |
266 | ||
d50171e4 | 267 | static void |
414a389f | 268 | reinsert_raw_breakpoint (struct breakpoint *bp) |
611cb4a5 | 269 | { |
d50171e4 | 270 | int err; |
611cb4a5 | 271 | |
d50171e4 | 272 | if (bp->inserted) |
611cb4a5 DJ |
273 | error ("Breakpoint already inserted at reinsert time."); |
274 | ||
d50171e4 PA |
275 | err = (*the_target->write_memory) (bp->pc, breakpoint_data, |
276 | breakpoint_len); | |
277 | if (err == 0) | |
278 | bp->inserted = 1; | |
279 | else if (debug_threads) | |
280 | fprintf (stderr, | |
281 | "Failed to reinsert breakpoint at 0x%s (%s).\n", | |
282 | paddress (bp->pc), strerror (err)); | |
611cb4a5 DJ |
283 | } |
284 | ||
d50171e4 PA |
285 | void |
286 | reinsert_breakpoints_at (CORE_ADDR pc) | |
611cb4a5 DJ |
287 | { |
288 | struct breakpoint *bp; | |
289 | ||
d50171e4 | 290 | bp = find_breakpoint_at (pc); |
611cb4a5 | 291 | if (bp == NULL) |
611cb4a5 | 292 | { |
d50171e4 PA |
293 | /* This can happen when we remove all breakpoints while handling |
294 | a step-over. */ | |
295 | if (debug_threads) | |
296 | fprintf (stderr, | |
297 | "Could not find breakpoint at 0x%s " | |
298 | "in list (reinserting).\n", | |
299 | paddress (pc)); | |
300 | return; | |
611cb4a5 DJ |
301 | } |
302 | ||
414a389f | 303 | reinsert_raw_breakpoint (bp); |
d50171e4 PA |
304 | } |
305 | ||
306 | void | |
307 | check_breakpoints (CORE_ADDR stop_pc) | |
308 | { | |
309 | struct process_info *proc = current_process (); | |
310 | struct breakpoint *bp, **bp_link; | |
311 | ||
312 | bp = proc->breakpoints; | |
313 | bp_link = &proc->breakpoints; | |
314 | ||
315 | while (bp) | |
b65d95c5 | 316 | { |
d50171e4 PA |
317 | if (bp->pc == stop_pc) |
318 | { | |
319 | if (!bp->inserted) | |
320 | { | |
321 | warning ("Hit a removed breakpoint?"); | |
322 | return; | |
323 | } | |
324 | ||
325 | if (bp->handler != NULL && (*bp->handler) (stop_pc)) | |
326 | { | |
327 | *bp_link = bp->next; | |
328 | ||
329 | delete_breakpoint (bp); | |
330 | ||
331 | bp = *bp_link; | |
332 | continue; | |
333 | } | |
334 | } | |
335 | ||
336 | bp_link = &bp->next; | |
337 | bp = *bp_link; | |
b65d95c5 | 338 | } |
611cb4a5 DJ |
339 | } |
340 | ||
341 | void | |
f450004a | 342 | set_breakpoint_data (const unsigned char *bp_data, int bp_len) |
611cb4a5 DJ |
343 | { |
344 | breakpoint_data = bp_data; | |
345 | breakpoint_len = bp_len; | |
346 | } | |
347 | ||
d50171e4 PA |
348 | int |
349 | breakpoint_here (CORE_ADDR addr) | |
350 | { | |
351 | struct process_info *proc = current_process (); | |
352 | struct breakpoint *bp; | |
353 | ||
354 | for (bp = proc->breakpoints; bp != NULL; bp = bp->next) | |
355 | if (bp->pc == addr) | |
356 | return 1; | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | int | |
362 | breakpoint_inserted_here (CORE_ADDR addr) | |
363 | { | |
364 | struct process_info *proc = current_process (); | |
365 | struct breakpoint *bp; | |
366 | ||
367 | for (bp = proc->breakpoints; bp != NULL; bp = bp->next) | |
368 | if (bp->pc == addr && bp->inserted) | |
369 | return 1; | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
611cb4a5 | 374 | void |
f450004a | 375 | check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) |
611cb4a5 | 376 | { |
95954743 PA |
377 | struct process_info *proc = current_process (); |
378 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
379 | CORE_ADDR mem_end = mem_addr + mem_len; |
380 | ||
381 | for (; bp != NULL; bp = bp->next) | |
382 | { | |
383 | CORE_ADDR bp_end = bp->pc + breakpoint_len; | |
384 | CORE_ADDR start, end; | |
385 | int copy_offset, copy_len, buf_offset; | |
386 | ||
387 | if (mem_addr >= bp_end) | |
388 | continue; | |
389 | if (bp->pc >= mem_end) | |
390 | continue; | |
391 | ||
392 | start = bp->pc; | |
393 | if (mem_addr > start) | |
394 | start = mem_addr; | |
395 | ||
396 | end = bp_end; | |
397 | if (end > mem_end) | |
398 | end = mem_end; | |
399 | ||
400 | copy_len = end - start; | |
401 | copy_offset = start - bp->pc; | |
402 | buf_offset = start - mem_addr; | |
403 | ||
404 | memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); | |
405 | } | |
406 | } | |
407 | ||
408 | void | |
f450004a | 409 | check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) |
611cb4a5 | 410 | { |
95954743 PA |
411 | struct process_info *proc = current_process (); |
412 | struct breakpoint *bp = proc->breakpoints; | |
611cb4a5 DJ |
413 | CORE_ADDR mem_end = mem_addr + mem_len; |
414 | ||
415 | for (; bp != NULL; bp = bp->next) | |
416 | { | |
417 | CORE_ADDR bp_end = bp->pc + breakpoint_len; | |
418 | CORE_ADDR start, end; | |
419 | int copy_offset, copy_len, buf_offset; | |
420 | ||
421 | if (mem_addr >= bp_end) | |
422 | continue; | |
423 | if (bp->pc >= mem_end) | |
424 | continue; | |
425 | ||
426 | start = bp->pc; | |
427 | if (mem_addr > start) | |
428 | start = mem_addr; | |
429 | ||
430 | end = bp_end; | |
431 | if (end > mem_end) | |
432 | end = mem_end; | |
433 | ||
434 | copy_len = end - start; | |
435 | copy_offset = start - bp->pc; | |
436 | buf_offset = start - mem_addr; | |
437 | ||
438 | memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len); | |
d50171e4 | 439 | if (bp->inserted) |
611cb4a5 DJ |
440 | memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len); |
441 | } | |
442 | } | |
ae13219e | 443 | |
95954743 | 444 | /* Delete all breakpoints, and un-insert them from the inferior. */ |
ae13219e DJ |
445 | |
446 | void | |
447 | delete_all_breakpoints (void) | |
448 | { | |
95954743 PA |
449 | struct process_info *proc = current_process (); |
450 | ||
451 | while (proc->breakpoints) | |
452 | delete_breakpoint (proc->breakpoints); | |
453 | } | |
454 | ||
455 | /* Release all breakpoints, but do not try to un-insert them from the | |
456 | inferior. */ | |
457 | ||
458 | void | |
459 | free_all_breakpoints (struct process_info *proc) | |
460 | { | |
461 | struct breakpoint *bp; | |
462 | ||
463 | while (proc->breakpoints) | |
464 | { | |
465 | bp = proc->breakpoints; | |
466 | proc->breakpoints = bp->next; | |
467 | free (bp); | |
468 | } | |
ae13219e | 469 | } |