diff options
-rw-r--r-- | gdb/gdbserver/ChangeLog | 58 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 54 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-low.c | 4 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.c | 322 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.h | 15 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 4 |
6 files changed, 362 insertions, 95 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 9831ef3..eb6c0ba 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,61 @@ +2010-04-01 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (linux_wait_1): Avoid setting need_step_over is + there's a GDB breakpoint at stop_pc. Always report a trap to GDB + if we could tell there's a GDB breakpoint at stop_pc. + (need_step_over_p): Don't do a step over if we find a GDB + breakpoint at the resume PC. + + * mem-break.c (struct raw_breakpoint): New. + (enum bkpt_type): New type `gdb_breakpoint'. + (struct breakpoint): Delete the `PC', `old_data' and `inserted' + fields. New field `raw'. + (find_raw_breakpoint_at): New. + (set_raw_breakpoint_at): Handle refcounting. Create a raw + breakpoint instead. + (set_breakpoint_at): Adjust. + (delete_raw_breakpoint): New. + (release_breakpoint): New. + (delete_breakpoint): Rename to... + (delete_breakpoint_1): ... this. Add proc parameter. Use + release_breakpoint. Return ENOENT. + (delete_breakpoint): Reimplement. + (find_breakpoint_at): Delete. + (find_gdb_breakpoint_at): New. + (delete_breakpoint_at): Delete. + (set_gdb_breakpoint_at): New. + (delete_gdb_breakpoint_at): New. + (gdb_breakpoint_here): New. + (set_reinsert_breakpoint): Use release_breakpoint. + (uninsert_breakpoint): Rename to ... + (uninsert_raw_breakpoint): ... this. + (uninsert_breakpoints_at): Adjust to handle raw breakpoints. + (reinsert_raw_breakpoint): Change parameter type to + raw_breakpoint. + (reinsert_breakpoints_at): Adjust to handle raw breakpoints + instead. + (check_breakpoints): Adjust. Use release_breakpoint. + (breakpoint_here): Rewrite using find_raw_breakpoint_at. + (breakpoint_inserted_here): Ditto. + (check_mem_read): Adjust to iterate over raw breakpoints instead. + Don't trust the breakpoint's shadow if it is not inserted. + (check_mem_write): Adjust to iterate over raw breakpoints instead. + (delete_all_breakpoints): Adjust. + (free_all_breakpoints): Mark all breakpoints as uninserted, and + use delete_breakpoint_1. + + * mem-break.h (breakpoints_supported): Delete declaration. + (set_gdb_breakpoint_at): Declare. + (gdb_breakpoint_here): Declare. + (delete_breakpoint_at): Delete. + (delete_gdb_breakpoint_at): Declare. + + * server.h (struct raw_breakpoint): Forward declare. + (struct process_info): New field `raw_breakpoints'. + + * linux-x86-low.c (x86_insert_point, x86_remote_point): Handle Z0 + breakpoints. + 2010-03-24 Pedro Alves <pedro@codesourcery.com> * linux-low.c (status_pending_p_callback): Fix comment. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index ad68179..84f549c 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -1740,7 +1740,8 @@ retry: if (debug_threads) fprintf (stderr, "Hit a gdbserver breakpoint.\n"); - event_child->need_step_over = 1; + if (breakpoint_here (event_child->stop_pc)) + event_child->need_step_over = 1; } } else @@ -1755,11 +1756,18 @@ retry: /* Check If GDB would be interested in this event. If GDB wanted this thread to single step, we always want to report the SIGTRAP, - and let GDB handle it. */ + and let GDB handle it. Watchpoints should always be reported. + So should signals we can't explain. A SIGTRAP we can't explain + could be a GDB breakpoint --- we may or not support Z0 + breakpoints. If we do, we're be able to handle GDB breakpoints + on top of internal breakpoints, by handling the internal + breakpoint and still reporting the event to GDB. If we don't, + we're out of luck, GDB won't see the breakpoint hit. */ report_to_gdb = (!maybe_internal_trap || event_child->last_resume_kind == resume_step || event_child->stopped_by_watchpoint - || (!step_over_finished && !bp_explains_trap)); + || (!step_over_finished && !bp_explains_trap) + || gdb_breakpoint_here (event_child->stop_pc)); /* We found no reason GDB would want us to stop. We either hit one of our own breakpoints, or finished an internal step GDB @@ -1801,6 +1809,8 @@ retry: fprintf (stderr, "GDB wanted to single-step, reporting event.\n"); if (event_child->stopped_by_watchpoint) fprintf (stderr, "Stopped by watchpoint.\n"); + if (gdb_breakpoint_here (event_child->stop_pc)) + fprintf (stderr, "Stopped by GDB breakpoint.\n"); if (debug_threads) fprintf (stderr, "Hit a non-gdbserver trap event.\n"); } @@ -2401,21 +2411,37 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy) saved_inferior = current_inferior; current_inferior = get_lwp_thread (lwp); - /* We only step over our breakpoints. */ + /* We can only step over breakpoints we know about. */ if (breakpoint_here (pc)) { - if (debug_threads) - fprintf (stderr, - "Need step over [LWP %ld]? yes, found breakpoint at 0x%s\n", - lwpid_of (lwp), paddress (pc)); + /* Don't step over a breakpoint that GDB expects to hit + though. */ + if (gdb_breakpoint_here (pc)) + { + if (debug_threads) + fprintf (stderr, + "Need step over [LWP %ld]? yes, but found" + " GDB breakpoint at 0x%s; skipping step over\n", + lwpid_of (lwp), paddress (pc)); - /* We've found an lwp that needs stepping over --- return 1 so - that find_inferior stops looking. */ - current_inferior = saved_inferior; + current_inferior = saved_inferior; + return 0; + } + else + { + if (debug_threads) + fprintf (stderr, + "Need step over [LWP %ld]? yes, found breakpoint at 0x%s\n", + lwpid_of (lwp), paddress (pc)); - /* If the step over is cancelled, this is set again. */ - lwp->need_step_over = 0; - return 1; + /* We've found an lwp that needs stepping over --- return 1 so + that find_inferior stops looking. */ + current_inferior = saved_inferior; + + /* If the step over is cancelled, this is set again. */ + lwp->need_step_over = 0; + return 1; + } } current_inferior = saved_inferior; diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index fe5d46e..1f9e1b1 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -431,6 +431,8 @@ x86_insert_point (char type, CORE_ADDR addr, int len) struct process_info *proc = current_process (); switch (type) { + case '0': + return set_gdb_breakpoint_at (addr); case '2': case '3': case '4': @@ -448,6 +450,8 @@ x86_remove_point (char type, CORE_ADDR addr, int len) struct process_info *proc = current_process (); switch (type) { + case '0': + return delete_gdb_breakpoint_at (addr); case '2': case '3': case '4': diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 33de80c..5256cb7 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -26,9 +26,53 @@ int breakpoint_len; #define MAX_BREAKPOINT_LEN 8 +/* 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 + 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 + it (e.g., to step over an internal breakpoint), so we keep an + `inserted' state associated with this low level breakpoint + structure. There can only be one such object for a given address. + Then, we have (a bit higher level) breakpoints. This structure + holds a callback to be called whenever a breakpoint is hit, a + high-level type, and a link to a low level raw breakpoint. There + can be many high-level breakpoints at the same address, and all of + them will point to the same raw breakpoint, which is reference + counted. */ + +/* The low level, physical, raw breakpoint. */ +struct raw_breakpoint +{ + struct raw_breakpoint *next; + + /* A reference count. Each high level breakpoint referencing this + raw breakpoint accounts for one reference. */ + int refcount; + + /* The breakpoint's insertion address. There can only be one raw + breakpoint for a given PC. */ + CORE_ADDR pc; + + /* The breakpoint's shadow memory. */ + unsigned char old_data[MAX_BREAKPOINT_LEN]; + + /* Non-zero if this breakpoint is currently inserted in the + inferior. */ + int inserted; +}; + /* The type of a breakpoint. */ enum bkpt_type { + /* A GDB breakpoint, requested with a Z0 packet. */ + gdb_breakpoint, + /* A basic-software-single-step breakpoint. */ reinsert_breakpoint, @@ -37,38 +81,57 @@ enum bkpt_type other_breakpoint, }; +/* A high level (in gdbserver's perspective) breakpoint. */ struct breakpoint { struct breakpoint *next; - CORE_ADDR pc; - unsigned char old_data[MAX_BREAKPOINT_LEN]; - - /* Non-zero if this breakpoint is currently inserted in the - inferior. */ - int inserted; /* The breakpoint's type. */ enum bkpt_type type; + /* Link to this breakpoint's raw breakpoint. This is always + non-NULL. */ + struct raw_breakpoint *raw; + /* Function to call when we hit this breakpoint. If it returns 1, - the breakpoint shall be deleted; 0, it will be left inserted. */ + the breakpoint shall be deleted; 0 or if this callback is NULL, + it will be left inserted. */ int (*handler) (CORE_ADDR); }; -static void uninsert_breakpoint (struct breakpoint *bp); +static struct raw_breakpoint * +find_raw_breakpoint_at (CORE_ADDR where) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; -static struct breakpoint * + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (bp->pc == where) + return bp; + + return NULL; +} + +static struct raw_breakpoint * set_raw_breakpoint_at (CORE_ADDR where) { struct process_info *proc = current_process (); - struct breakpoint *bp; + struct raw_breakpoint *bp; int err; if (breakpoint_data == NULL) error ("Target does not support breakpoints."); + bp = find_raw_breakpoint_at (where); + if (bp != NULL) + { + bp->refcount++; + return bp; + } + bp = xcalloc (1, sizeof (*bp)); bp->pc = where; + bp->refcount = 1; err = (*the_target->read_memory) (where, bp->old_data, breakpoint_len); @@ -97,8 +160,8 @@ set_raw_breakpoint_at (CORE_ADDR where) /* Link the breakpoint in. */ bp->inserted = 1; - bp->next = proc->breakpoints; - proc->breakpoints = bp; + bp->next = proc->raw_breakpoints; + proc->raw_breakpoints = bp; return bp; } @@ -107,10 +170,11 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) { struct process_info *proc = current_process (); struct breakpoint *bp; + struct raw_breakpoint *raw; - bp = set_raw_breakpoint_at (where); + raw = set_raw_breakpoint_at (where); - if (bp == NULL) + if (raw == NULL) { /* warn? */ return NULL; @@ -118,6 +182,8 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) bp = xcalloc (1, sizeof (struct breakpoint)); bp->type = other_breakpoint; + + bp->raw = raw; bp->handler = handler; bp->next = proc->breakpoints; @@ -126,11 +192,84 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) return bp; } -static void -delete_breakpoint (struct breakpoint *todel) +static int +delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel) +{ + struct raw_breakpoint *bp, **bp_link; + int ret; + + bp = proc->raw_breakpoints; + bp_link = &proc->raw_breakpoints; + + while (bp) + { + if (bp == todel) + { + if (bp->inserted) + { + struct raw_breakpoint *prev_bp_link = *bp_link; + + *bp_link = bp->next; + + ret = (*the_target->write_memory) (bp->pc, bp->old_data, + breakpoint_len); + 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)); + return ret; + } + + } + else + *bp_link = bp->next; + + free (bp); + return 0; + } + else + { + bp_link = &bp->next; + bp = *bp_link; + } + } + + warning ("Could not find raw breakpoint in list."); + return ENOENT; +} + +static int +release_breakpoint (struct process_info *proc, struct breakpoint *bp) +{ + int newrefcount; + int ret; + + newrefcount = bp->raw->refcount - 1; + if (newrefcount == 0) + { + ret = delete_raw_breakpoint (proc, bp->raw); + if (ret != 0) + return ret; + } + else + bp->raw->refcount = newrefcount; + + free (bp); + + return 0; +} + +static int +delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel) { - struct process_info *proc = current_process (); struct breakpoint *bp, **bp_link; + int err; bp = proc->breakpoints; bp_link = &proc->breakpoints; @@ -141,9 +280,12 @@ delete_breakpoint (struct breakpoint *todel) { *bp_link = bp->next; - uninsert_breakpoint (bp); - free (bp); - return; + err = release_breakpoint (proc, bp); + if (err != 0) + return err; + + bp = *bp_link; + return 0; } else { @@ -153,30 +295,71 @@ delete_breakpoint (struct breakpoint *todel) } warning ("Could not find breakpoint in list."); + return ENOENT; +} + +static int +delete_breakpoint (struct breakpoint *todel) +{ + struct process_info *proc = current_process (); + return delete_breakpoint_1 (proc, todel); } static struct breakpoint * -find_breakpoint_at (CORE_ADDR where) +find_gdb_breakpoint_at (CORE_ADDR where) { struct process_info *proc = current_process (); - struct breakpoint *bp = proc->breakpoints; + struct breakpoint *bp; - while (bp != NULL) - { - if (bp->pc == where) - return bp; - bp = bp->next; - } + for (bp = proc->breakpoints; bp != NULL; bp = bp->next) + if (bp->type == gdb_breakpoint && bp->raw->pc == where) + return bp; return NULL; } -void -delete_breakpoint_at (CORE_ADDR addr) +int +set_gdb_breakpoint_at (CORE_ADDR where) { - struct breakpoint *bp = find_breakpoint_at (addr); - if (bp != NULL) - delete_breakpoint (bp); + struct breakpoint *bp; + + if (breakpoint_data == NULL) + return 1; + + bp = set_breakpoint_at (where, NULL); + if (bp == NULL) + return -1; + + bp->type = gdb_breakpoint; + return 0; +} + +int +delete_gdb_breakpoint_at (CORE_ADDR addr) +{ + struct breakpoint *bp; + int err; + + if (breakpoint_data == NULL) + return 1; + + bp = find_gdb_breakpoint_at (addr); + if (bp == NULL) + return -1; + + err = delete_breakpoint (bp); + if (err) + return -1; + + return 0; +} + +int +gdb_breakpoint_here (CORE_ADDR where) +{ + struct breakpoint *bp = find_gdb_breakpoint_at (where); + + return (bp != NULL); } void @@ -185,7 +368,6 @@ set_reinsert_breakpoint (CORE_ADDR stop_at) struct breakpoint *bp; bp = set_breakpoint_at (stop_at, NULL); - bp->type = reinsert_breakpoint; } @@ -203,13 +385,7 @@ delete_reinsert_breakpoints (void) if (bp->type == reinsert_breakpoint) { *bp_link = bp->next; - - /* If something goes wrong, maybe this is a shared library - breakpoint, and the shared library has been unmapped. - Assume the breakpoint is gone anyway. */ - uninsert_breakpoint (bp); - free (bp); - + release_breakpoint (proc, bp); bp = *bp_link; } else @@ -221,7 +397,7 @@ delete_reinsert_breakpoints (void) } static void -uninsert_breakpoint (struct breakpoint *bp) +uninsert_raw_breakpoint (struct raw_breakpoint *bp) { if (bp->inserted) { @@ -245,9 +421,9 @@ uninsert_breakpoint (struct breakpoint *bp) void uninsert_breakpoints_at (CORE_ADDR pc) { - struct breakpoint *bp; + struct raw_breakpoint *bp; - bp = find_breakpoint_at (pc); + bp = find_raw_breakpoint_at (pc); if (bp == NULL) { /* This can happen when we remove all breakpoints while handling @@ -261,11 +437,11 @@ uninsert_breakpoints_at (CORE_ADDR pc) } if (bp->inserted) - uninsert_breakpoint (bp); + uninsert_raw_breakpoint (bp); } static void -reinsert_raw_breakpoint (struct breakpoint *bp) +reinsert_raw_breakpoint (struct raw_breakpoint *bp) { int err; @@ -285,16 +461,16 @@ reinsert_raw_breakpoint (struct breakpoint *bp) void reinsert_breakpoints_at (CORE_ADDR pc) { - struct breakpoint *bp; + struct raw_breakpoint *bp; - bp = find_breakpoint_at (pc); + bp = find_raw_breakpoint_at (pc); if (bp == NULL) { /* 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 " + "Could not find raw breakpoint at 0x%s " "in list (reinserting).\n", paddress (pc)); return; @@ -314,9 +490,9 @@ check_breakpoints (CORE_ADDR stop_pc) while (bp) { - if (bp->pc == stop_pc) + if (bp->raw->pc == stop_pc) { - if (!bp->inserted) + if (!bp->raw->inserted) { warning ("Hit a removed breakpoint?"); return; @@ -326,7 +502,7 @@ check_breakpoints (CORE_ADDR stop_pc) { *bp_link = bp->next; - delete_breakpoint (bp); + release_breakpoint (proc, bp); bp = *bp_link; continue; @@ -348,34 +524,24 @@ set_breakpoint_data (const unsigned char *bp_data, int bp_len) int breakpoint_here (CORE_ADDR addr) { - struct process_info *proc = current_process (); - struct breakpoint *bp; - - for (bp = proc->breakpoints; bp != NULL; bp = bp->next) - if (bp->pc == addr) - return 1; - - return 0; + return (find_raw_breakpoint_at (addr) != NULL); } int breakpoint_inserted_here (CORE_ADDR addr) { - struct process_info *proc = current_process (); - struct breakpoint *bp; + struct raw_breakpoint *bp; - for (bp = proc->breakpoints; bp != NULL; bp = bp->next) - if (bp->pc == addr && bp->inserted) - return 1; + bp = find_raw_breakpoint_at (addr); - return 0; + return (bp != NULL && bp->inserted); } void check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) { struct process_info *proc = current_process (); - struct breakpoint *bp = proc->breakpoints; + struct raw_breakpoint *bp = proc->raw_breakpoints; CORE_ADDR mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) @@ -401,7 +567,8 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) copy_offset = start - bp->pc; buf_offset = start - mem_addr; - memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); + if (bp->inserted) + memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); } } @@ -409,7 +576,7 @@ void check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) { struct process_info *proc = current_process (); - struct breakpoint *bp = proc->breakpoints; + struct raw_breakpoint *bp = proc->raw_breakpoints; CORE_ADDR mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) @@ -449,7 +616,7 @@ delete_all_breakpoints (void) struct process_info *proc = current_process (); while (proc->breakpoints) - delete_breakpoint (proc->breakpoints); + delete_breakpoint_1 (proc, proc->breakpoints); } /* Release all breakpoints, but do not try to un-insert them from the @@ -458,12 +625,15 @@ delete_all_breakpoints (void) void free_all_breakpoints (struct process_info *proc) { - struct breakpoint *bp; + struct raw_breakpoint *raw_bp; + for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next) + raw_bp->inserted = 0; + + /* Note: use PROC explicitly instead of deferring to + delete_all_breakpoints --- CURRENT_INFERIOR may already have been + released when we get here. There should be no call to + current_process from here on. */ while (proc->breakpoints) - { - bp = proc->breakpoints; - proc->breakpoints = bp->next; - free (bp); - } + delete_breakpoint_1 (proc, proc->breakpoints); } diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index 2953d03..01087fd 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -25,9 +25,10 @@ /* Breakpoints are opaque. */ struct breakpoint; -/* Returns TRUE if breakpoints are supported on this target. */ +/* Create a new GDB breakpoint at WHERE. Returns -1 if breakpoints + are not supported on this target, 0 otherwise. */ -int breakpoints_supported (void); +int set_gdb_breakpoint_at (CORE_ADDR where); /* Returns TRUE if there's any breakpoint at ADDR in our tables, inserted, or not. */ @@ -38,6 +39,10 @@ int breakpoint_here (CORE_ADDR addr); int breakpoint_inserted_here (CORE_ADDR addr); +/* Returns TRUE if there's a GDB breakpoint set at ADDR. */ + +int gdb_breakpoint_here (CORE_ADDR where); + /* Create a new breakpoint at WHERE, and call HANDLER when it is hit. HANDLER should return 1 if the breakpoint should be deleted, 0 otherwise. */ @@ -45,10 +50,10 @@ int breakpoint_inserted_here (CORE_ADDR addr); struct breakpoint *set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)); -/* Delete a breakpoint previously inserted at ADDR with - set_breakpoint_at. */ +/* Delete a GDB breakpoint previously inserted at ADDR with + set_gdb_breakpoint_at. */ -void delete_breakpoint_at (CORE_ADDR addr); +int delete_gdb_breakpoint_at (CORE_ADDR addr); /* Set a reinsert breakpoint at STOP_AT. */ diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 54d80ed..88c6c5e 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -191,6 +191,7 @@ struct dll_info struct sym_cache; struct breakpoint; +struct raw_breakpoint; struct process_info_private; struct process_info @@ -209,6 +210,9 @@ struct process_info /* The list of memory breakpoints. */ struct breakpoint *breakpoints; + /* The list of raw memory breakpoints. */ + struct raw_breakpoint *raw_breakpoints; + /* Private target data. */ struct process_info_private *private; }; |