X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fmem-break.c;h=5c73326ebb547a5c93bbae09ca4011c1a0ecd182;hb=bae62ee2087bb54fd06746c99de9b734cc58a721;hp=6886ab801a009dbb086a03e4b2275f2f709f2840;hpb=28e7fd62340426746f9c896cbc40c5d374ec47aa;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 6886ab801a..5c73326ebb 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -1,5 +1,5 @@ /* Memory breakpoint operations for the remote server for GDB. - Copyright (C) 2002-2013 Free Software Foundation, Inc. + Copyright (C) 2002-2016 Free Software Foundation, Inc. Contributed by MontaVista Software. @@ -21,20 +21,53 @@ #include "server.h" #include "regcache.h" #include "ax.h" -#include - -const unsigned char *breakpoint_data; -int breakpoint_len; #define MAX_BREAKPOINT_LEN 8 +/* Helper macro used in loops that append multiple items to a singly-linked + list instead of inserting items at the head of the list, as, say, in the + breakpoint lists. LISTPP is a pointer to the pointer that is the head of + the new list. ITEMP is a pointer to the item to be added to the list. + TAILP must be defined to be the same type as ITEMP, and initialized to + NULL. */ + +#define APPEND_TO_LIST(listpp, itemp, tailp) \ + do \ + { \ + if ((tailp) == NULL) \ + *(listpp) = (itemp); \ + else \ + (tailp)->next = (itemp); \ + (tailp) = (itemp); \ + } \ + while (0) + /* GDB will never try to install multiple breakpoints at the same - address. But, we need to keep track of internal breakpoints too, - and so we do need to be able to install multiple breakpoints at the - same address transparently. We keep track of two different, and - closely related structures. A raw breakpoint, which manages the - low level, close to the metal aspect of a breakpoint. It holds the - breakpoint address, and a buffer holding a copy of the instructions + address. However, we can see GDB requesting to insert a breakpoint + at an address is had already inserted one previously in a few + situations. + + - The RSP documentation on Z packets says that to avoid potential + problems with duplicate packets, the operations should be + implemented in an idempotent way. + + - A breakpoint is set at ADDR, an address in a shared library. + Then the shared library is unloaded. And then another, unrelated, + breakpoint at ADDR is set. There is not breakpoint removal request + between the first and the second breakpoint. + + - When GDB wants to update the target-side breakpoint conditions or + commands, it re-inserts the breakpoint, with updated + conditions/commands associated. + + Also, we need to keep track of internal breakpoints too, so we do + need to be able to install multiple breakpoints at the same address + transparently. + + We keep track of two different, and closely related structures. A + raw breakpoint, which manages the low level, close to the metal + aspect of a breakpoint. It holds the breakpoint address, and for + software breakpoints, a buffer holding a copy of the instructions that would be in memory had not been a breakpoint there (we call that the shadow memory of the breakpoint). We occasionally need to temporarilly uninsert a breakpoint without the client knowing about @@ -53,6 +86,10 @@ struct raw_breakpoint { struct raw_breakpoint *next; + /* The low level type of the breakpoint (software breakpoint, + watchpoint, etc.) */ + enum raw_bkpt_type raw_type; + /* A reference count. Each high level breakpoint referencing this raw breakpoint accounts for one reference. */ int refcount; @@ -61,23 +98,40 @@ struct raw_breakpoint breakpoint for a given PC. */ CORE_ADDR pc; + /* The breakpoint's kind. This is target specific. Most + architectures only use one specific instruction for breakpoints, while + others may use more than one. E.g., on ARM, we need to use different + breakpoint instructions on Thumb, Thumb-2, and ARM code. Likewise for + hardware breakpoints -- some architectures (including ARM) need to + setup debug registers differently depending on mode. */ + int kind; + /* The breakpoint's shadow memory. */ unsigned char old_data[MAX_BREAKPOINT_LEN]; - /* Non-zero if this breakpoint is currently inserted in the - inferior. */ + /* Positive if this breakpoint is currently inserted in the + inferior. Negative if it was, but we've detected that it's now + gone. Zero if not inserted. */ int inserted; - - /* Non-zero if this breakpoint is currently disabled because we no - longer detect it as inserted. */ - int shlib_disabled; }; /* The type of a breakpoint. */ enum bkpt_type { /* A GDB breakpoint, requested with a Z0 packet. */ - gdb_breakpoint, + gdb_breakpoint_Z0, + + /* A GDB hardware breakpoint, requested with a Z1 packet. */ + gdb_breakpoint_Z1, + + /* A GDB write watchpoint, requested with a Z2 packet. */ + gdb_breakpoint_Z2, + + /* A GDB read watchpoint, requested with a Z3 packet. */ + gdb_breakpoint_Z3, + + /* A GDB access watchpoint, requested with a Z4 packet. */ + gdb_breakpoint_Z4, /* A basic-software-single-step breakpoint. */ reinsert_breakpoint, @@ -138,8 +192,82 @@ struct breakpoint int (*handler) (CORE_ADDR); }; +/* Return the breakpoint size from its kind. */ + +static int +bp_size (struct raw_breakpoint *bp) +{ + int size = 0; + + the_target->sw_breakpoint_from_kind (bp->kind, &size); + return size; +} + +/* Return the breakpoint opcode from its kind. */ + +static const gdb_byte * +bp_opcode (struct raw_breakpoint *bp) +{ + int size = 0; + + return the_target->sw_breakpoint_from_kind (bp->kind, &size); +} + +/* See mem-break.h. */ + +enum target_hw_bp_type +raw_bkpt_type_to_target_hw_bp_type (enum raw_bkpt_type raw_type) +{ + switch (raw_type) + { + case raw_bkpt_type_hw: + return hw_execute; + case raw_bkpt_type_write_wp: + return hw_write; + case raw_bkpt_type_read_wp: + return hw_read; + case raw_bkpt_type_access_wp: + return hw_access; + default: + internal_error (__FILE__, __LINE__, + "bad raw breakpoint type %d", (int) raw_type); + } +} + +/* See mem-break.h. */ + +static enum bkpt_type +Z_packet_to_bkpt_type (char z_type) +{ + gdb_assert ('0' <= z_type && z_type <= '4'); + + return (enum bkpt_type) (gdb_breakpoint_Z0 + (z_type - '0')); +} + +/* See mem-break.h. */ + +enum raw_bkpt_type +Z_packet_to_raw_bkpt_type (char z_type) +{ + switch (z_type) + { + case Z_PACKET_SW_BP: + return raw_bkpt_type_sw; + case Z_PACKET_HW_BP: + return raw_bkpt_type_hw; + case Z_PACKET_WRITE_WP: + return raw_bkpt_type_write_wp; + case Z_PACKET_READ_WP: + return raw_bkpt_type_read_wp; + case Z_PACKET_ACCESS_WP: + return raw_bkpt_type_access_wp; + default: + gdb_assert_not_reached ("unhandled Z packet type."); + } +} + int -any_persistent_commands () +any_persistent_commands (void) { struct process_info *proc = current_process (); struct breakpoint *bp; @@ -155,73 +283,165 @@ any_persistent_commands () return 0; } +/* Find low-level breakpoint of type TYPE at address ADDR that is not + insert-disabled. Returns NULL if not found. */ + static struct raw_breakpoint * -find_raw_breakpoint_at (CORE_ADDR where) +find_enabled_raw_code_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type) { struct process_info *proc = current_process (); struct raw_breakpoint *bp; for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) - if (bp->pc == where) + if (bp->pc == addr + && bp->raw_type == type + && bp->inserted >= 0) return bp; return NULL; } +/* Find low-level breakpoint of type TYPE at address ADDR. Returns + NULL if not found. */ + static struct raw_breakpoint * -set_raw_breakpoint_at (CORE_ADDR where) +find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int kind) { struct process_info *proc = current_process (); struct raw_breakpoint *bp; - int err; - unsigned char buf[MAX_BREAKPOINT_LEN]; - if (breakpoint_data == NULL) - error ("Target does not support breakpoints."); - - bp = find_raw_breakpoint_at (where); - if (bp != NULL) - { - bp->refcount++; + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (bp->pc == addr && bp->raw_type == type && bp->kind == kind) return bp; - } - bp = xcalloc (1, sizeof (*bp)); - bp->pc = where; - bp->refcount = 1; + return NULL; +} + +/* See mem-break.h. */ + +int +insert_memory_breakpoint (struct raw_breakpoint *bp) +{ + unsigned char buf[MAX_BREAKPOINT_LEN]; + int err; /* Note that there can be fast tracepoint jumps installed in the same memory range, so to get at the original memory, we need to use read_inferior_memory, which masks those out. */ - err = read_inferior_memory (where, buf, breakpoint_len); + err = read_inferior_memory (bp->pc, buf, bp_size (bp)); if (err != 0) { if (debug_threads) - fprintf (stderr, - "Failed to read shadow memory of" - " breakpoint at 0x%s (%s).\n", - paddress (where), strerror (err)); - free (bp); - return NULL; + debug_printf ("Failed to read shadow memory of" + " breakpoint at 0x%s (%s).\n", + paddress (bp->pc), strerror (err)); } - memcpy (bp->old_data, buf, breakpoint_len); + else + { + memcpy (bp->old_data, buf, bp_size (bp)); + + err = (*the_target->write_memory) (bp->pc, bp_opcode (bp), + bp_size (bp)); + if (err != 0) + { + if (debug_threads) + debug_printf ("Failed to insert breakpoint at 0x%s (%s).\n", + paddress (bp->pc), strerror (err)); + } + } + return err != 0 ? -1 : 0; +} + +/* See mem-break.h */ - err = (*the_target->write_memory) (where, breakpoint_data, - breakpoint_len); +int +remove_memory_breakpoint (struct raw_breakpoint *bp) +{ + unsigned char buf[MAX_BREAKPOINT_LEN]; + int err; + + /* Since there can be trap breakpoints inserted in the same address + range, we use `write_inferior_memory', which takes care of + layering breakpoints on top of fast tracepoints, and on top of + the buffer we pass it. This works because the caller has already + either unlinked the breakpoint or marked it uninserted. Also + note that we need to pass the current shadow contents, because + write_inferior_memory updates any shadow memory with what we pass + here, and we want that to be a nop. */ + memcpy (buf, bp->old_data, bp_size (bp)); + err = write_inferior_memory (bp->pc, buf, bp_size (bp)); if (err != 0) { if (debug_threads) - fprintf (stderr, - "Failed to insert breakpoint at 0x%s (%s).\n", - paddress (where), strerror (err)); - free (bp); - return NULL; + debug_printf ("Failed to uninsert raw breakpoint " + "at 0x%s (%s) while deleting it.\n", + paddress (bp->pc), strerror (err)); } + return err != 0 ? -1 : 0; +} + +/* Set a RAW breakpoint of type TYPE and kind KIND at WHERE. On + success, a pointer to the new breakpoint is returned. On failure, + returns NULL and writes the error code to *ERR. */ + +static struct raw_breakpoint * +set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int kind, + int *err) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); + + if (type == raw_bkpt_type_sw || type == raw_bkpt_type_hw) + { + bp = find_enabled_raw_code_breakpoint_at (where, type); + if (bp != NULL && bp->kind != kind) + { + /* A different kind than previously seen. The previous + breakpoint must be gone then. */ + if (debug_threads) + debug_printf ("Inconsistent breakpoint kind? Was %d, now %d.\n", + bp->kind, kind); + bp->inserted = -1; + bp = NULL; + } + } + else + bp = find_raw_breakpoint_at (where, type, kind); + + if (bp == NULL) + { + bp = XCNEW (struct raw_breakpoint); + bp->pc = where; + bp->kind = kind; + bp->raw_type = type; + make_cleanup (xfree, bp); + } + + if (!bp->inserted) + { + *err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp); + if (*err != 0) + { + if (debug_threads) + debug_printf ("Failed to insert breakpoint at 0x%s (%d).\n", + paddress (where), *err); + + do_cleanups (old_chain); + return NULL; + } + + bp->inserted = 1; + } + + discard_cleanups (old_chain); - /* Link the breakpoint in. */ - bp->inserted = 1; - bp->next = proc->raw_breakpoints; - proc->raw_breakpoints = bp; + /* Link the breakpoint in, if this is the first reference. */ + if (++bp->refcount == 1) + { + bp->next = proc->raw_breakpoints; + proc->raw_breakpoints = bp; + } return bp; } @@ -325,7 +545,7 @@ delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel) pass the current shadow contents, because write_inferior_memory updates any shadow memory with what we pass here, and we want that to be a nop. */ - buf = alloca (bp->length); + buf = (unsigned char *) alloca (bp->length); memcpy (buf, fast_tracepoint_jump_shadow (bp), bp->length); ret = write_inferior_memory (bp->pc, buf, bp->length); if (ret != 0) @@ -334,10 +554,9 @@ delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel) *bp_link = prev_bp_link; if (debug_threads) - fprintf (stderr, - "Failed to uninsert fast tracepoint jump " - "at 0x%s (%s) while deleting it.\n", - paddress (bp->pc), strerror (ret)); + debug_printf ("Failed to uninsert fast tracepoint jump " + "at 0x%s (%s) while deleting it.\n", + paddress (bp->pc), strerror (ret)); return ret; } @@ -384,12 +603,12 @@ set_fast_tracepoint_jump (CORE_ADDR where, /* We don't, so create a new object. Double the length, because the flexible array member holds both the jump insn, and the shadow. */ - jp = xcalloc (1, sizeof (*jp) + (length * 2)); + jp = (struct fast_tracepoint_jump *) xcalloc (1, sizeof (*jp) + (length * 2)); jp->pc = where; jp->length = length; memcpy (fast_tracepoint_jump_insn (jp), insn, length); jp->refcount = 1; - buf = alloca (length); + buf = (unsigned char *) alloca (length); /* Note that there can be trap breakpoints inserted in the same address range. To access the original memory contents, we use @@ -398,10 +617,9 @@ set_fast_tracepoint_jump (CORE_ADDR where, if (err != 0) { if (debug_threads) - fprintf (stderr, - "Failed to read shadow memory of" - " fast tracepoint at 0x%s (%s).\n", - paddress (where), strerror (err)); + debug_printf ("Failed to read shadow memory of" + " fast tracepoint at 0x%s (%s).\n", + paddress (where), strerror (err)); free (jp); return NULL; } @@ -424,9 +642,8 @@ set_fast_tracepoint_jump (CORE_ADDR where, if (err != 0) { if (debug_threads) - fprintf (stderr, - "Failed to insert fast tracepoint jump at 0x%s (%s).\n", - paddress (where), strerror (err)); + debug_printf ("Failed to insert fast tracepoint jump at 0x%s (%s).\n", + paddress (where), strerror (err)); /* Unlink it. */ proc->fast_tracepoint_jumps = jp->next; @@ -450,10 +667,9 @@ uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc) /* This can happen when we remove all breakpoints while handling a step-over. */ if (debug_threads) - fprintf (stderr, - "Could not find fast tracepoint jump at 0x%s " - "in list (uninserting).\n", - paddress (pc)); + debug_printf ("Could not find fast tracepoint jump at 0x%s " + "in list (uninserting).\n", + paddress (pc)); return; } @@ -472,7 +688,7 @@ uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc) pass the current shadow contents, because write_inferior_memory updates any shadow memory with what we pass here, and we want that to be a nop. */ - buf = alloca (jp->length); + buf = (unsigned char *) alloca (jp->length); memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length); err = write_inferior_memory (jp->pc, buf, jp->length); if (err != 0) @@ -480,9 +696,9 @@ uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc) jp->inserted = 1; if (debug_threads) - fprintf (stderr, - "Failed to uninsert fast tracepoint jump at 0x%s (%s).\n", - paddress (pc), strerror (err)); + debug_printf ("Failed to uninsert fast tracepoint jump at" + " 0x%s (%s).\n", + paddress (pc), strerror (err)); } } } @@ -500,10 +716,9 @@ reinsert_fast_tracepoint_jumps_at (CORE_ADDR where) /* This can happen when we remove breakpoints when a tracepoint hit causes a tracing stop, while handling a step-over. */ if (debug_threads) - fprintf (stderr, - "Could not find fast tracepoint jump at 0x%s " - "in list (reinserting).\n", - paddress (where)); + debug_printf ("Could not find fast tracepoint jump at 0x%s " + "in list (reinserting).\n", + paddress (where)); return; } @@ -520,7 +735,7 @@ reinsert_fast_tracepoint_jumps_at (CORE_ADDR where) to pass the current shadow contents, because write_inferior_memory updates any shadow memory with what we pass here, and we want that to be a nop. */ - buf = alloca (jp->length); + buf = (unsigned char *) alloca (jp->length); memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length); err = write_inferior_memory (where, buf, jp->length); if (err != 0) @@ -528,20 +743,29 @@ reinsert_fast_tracepoint_jumps_at (CORE_ADDR where) jp->inserted = 0; if (debug_threads) - fprintf (stderr, - "Failed to reinsert fast tracepoint jump at 0x%s (%s).\n", - paddress (where), strerror (err)); + debug_printf ("Failed to reinsert fast tracepoint jump at" + " 0x%s (%s).\n", + paddress (where), strerror (err)); } } -struct breakpoint * -set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) +/* Set a high-level breakpoint of type TYPE, with low level type + RAW_TYPE and kind KIND, at WHERE. On success, a pointer to the new + breakpoint is returned. On failure, returns NULL and writes the + error code to *ERR. HANDLER is called when the breakpoint is hit. + HANDLER should return 1 if the breakpoint should be deleted, 0 + otherwise. */ + +static struct breakpoint * +set_breakpoint (enum bkpt_type type, enum raw_bkpt_type raw_type, + CORE_ADDR where, int kind, + int (*handler) (CORE_ADDR), int *err) { struct process_info *proc = current_process (); struct breakpoint *bp; struct raw_breakpoint *raw; - raw = set_raw_breakpoint_at (where); + raw = set_raw_breakpoint_at (raw_type, where, kind, err); if (raw == NULL) { @@ -549,8 +773,8 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) return NULL; } - bp = xcalloc (1, sizeof (struct breakpoint)); - bp->type = other_breakpoint; + bp = XCNEW (struct breakpoint); + bp->type = type; bp->raw = raw; bp->handler = handler; @@ -561,6 +785,21 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) return bp; } +/* See mem-break.h */ + +struct breakpoint * +set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) +{ + int err_ignored; + CORE_ADDR placed_address = where; + int breakpoint_kind = target_breakpoint_kind_from_pc (&placed_address); + + return set_breakpoint (other_breakpoint, raw_bkpt_type_sw, + placed_address, breakpoint_kind, handler, + &err_ignored); +} + + static int delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel) { @@ -574,37 +813,25 @@ delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel) { if (bp == todel) { - if (bp->inserted) + if (bp->inserted > 0) { struct raw_breakpoint *prev_bp_link = *bp_link; - unsigned char buf[MAX_BREAKPOINT_LEN]; *bp_link = bp->next; - /* Since there can be trap breakpoints inserted in the - same address range, we use `write_inferior_memory', - which takes care of layering breakpoints on top of - fast tracepoints, and on top of the buffer we pass - it. This works because we've already unlinked the - fast tracepoint jump above. Also note that we need - to pass the current shadow contents, because - write_inferior_memory updates any shadow memory with - what we pass here, and we want that to be a nop. */ - memcpy (buf, bp->old_data, breakpoint_len); - ret = write_inferior_memory (bp->pc, buf, breakpoint_len); + ret = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, + bp); if (ret != 0) { /* Something went wrong, relink the breakpoint. */ *bp_link = prev_bp_link; if (debug_threads) - fprintf (stderr, - "Failed to uninsert raw breakpoint " - "at 0x%s (%s) while deleting it.\n", - paddress (bp->pc), strerror (ret)); + debug_printf ("Failed to uninsert raw breakpoint " + "at 0x%s while deleting it.\n", + paddress (bp->pc)); return ret; } - } else *bp_link = bp->next; @@ -684,85 +911,223 @@ delete_breakpoint (struct breakpoint *todel) return delete_breakpoint_1 (proc, todel); } -struct breakpoint * -find_gdb_breakpoint_at (CORE_ADDR where) +/* Locate a GDB breakpoint of type Z_TYPE and kind KIND placed at + address ADDR and return a pointer to its structure. If KIND is -1, + the breakpoint's kind is ignored. */ + +static struct breakpoint * +find_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind) { struct process_info *proc = current_process (); struct breakpoint *bp; + enum bkpt_type type = Z_packet_to_bkpt_type (z_type); for (bp = proc->breakpoints; bp != NULL; bp = bp->next) - if (bp->type == gdb_breakpoint && bp->raw->pc == where) + if (bp->type == type && bp->raw->pc == addr + && (kind == -1 || bp->raw->kind == kind)) return bp; return NULL; } -int -set_gdb_breakpoint_at (CORE_ADDR where) +static int +z_type_supported (char z_type) +{ + return (z_type >= '0' && z_type <= '4' + && the_target->supports_z_point_type != NULL + && the_target->supports_z_point_type (z_type)); +} + +/* Create a new GDB breakpoint of type Z_TYPE at ADDR with kind KIND. + Returns a pointer to the newly created breakpoint on success. On + failure returns NULL and sets *ERR to either -1 for error, or 1 if + Z_TYPE breakpoints are not supported on this target. */ + +static struct breakpoint * +set_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind, int *err) { struct breakpoint *bp; + enum bkpt_type type; + enum raw_bkpt_type raw_type; + + /* If we see GDB inserting a second code breakpoint at the same + address, then either: GDB is updating the breakpoint's conditions + or commands; or, the first breakpoint must have disappeared due + to a shared library unload. On targets where the shared + libraries are handled by userspace, like SVR4, for example, + GDBserver can't tell if a library was loaded or unloaded. Since + we refcount raw breakpoints, we must be careful to make sure GDB + breakpoints never contribute more than one reference. if we + didn't do this, in case the previous breakpoint is gone due to a + shared library unload, we'd just increase the refcount of the + previous breakpoint at this address, but the trap was not planted + in the inferior anymore, thus the breakpoint would never be hit. + Note this must be careful to not create a window where + breakpoints are removed from the target, for non-stop, in case + the target can poke at memory while the program is running. */ + if (z_type == Z_PACKET_SW_BP + || z_type == Z_PACKET_HW_BP) + { + bp = find_gdb_breakpoint (z_type, addr, -1); - if (breakpoint_data == NULL) - return 1; + if (bp != NULL) + { + if (bp->raw->kind != kind) + { + /* A different kind than previously seen. The previous + breakpoint must be gone then. */ + bp->raw->inserted = -1; + delete_breakpoint (bp); + bp = NULL; + } + else if (z_type == Z_PACKET_SW_BP) + { + /* Check if the breakpoint is actually gone from the + target, due to an solib unload, for example. Might + as well validate _all_ breakpoints. */ + validate_breakpoints (); + + /* Breakpoints that don't pass validation are + deleted. */ + bp = find_gdb_breakpoint (z_type, addr, -1); + } + } + } + else + { + /* Data breakpoints for the same address but different kind are + expected. GDB doesn't merge these. The backend gets to do + that if it wants/can. */ + bp = find_gdb_breakpoint (z_type, addr, kind); + } - /* If we see GDB inserting a second breakpoint at the same address, - then the first breakpoint must have disappeared due to a shared - library unload. On targets where the shared libraries are - handled by userspace, like SVR4, for example, GDBserver can't - tell if a library was loaded or unloaded. Since we refcount - breakpoints, if we didn't do this, we'd just increase the - refcount of the previous breakpoint at this address, but the trap - was not planted in the inferior anymore, thus the breakpoint - would never be hit. */ - bp = find_gdb_breakpoint_at (where); if (bp != NULL) { - delete_gdb_breakpoint_at (where); + /* We already know about this breakpoint, there's nothing else + to do - GDB's reference is already accounted for. Note that + whether the breakpoint inserted is left as is - we may be + stepping over it, for example, in which case we don't want to + force-reinsert it. */ + return bp; + } + + raw_type = Z_packet_to_raw_bkpt_type (z_type); + type = Z_packet_to_bkpt_type (z_type); + return set_breakpoint (type, raw_type, addr, kind, NULL, err); +} - /* Might as well validate all other breakpoints. */ - validate_breakpoints (); +static int +check_gdb_bp_preconditions (char z_type, int *err) +{ + /* As software/memory breakpoints work by poking at memory, we need + to prepare to access memory. If that operation fails, we need to + return error. Seeing an error, if this is the first breakpoint + of that type that GDB tries to insert, GDB would then assume the + breakpoint type is supported, but it may actually not be. So we + need to check whether the type is supported at all before + preparing to access memory. */ + if (!z_type_supported (z_type)) + { + *err = 1; + return 0; } - bp = set_breakpoint_at (where, NULL); - if (bp == NULL) - return -1; + return 1; +} - bp->type = gdb_breakpoint; - return 0; +/* See mem-break.h. This is a wrapper for set_gdb_breakpoint_1 that + knows to prepare to access memory for Z0 breakpoints. */ + +struct breakpoint * +set_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind, int *err) +{ + struct breakpoint *bp; + + if (!check_gdb_bp_preconditions (z_type, err)) + return NULL; + + /* If inserting a software/memory breakpoint, need to prepare to + access memory. */ + if (z_type == Z_PACKET_SW_BP) + { + if (prepare_to_access_memory () != 0) + { + *err = -1; + return NULL; + } + } + + bp = set_gdb_breakpoint_1 (z_type, addr, kind, err); + + if (z_type == Z_PACKET_SW_BP) + done_accessing_memory (); + + return bp; } -int -delete_gdb_breakpoint_at (CORE_ADDR addr) +/* Delete a GDB breakpoint of type Z_TYPE and kind KIND previously + inserted at ADDR with set_gdb_breakpoint_at. Returns 0 on success, + -1 on error, and 1 if Z_TYPE breakpoints are not supported on this + target. */ + +static int +delete_gdb_breakpoint_1 (char z_type, CORE_ADDR addr, int kind) { struct breakpoint *bp; int err; - if (breakpoint_data == NULL) - return 1; - - bp = find_gdb_breakpoint_at (addr); + bp = find_gdb_breakpoint (z_type, addr, kind); if (bp == NULL) return -1; - /* Before deleting the breakpoint, make sure to free - its condition list. */ - clear_gdb_breakpoint_conditions (addr); + /* Before deleting the breakpoint, make sure to free its condition + and command lists. */ + clear_breakpoint_conditions_and_commands (bp); err = delete_breakpoint (bp); - if (err) + if (err != 0) return -1; return 0; } -/* Clear all conditions associated with this breakpoint address. */ +/* See mem-break.h. This is a wrapper for delete_gdb_breakpoint that + knows to prepare to access memory for Z0 breakpoints. */ -void -clear_gdb_breakpoint_conditions (CORE_ADDR addr) +int +delete_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind) +{ + int ret; + + if (!check_gdb_bp_preconditions (z_type, &ret)) + return ret; + + /* If inserting a software/memory breakpoint, need to prepare to + access memory. */ + if (z_type == Z_PACKET_SW_BP) + { + int err; + + err = prepare_to_access_memory (); + if (err != 0) + return -1; + } + + ret = delete_gdb_breakpoint_1 (z_type, addr, kind); + + if (z_type == Z_PACKET_SW_BP) + done_accessing_memory (); + + return ret; +} + +/* Clear all conditions associated with a breakpoint. */ + +static void +clear_breakpoint_conditions (struct breakpoint *bp) { - struct breakpoint *bp = find_gdb_breakpoint_at (addr); struct point_cond_list *cond; - if (bp == NULL || bp->cond_list == NULL) + if (bp->cond_list == NULL) return; cond = bp->cond_list; @@ -772,8 +1137,7 @@ clear_gdb_breakpoint_conditions (CORE_ADDR addr) struct point_cond_list *cond_next; cond_next = cond->next; - free (cond->cond->bytes); - free (cond->cond); + gdb_free_agent_expr (cond->cond); free (cond); cond = cond_next; } @@ -781,16 +1145,48 @@ clear_gdb_breakpoint_conditions (CORE_ADDR addr) bp->cond_list = NULL; } -/* Add condition CONDITION to GDBserver's breakpoint BP. */ +/* Clear all commands associated with a breakpoint. */ + +static void +clear_breakpoint_commands (struct breakpoint *bp) +{ + struct point_command_list *cmd; + + if (bp->command_list == NULL) + return; + + cmd = bp->command_list; + + while (cmd != NULL) + { + struct point_command_list *cmd_next; + + cmd_next = cmd->next; + gdb_free_agent_expr (cmd->cmd); + free (cmd); + cmd = cmd_next; + } + + bp->command_list = NULL; +} void +clear_breakpoint_conditions_and_commands (struct breakpoint *bp) +{ + clear_breakpoint_conditions (bp); + clear_breakpoint_commands (bp); +} + +/* Add condition CONDITION to GDBserver's breakpoint BP. */ + +static void add_condition_to_breakpoint (struct breakpoint *bp, struct agent_expr *condition) { struct point_cond_list *new_cond; /* Create new condition. */ - new_cond = xcalloc (1, sizeof (*new_cond)); + new_cond = XCNEW (struct point_cond_list); new_cond->cond = condition; /* Add condition to the list. */ @@ -798,21 +1194,20 @@ add_condition_to_breakpoint (struct breakpoint *bp, bp->cond_list = new_cond; } -/* Add a target-side condition CONDITION to the breakpoint at ADDR. */ +/* Add a target-side condition CONDITION to a breakpoint. */ int -add_breakpoint_condition (CORE_ADDR addr, char **condition) +add_breakpoint_condition (struct breakpoint *bp, char **condition) { - struct breakpoint *bp = find_gdb_breakpoint_at (addr); char *actparm = *condition; struct agent_expr *cond; - if (bp == NULL) - return 1; - if (condition == NULL) return 1; + if (bp == NULL) + return 0; + cond = gdb_parse_agent_expr (&actparm); if (cond == NULL) @@ -826,22 +1221,21 @@ add_breakpoint_condition (CORE_ADDR addr, char **condition) *condition = actparm; - return 0; + return 1; } /* Evaluate condition (if any) at breakpoint BP. Return 1 if true and 0 otherwise. */ -int -gdb_condition_true_at_breakpoint (CORE_ADDR where) +static int +gdb_condition_true_at_breakpoint_z_type (char z_type, CORE_ADDR addr) { /* Fetch registers for the current inferior. */ - struct breakpoint *bp = find_gdb_breakpoint_at (where); + struct breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); ULONGEST value = 0; struct point_cond_list *cl; int err = 0; - - struct regcache *regcache = get_thread_regcache (current_inferior, 1); + struct eval_agent_expr_context ctx; if (bp == NULL) return 0; @@ -851,6 +1245,10 @@ gdb_condition_true_at_breakpoint (CORE_ADDR where) if (bp->cond_list == NULL) return 1; + ctx.regcache = get_thread_regcache (current_thread, 1); + ctx.tframe = NULL; + ctx.tpoint = NULL; + /* Evaluate each condition in the breakpoint's list of conditions. Return true if any of the conditions evaluates to TRUE. @@ -860,7 +1258,7 @@ gdb_condition_true_at_breakpoint (CORE_ADDR where) cl && !value && !err; cl = cl->next) { /* Evaluate the condition. */ - err = gdb_eval_agent_expr (regcache, NULL, cl->cond, &value); + err = gdb_eval_agent_expr (&ctx, cl->cond, &value); } if (err) @@ -869,16 +1267,24 @@ gdb_condition_true_at_breakpoint (CORE_ADDR where) return (value != 0); } +int +gdb_condition_true_at_breakpoint (CORE_ADDR where) +{ + /* Only check code (software or hardware) breakpoints. */ + return (gdb_condition_true_at_breakpoint_z_type (Z_PACKET_SW_BP, where) + || gdb_condition_true_at_breakpoint_z_type (Z_PACKET_HW_BP, where)); +} + /* Add commands COMMANDS to GDBserver's breakpoint BP. */ -void +static void add_commands_to_breakpoint (struct breakpoint *bp, struct agent_expr *commands, int persist) { struct point_command_list *new_cmd; /* Create new command. */ - new_cmd = xcalloc (1, sizeof (*new_cmd)); + new_cmd = XCNEW (struct point_command_list); new_cmd->cmd = commands; new_cmd->persistence = persist; @@ -890,18 +1296,18 @@ add_commands_to_breakpoint (struct breakpoint *bp, /* Add a target-side command COMMAND to the breakpoint at ADDR. */ int -add_breakpoint_commands (CORE_ADDR addr, char **command, int persist) +add_breakpoint_commands (struct breakpoint *bp, char **command, + int persist) { - struct breakpoint *bp = find_gdb_breakpoint_at (addr); char *actparm = *command; struct agent_expr *cmd; - if (bp == NULL) - return 1; - if (command == NULL) return 1; + if (bp == NULL) + return 0; + cmd = gdb_parse_agent_expr (&actparm); if (cmd == NULL) @@ -915,59 +1321,89 @@ add_breakpoint_commands (CORE_ADDR addr, char **command, int persist) *command = actparm; - return 0; + return 1; } /* Return true if there are no commands to run at this location, which likely means we want to report back to GDB. */ -int -gdb_no_commands_at_breakpoint (CORE_ADDR where) + +static int +gdb_no_commands_at_breakpoint_z_type (char z_type, CORE_ADDR addr) { - struct breakpoint *bp = find_gdb_breakpoint_at (where); + struct breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); if (bp == NULL) - return 0; + return 1; if (debug_threads) - fprintf (stderr, "at 0x%s, bp command_list is 0x%s\n", - paddress (where), - phex_nz ((uintptr_t) bp->command_list, 0)); + debug_printf ("at 0x%s, type Z%c, bp command_list is 0x%s\n", + paddress (addr), z_type, + phex_nz ((uintptr_t) bp->command_list, 0)); return (bp->command_list == NULL); } -void -run_breakpoint_commands (CORE_ADDR where) +/* Return true if there are no commands to run at this location, + which likely means we want to report back to GDB. */ + +int +gdb_no_commands_at_breakpoint (CORE_ADDR where) +{ + /* Only check code (software or hardware) breakpoints. */ + return (gdb_no_commands_at_breakpoint_z_type (Z_PACKET_SW_BP, where) + && gdb_no_commands_at_breakpoint_z_type (Z_PACKET_HW_BP, where)); +} + +/* Run a breakpoint's commands. Returns 0 if there was a problem + running any command, 1 otherwise. */ + +static int +run_breakpoint_commands_z_type (char z_type, CORE_ADDR addr) { /* Fetch registers for the current inferior. */ - struct breakpoint *bp = find_gdb_breakpoint_at (where); + struct breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); ULONGEST value = 0; struct point_command_list *cl; int err = 0; - - struct regcache *regcache = get_thread_regcache (current_inferior, 1); + struct eval_agent_expr_context ctx; if (bp == NULL) - return; + return 1; + + ctx.regcache = get_thread_regcache (current_thread, 1); + ctx.tframe = NULL; + ctx.tpoint = NULL; for (cl = bp->command_list; cl && !value && !err; cl = cl->next) { /* Run the command. */ - err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value); + err = gdb_eval_agent_expr (&ctx, cl->cmd, &value); /* If one command has a problem, stop digging the hole deeper. */ if (err) - break; + return 0; } + + return 1; } -/* Return 1 if there is a breakpoint inserted in address WHERE - and if its condition, if it exists, is true. */ +void +run_breakpoint_commands (CORE_ADDR where) +{ + /* Only check code (software or hardware) breakpoints. If one + command has a problem, stop digging the hole deeper. */ + if (run_breakpoint_commands_z_type (Z_PACKET_SW_BP, where)) + run_breakpoint_commands_z_type (Z_PACKET_HW_BP, where); +} + +/* See mem-break.h. */ int gdb_breakpoint_here (CORE_ADDR where) { - return (find_gdb_breakpoint_at (where) != NULL); + /* Only check code (software or hardware) breakpoints. */ + return (find_gdb_breakpoint (Z_PACKET_SW_BP, where, -1) != NULL + || find_gdb_breakpoint (Z_PACKET_HW_BP, where, -1) != NULL); } void @@ -1007,30 +1443,26 @@ delete_reinsert_breakpoints (void) static void uninsert_raw_breakpoint (struct raw_breakpoint *bp) { - if (bp->inserted) + if (bp->inserted < 0) + { + if (debug_threads) + debug_printf ("Breakpoint at %s is marked insert-disabled.\n", + paddress (bp->pc)); + } + else if (bp->inserted > 0) { int err; - unsigned char buf[MAX_BREAKPOINT_LEN]; bp->inserted = 0; - /* Since there can be fast tracepoint jumps inserted in the same - address range, we use `write_inferior_memory', which takes - care of layering breakpoints on top of fast tracepoints, and - on top of the buffer we pass it. This works because we've - already unlinked the fast tracepoint jump above. Also note - that we need to pass the current shadow contents, because - write_inferior_memory updates any shadow memory with what we - pass here, and we want that to be a nop. */ - memcpy (buf, bp->old_data, breakpoint_len); - err = write_inferior_memory (bp->pc, buf, breakpoint_len); + + err = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, bp); if (err != 0) { bp->inserted = 1; if (debug_threads) - fprintf (stderr, - "Failed to uninsert raw breakpoint at 0x%s (%s).\n", - paddress (bp->pc), strerror (err)); + debug_printf ("Failed to uninsert raw breakpoint at 0x%s.\n", + paddress (bp->pc)); } } } @@ -1038,23 +1470,30 @@ uninsert_raw_breakpoint (struct raw_breakpoint *bp) void uninsert_breakpoints_at (CORE_ADDR pc) { + struct process_info *proc = current_process (); struct raw_breakpoint *bp; + int found = 0; - bp = find_raw_breakpoint_at (pc); - if (bp == NULL) + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && bp->pc == pc) + { + found = 1; + + if (bp->inserted) + uninsert_raw_breakpoint (bp); + } + + if (!found) { /* This can happen when we remove all breakpoints while handling a step-over. */ if (debug_threads) - fprintf (stderr, - "Could not find breakpoint at 0x%s " - "in list (uninserting).\n", - paddress (pc)); - return; + debug_printf ("Could not find breakpoint at 0x%s " + "in list (uninserting).\n", + paddress (pc)); } - - if (bp->inserted) - uninsert_raw_breakpoint (bp); } void @@ -1064,47 +1503,96 @@ uninsert_all_breakpoints (void) struct raw_breakpoint *bp; for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) - if (bp->inserted) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && bp->inserted) uninsert_raw_breakpoint (bp); } +void +uninsert_reinsert_breakpoints (void) +{ + struct process_info *proc = current_process (); + struct breakpoint *bp; + + for (bp = proc->breakpoints; bp != NULL; bp = bp->next) + { + if (bp->type == reinsert_breakpoint) + { + gdb_assert (bp->raw->inserted > 0); + + /* Only uninsert the raw breakpoint if it only belongs to a + reinsert breakpoint. */ + if (bp->raw->refcount == 1) + uninsert_raw_breakpoint (bp->raw); + } + } +} + static void reinsert_raw_breakpoint (struct raw_breakpoint *bp) { int err; if (bp->inserted) - error ("Breakpoint already inserted at reinsert time."); + return; - err = (*the_target->write_memory) (bp->pc, breakpoint_data, - breakpoint_len); + err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp); if (err == 0) bp->inserted = 1; else if (debug_threads) - fprintf (stderr, - "Failed to reinsert breakpoint at 0x%s (%s).\n", - paddress (bp->pc), strerror (err)); + debug_printf ("Failed to reinsert breakpoint at 0x%s (%d).\n", + paddress (bp->pc), err); } void reinsert_breakpoints_at (CORE_ADDR pc) { + struct process_info *proc = current_process (); struct raw_breakpoint *bp; + int found = 0; - bp = find_raw_breakpoint_at (pc); - if (bp == NULL) + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && bp->pc == pc) + { + found = 1; + + reinsert_raw_breakpoint (bp); + } + + if (!found) { /* This can happen when we remove all breakpoints while handling a step-over. */ if (debug_threads) - fprintf (stderr, - "Could not find raw breakpoint at 0x%s " - "in list (reinserting).\n", - paddress (pc)); - return; + debug_printf ("Could not find raw breakpoint at 0x%s " + "in list (reinserting).\n", + paddress (pc)); } +} + +int +has_reinsert_breakpoints (struct process_info *proc) +{ + struct breakpoint *bp, **bp_link; + + bp = proc->breakpoints; + bp_link = &proc->breakpoints; - reinsert_raw_breakpoint (bp); + while (bp) + { + if (bp->type == reinsert_breakpoint) + return 1; + else + { + bp_link = &bp->next; + bp = *bp_link; + } + } + + return 0; } void @@ -1114,10 +1602,30 @@ reinsert_all_breakpoints (void) struct raw_breakpoint *bp; for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) - if (!bp->inserted) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && !bp->inserted) reinsert_raw_breakpoint (bp); } +void +reinsert_reinsert_breakpoints (void) +{ + struct process_info *proc = current_process (); + struct breakpoint *bp; + + for (bp = proc->breakpoints; bp != NULL; bp = bp->next) + { + if (bp->type == reinsert_breakpoint) + { + gdb_assert (bp->raw->inserted > 0); + + if (bp->raw->refcount == 1) + reinsert_raw_breakpoint (bp->raw); + } + } +} + void check_breakpoints (CORE_ADDR stop_pc) { @@ -1129,9 +1637,13 @@ check_breakpoints (CORE_ADDR stop_pc) while (bp) { - if (bp->raw->pc == stop_pc) + struct raw_breakpoint *raw = bp->raw; + + if ((raw->raw_type == raw_bkpt_type_sw + || raw->raw_type == raw_bkpt_type_hw) + && raw->pc == stop_pc) { - if (!bp->raw->inserted) + if (!raw->inserted) { warning ("Hit a removed breakpoint?"); return; @@ -1153,27 +1665,86 @@ check_breakpoints (CORE_ADDR stop_pc) } } -void -set_breakpoint_data (const unsigned char *bp_data, int bp_len) +int +breakpoint_here (CORE_ADDR addr) { - breakpoint_data = bp_data; - breakpoint_len = bp_len; + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && bp->pc == addr) + return 1; + + return 0; } int -breakpoint_here (CORE_ADDR addr) +breakpoint_inserted_here (CORE_ADDR addr) { - return (find_raw_breakpoint_at (addr) != NULL); + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if ((bp->raw_type == raw_bkpt_type_sw + || bp->raw_type == raw_bkpt_type_hw) + && bp->pc == addr + && bp->inserted) + return 1; + + return 0; } +/* See mem-break.h. */ + int -breakpoint_inserted_here (CORE_ADDR addr) +software_breakpoint_inserted_here (CORE_ADDR addr) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (bp->raw_type == raw_bkpt_type_sw + && bp->pc == addr + && bp->inserted) + return 1; + + return 0; +} + +/* See mem-break.h. */ + +int +hardware_breakpoint_inserted_here (CORE_ADDR addr) { + struct process_info *proc = current_process (); struct raw_breakpoint *bp; - bp = find_raw_breakpoint_at (addr); + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (bp->raw_type == raw_bkpt_type_hw + && bp->pc == addr + && bp->inserted) + return 1; + + return 0; +} + +/* See mem-break.h. */ + +int +reinsert_breakpoint_inserted_here (CORE_ADDR addr) +{ + struct process_info *proc = current_process (); + struct breakpoint *bp; + + for (bp = proc->breakpoints; bp != NULL; bp = bp->next) + if (bp->type == reinsert_breakpoint + && bp->raw->pc == addr + && bp->raw->inserted) + return 1; - return (bp != NULL && bp->inserted); + return 0; } static int @@ -1183,14 +1754,14 @@ validate_inserted_breakpoint (struct raw_breakpoint *bp) int err; gdb_assert (bp->inserted); + gdb_assert (bp->raw_type == raw_bkpt_type_sw); - buf = alloca (breakpoint_len); - err = (*the_target->read_memory) (bp->pc, buf, breakpoint_len); - if (err || memcmp (buf, breakpoint_data, breakpoint_len) != 0) + buf = (unsigned char *) alloca (bp_size (bp)); + err = (*the_target->read_memory) (bp->pc, buf, bp_size (bp)); + if (err || memcmp (buf, bp_opcode (bp), bp_size (bp)) != 0) { /* Tag it as gone. */ - bp->inserted = 0; - bp->shlib_disabled = 1; + bp->inserted = -1; return 0; } @@ -1206,8 +1777,13 @@ delete_disabled_breakpoints (void) for (bp = proc->breakpoints; bp != NULL; bp = next) { next = bp->next; - if (bp->raw->shlib_disabled) - delete_breakpoint_1 (proc, bp); + if (bp->raw->inserted < 0) + { + /* If reinsert_breakpoints become disabled, that means the + manipulations (insertion and removal) of them are wrong. */ + gdb_assert (bp->type != reinsert_breakpoint); + delete_breakpoint_1 (proc, bp); + } } } @@ -1226,8 +1802,10 @@ validate_breakpoints (void) for (bp = proc->breakpoints; bp != NULL; bp = bp->next) { - if (bp->raw->inserted) - validate_inserted_breakpoint (bp->raw); + struct raw_breakpoint *raw = bp->raw; + + if (raw->raw_type == raw_bkpt_type_sw && raw->inserted > 0) + validate_inserted_breakpoint (raw); } delete_disabled_breakpoints (); @@ -1276,10 +1854,13 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) for (; bp != NULL; bp = bp->next) { - CORE_ADDR bp_end = bp->pc + breakpoint_len; + CORE_ADDR bp_end = bp->pc + bp_size (bp); CORE_ADDR start, end; int copy_offset, copy_len, buf_offset; + if (bp->raw_type != raw_bkpt_type_sw) + continue; + gdb_assert (bp->old_data >= buf + mem_len || buf >= &bp->old_data[sizeof (bp->old_data)]); @@ -1300,7 +1881,7 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) copy_offset = start - bp->pc; buf_offset = start - mem_addr; - if (bp->inserted) + if (bp->inserted > 0) { if (validate_inserted_breakpoint (bp)) memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); @@ -1362,10 +1943,13 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, for (; bp != NULL; bp = bp->next) { - CORE_ADDR bp_end = bp->pc + breakpoint_len; + CORE_ADDR bp_end = bp->pc + bp_size (bp); CORE_ADDR start, end; int copy_offset, copy_len, buf_offset; + if (bp->raw_type != raw_bkpt_type_sw) + continue; + gdb_assert (bp->old_data >= myaddr + mem_len || myaddr >= &bp->old_data[sizeof (bp->old_data)]); @@ -1387,10 +1971,10 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, buf_offset = start - mem_addr; memcpy (bp->old_data + copy_offset, myaddr + buf_offset, copy_len); - if (bp->inserted) + if (bp->inserted > 0) { if (validate_inserted_breakpoint (bp)) - memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len); + memcpy (buf + buf_offset, bp_opcode (bp) + copy_offset, copy_len); else disabled_one = 1; } @@ -1437,3 +2021,90 @@ free_all_breakpoints (struct process_info *proc) while (proc->breakpoints) delete_breakpoint_1 (proc, proc->breakpoints); } + +/* Clone an agent expression. */ + +static struct agent_expr * +clone_agent_expr (const struct agent_expr *src_ax) +{ + struct agent_expr *ax; + + ax = XCNEW (struct agent_expr); + ax->length = src_ax->length; + ax->bytes = (unsigned char *) xcalloc (ax->length, 1); + memcpy (ax->bytes, src_ax->bytes, ax->length); + return ax; +} + +/* Deep-copy the contents of one breakpoint to another. */ + +static struct breakpoint * +clone_one_breakpoint (const struct breakpoint *src) +{ + struct breakpoint *dest; + struct raw_breakpoint *dest_raw; + struct point_cond_list *current_cond; + struct point_cond_list *new_cond; + struct point_cond_list *cond_tail = NULL; + struct point_command_list *current_cmd; + struct point_command_list *new_cmd; + struct point_command_list *cmd_tail = NULL; + + /* Clone the raw breakpoint. */ + dest_raw = XCNEW (struct raw_breakpoint); + dest_raw->raw_type = src->raw->raw_type; + dest_raw->refcount = src->raw->refcount; + dest_raw->pc = src->raw->pc; + dest_raw->kind = src->raw->kind; + memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN); + dest_raw->inserted = src->raw->inserted; + + /* Clone the high-level breakpoint. */ + dest = XCNEW (struct breakpoint); + dest->type = src->type; + dest->raw = dest_raw; + dest->handler = src->handler; + + /* Clone the condition list. */ + for (current_cond = src->cond_list; current_cond != NULL; + current_cond = current_cond->next) + { + new_cond = XCNEW (struct point_cond_list); + new_cond->cond = clone_agent_expr (current_cond->cond); + APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail); + } + + /* Clone the command list. */ + for (current_cmd = src->command_list; current_cmd != NULL; + current_cmd = current_cmd->next) + { + new_cmd = XCNEW (struct point_command_list); + new_cmd->cmd = clone_agent_expr (current_cmd->cmd); + new_cmd->persistence = current_cmd->persistence; + APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail); + } + + return dest; +} + +/* Create a new breakpoint list NEW_LIST that is a copy of the + list starting at SRC_LIST. Create the corresponding new + raw_breakpoint list NEW_RAW_LIST as well. */ + +void +clone_all_breakpoints (struct breakpoint **new_list, + struct raw_breakpoint **new_raw_list, + const struct breakpoint *src_list) +{ + const struct breakpoint *bp; + struct breakpoint *new_bkpt; + struct breakpoint *bkpt_tail = NULL; + struct raw_breakpoint *raw_bkpt_tail = NULL; + + for (bp = src_list; bp != NULL; bp = bp->next) + { + new_bkpt = clone_one_breakpoint (bp); + APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail); + APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail); + } +}