| /* Memory breakpoint operations for the remote server for GDB. |
| Copyright (C) 2002-2022 Free Software Foundation, Inc. |
| |
| Contributed by MontaVista Software. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "server.h" |
| #include "regcache.h" |
| #include "ax.h" |
| |
| #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. 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 |
| 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; |
| |
| /* 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; |
| |
| /* The breakpoint's insertion address. There can only be one raw |
| 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]; |
| |
| /* 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; |
| }; |
| |
| /* The type of a breakpoint. */ |
| enum bkpt_type |
| { |
| /* A GDB breakpoint, requested with a Z0 packet. */ |
| 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 software single-step breakpoint. */ |
| single_step_breakpoint, |
| |
| /* Any other breakpoint type that doesn't require specific |
| treatment goes here. E.g., an event breakpoint. */ |
| other_breakpoint, |
| }; |
| |
| struct point_cond_list |
| { |
| /* Pointer to the agent expression that is the breakpoint's |
| conditional. */ |
| struct agent_expr *cond; |
| |
| /* Pointer to the next condition. */ |
| struct point_cond_list *next; |
| }; |
| |
| struct point_command_list |
| { |
| /* Pointer to the agent expression that is the breakpoint's |
| commands. */ |
| struct agent_expr *cmd; |
| |
| /* Flag that is true if this command should run even while GDB is |
| disconnected. */ |
| int persistence; |
| |
| /* Pointer to the next command. */ |
| struct point_command_list *next; |
| }; |
| |
| /* A high level (in gdbserver's perspective) breakpoint. */ |
| struct breakpoint |
| { |
| struct breakpoint *next; |
| |
| /* The breakpoint's type. */ |
| enum bkpt_type type; |
| |
| /* Link to this breakpoint's raw breakpoint. This is always |
| non-NULL. */ |
| struct raw_breakpoint *raw; |
| }; |
| |
| /* Breakpoint requested by GDB. */ |
| |
| struct gdb_breakpoint |
| { |
| struct breakpoint base; |
| |
| /* Pointer to the condition list that should be evaluated on |
| the target or NULL if the breakpoint is unconditional or |
| if GDB doesn't want us to evaluate the conditionals on the |
| target's side. */ |
| struct point_cond_list *cond_list; |
| |
| /* Point to the list of commands to run when this is hit. */ |
| struct point_command_list *command_list; |
| }; |
| |
| /* Breakpoint used by GDBserver. */ |
| |
| struct other_breakpoint |
| { |
| struct breakpoint base; |
| |
| /* Function to call when we hit this breakpoint. If it returns 1, |
| the breakpoint shall be deleted; 0 or if this callback is NULL, |
| it will be left inserted. */ |
| int (*handler) (CORE_ADDR); |
| }; |
| |
| /* Breakpoint for single step. */ |
| |
| struct single_step_breakpoint |
| { |
| struct breakpoint base; |
| |
| /* Thread the reinsert breakpoint belongs to. */ |
| ptid_t ptid; |
| }; |
| |
| /* 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."); |
| } |
| } |
| |
| /* Return true if breakpoint TYPE is a GDB breakpoint. */ |
| |
| static int |
| is_gdb_breakpoint (enum bkpt_type type) |
| { |
| return (type == gdb_breakpoint_Z0 |
| || type == gdb_breakpoint_Z1 |
| || type == gdb_breakpoint_Z2 |
| || type == gdb_breakpoint_Z3 |
| || type == gdb_breakpoint_Z4); |
| } |
| |
| bool |
| any_persistent_commands (process_info *proc) |
| { |
| struct breakpoint *bp; |
| struct point_command_list *cl; |
| |
| for (bp = proc->breakpoints; bp != NULL; bp = bp->next) |
| { |
| if (is_gdb_breakpoint (bp->type)) |
| { |
| struct gdb_breakpoint *gdb_bp = (struct gdb_breakpoint *) bp; |
| |
| for (cl = gdb_bp->command_list; cl != NULL; cl = cl->next) |
| if (cl->persistence) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /* 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_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 == 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 * |
| find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int kind) |
| { |
| struct process_info *proc = current_process (); |
| struct raw_breakpoint *bp; |
| |
| for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) |
| if (bp->pc == addr && bp->raw_type == type && bp->kind == kind) |
| return bp; |
| |
| 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 (bp->pc, buf, bp_size (bp)); |
| if (err != 0) |
| { |
| threads_debug_printf ("Failed to read shadow memory of" |
| " breakpoint at 0x%s (%s).", |
| paddress (bp->pc), safe_strerror (err)); |
| } |
| 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) |
| threads_debug_printf ("Failed to insert breakpoint at 0x%s (%s).", |
| paddress (bp->pc), safe_strerror (err)); |
| } |
| return err != 0 ? -1 : 0; |
| } |
| |
| /* See mem-break.h */ |
| |
| 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 `target_write_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 |
| target_write_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 = target_write_memory (bp->pc, buf, bp_size (bp)); |
| if (err != 0) |
| threads_debug_printf ("Failed to uninsert raw breakpoint " |
| "at 0x%s (%s) while deleting it.", |
| paddress (bp->pc), safe_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; |
| |
| 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. */ |
| threads_debug_printf |
| ("Inconsistent breakpoint kind? Was %d, now %d.", |
| bp->kind, kind); |
| bp->inserted = -1; |
| bp = NULL; |
| } |
| } |
| else |
| bp = find_raw_breakpoint_at (where, type, kind); |
| |
| gdb::unique_xmalloc_ptr<struct raw_breakpoint> bp_holder; |
| if (bp == NULL) |
| { |
| bp_holder.reset (XCNEW (struct raw_breakpoint)); |
| bp = bp_holder.get (); |
| bp->pc = where; |
| bp->kind = kind; |
| bp->raw_type = type; |
| } |
| |
| if (!bp->inserted) |
| { |
| *err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp); |
| if (*err != 0) |
| { |
| threads_debug_printf ("Failed to insert breakpoint at 0x%s (%d).", |
| paddress (where), *err); |
| |
| return NULL; |
| } |
| |
| bp->inserted = 1; |
| } |
| |
| /* If the breakpoint was allocated above, we know we want to keep it |
| now. */ |
| bp_holder.release (); |
| |
| /* 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; |
| } |
| |
| /* Notice that breakpoint traps are always installed on top of fast |
| tracepoint jumps. This is even if the fast tracepoint is installed |
| at a later time compared to when the breakpoint was installed. |
| This means that a stopping breakpoint or tracepoint has higher |
| "priority". In turn, this allows having fast and slow tracepoints |
| (and breakpoints) at the same address behave correctly. */ |
| |
| |
| /* A fast tracepoint jump. */ |
| |
| struct fast_tracepoint_jump |
| { |
| struct fast_tracepoint_jump *next; |
| |
| /* A reference count. GDB can install more than one fast tracepoint |
| at the same address (each with its own action list, for |
| example). */ |
| int refcount; |
| |
| /* The fast tracepoint's insertion address. There can only be one |
| of these for a given PC. */ |
| CORE_ADDR pc; |
| |
| /* Non-zero if this fast tracepoint jump is currently inserted in |
| the inferior. */ |
| int inserted; |
| |
| /* The length of the jump instruction. */ |
| int length; |
| |
| /* A poor-man's flexible array member, holding both the jump |
| instruction to insert, and a copy of the instruction that would |
| be in memory had not been a jump there (the shadow memory of the |
| tracepoint jump). */ |
| unsigned char insn_and_shadow[0]; |
| }; |
| |
| /* Fast tracepoint FP's jump instruction to insert. */ |
| #define fast_tracepoint_jump_insn(fp) \ |
| ((fp)->insn_and_shadow + 0) |
| |
| /* The shadow memory of fast tracepoint jump FP. */ |
| #define fast_tracepoint_jump_shadow(fp) \ |
| ((fp)->insn_and_shadow + (fp)->length) |
| |
| |
| /* Return the fast tracepoint jump set at WHERE. */ |
| |
| static struct fast_tracepoint_jump * |
| find_fast_tracepoint_jump_at (CORE_ADDR where) |
| { |
| struct process_info *proc = current_process (); |
| struct fast_tracepoint_jump *jp; |
| |
| for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next) |
| if (jp->pc == where) |
| return jp; |
| |
| return NULL; |
| } |
| |
| int |
| fast_tracepoint_jump_here (CORE_ADDR where) |
| { |
| struct fast_tracepoint_jump *jp = find_fast_tracepoint_jump_at (where); |
| |
| return (jp != NULL); |
| } |
| |
| int |
| delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel) |
| { |
| struct fast_tracepoint_jump *bp, **bp_link; |
| int ret; |
| struct process_info *proc = current_process (); |
| |
| bp = proc->fast_tracepoint_jumps; |
| bp_link = &proc->fast_tracepoint_jumps; |
| |
| while (bp) |
| { |
| if (bp == todel) |
| { |
| if (--bp->refcount == 0) |
| { |
| struct fast_tracepoint_jump *prev_bp_link = *bp_link; |
| unsigned char *buf; |
| |
| /* Unlink it. */ |
| *bp_link = bp->next; |
| |
| /* Since there can be breakpoints inserted in the same |
| address range, we use `target_write_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 |
| target_write_memory updates any shadow memory with |
| what we pass here, and we want that to be a nop. */ |
| buf = (unsigned char *) alloca (bp->length); |
| memcpy (buf, fast_tracepoint_jump_shadow (bp), bp->length); |
| ret = target_write_memory (bp->pc, buf, bp->length); |
| if (ret != 0) |
| { |
| /* Something went wrong, relink the jump. */ |
| *bp_link = prev_bp_link; |
| |
| threads_debug_printf |
| ("Failed to uninsert fast tracepoint jump " |
| "at 0x%s (%s) while deleting it.", |
| paddress (bp->pc), safe_strerror (ret)); |
| return ret; |
| } |
| |
| free (bp); |
| } |
| |
| return 0; |
| } |
| else |
| { |
| bp_link = &bp->next; |
| bp = *bp_link; |
| } |
| } |
| |
| warning ("Could not find fast tracepoint jump in list."); |
| return ENOENT; |
| } |
| |
| void |
| inc_ref_fast_tracepoint_jump (struct fast_tracepoint_jump *jp) |
| { |
| jp->refcount++; |
| } |
| |
| struct fast_tracepoint_jump * |
| set_fast_tracepoint_jump (CORE_ADDR where, |
| unsigned char *insn, ULONGEST length) |
| { |
| struct process_info *proc = current_process (); |
| struct fast_tracepoint_jump *jp; |
| int err; |
| unsigned char *buf; |
| |
| /* We refcount fast tracepoint jumps. Check if we already know |
| about a jump at this address. */ |
| jp = find_fast_tracepoint_jump_at (where); |
| if (jp != NULL) |
| { |
| jp->refcount++; |
| return jp; |
| } |
| |
| /* 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 = (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 = (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 |
| `read_inferior_memory', which masks out breakpoints. */ |
| err = read_inferior_memory (where, buf, length); |
| if (err != 0) |
| { |
| threads_debug_printf ("Failed to read shadow memory of" |
| " fast tracepoint at 0x%s (%s).", |
| paddress (where), safe_strerror (err)); |
| free (jp); |
| return NULL; |
| } |
| memcpy (fast_tracepoint_jump_shadow (jp), buf, length); |
| |
| /* Link the jump in. */ |
| jp->inserted = 1; |
| jp->next = proc->fast_tracepoint_jumps; |
| proc->fast_tracepoint_jumps = jp; |
| |
| /* Since there can be trap breakpoints inserted in the same address |
| range, we use use `target_write_memory', which takes care of |
| layering breakpoints on top of fast tracepoints, on top of the |
| buffer we pass it. This works because we've already linked in |
| the fast tracepoint jump above. Also note that we need to pass |
| the current shadow contents, because target_write_memory |
| updates any shadow memory with what we pass here, and we want |
| that to be a nop. */ |
| err = target_write_memory (where, buf, length); |
| if (err != 0) |
| { |
| threads_debug_printf |
| ("Failed to insert fast tracepoint jump at 0x%s (%s).", |
| paddress (where), safe_strerror (err)); |
| |
| /* Unlink it. */ |
| proc->fast_tracepoint_jumps = jp->next; |
| free (jp); |
| |
| return NULL; |
| } |
| |
| return jp; |
| } |
| |
| void |
| uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc) |
| { |
| struct fast_tracepoint_jump *jp; |
| int err; |
| |
| jp = find_fast_tracepoint_jump_at (pc); |
| if (jp == NULL) |
| { |
| /* This can happen when we remove all breakpoints while handling |
| a step-over. */ |
| threads_debug_printf ("Could not find fast tracepoint jump at 0x%s " |
| "in list (uninserting).", |
| paddress (pc)); |
| return; |
| } |
| |
| if (jp->inserted) |
| { |
| unsigned char *buf; |
| |
| jp->inserted = 0; |
| |
| /* Since there can be trap breakpoints inserted in the same |
| address range, we use use `target_write_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 marked the fast tracepoint fast |
| tracepoint jump uninserted above. Also note that we need to |
| pass the current shadow contents, because |
| target_write_memory updates any shadow memory with what we |
| pass here, and we want that to be a nop. */ |
| buf = (unsigned char *) alloca (jp->length); |
| memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length); |
| err = target_write_memory (jp->pc, buf, jp->length); |
| if (err != 0) |
| { |
| jp->inserted = 1; |
| |
| threads_debug_printf ("Failed to uninsert fast tracepoint jump at" |
| " 0x%s (%s).", |
| paddress (pc), safe_strerror (err)); |
| } |
| } |
| } |
| |
| void |
| reinsert_fast_tracepoint_jumps_at (CORE_ADDR where) |
| { |
| struct fast_tracepoint_jump *jp; |
| int err; |
| unsigned char *buf; |
| |
| jp = find_fast_tracepoint_jump_at (where); |
| if (jp == NULL) |
| { |
| /* This can happen when we remove breakpoints when a tracepoint |
| hit causes a tracing stop, while handling a step-over. */ |
| threads_debug_printf ("Could not find fast tracepoint jump at 0x%s " |
| "in list (reinserting).", |
| paddress (where)); |
| return; |
| } |
| |
| if (jp->inserted) |
| error ("Jump already inserted at reinsert time."); |
| |
| jp->inserted = 1; |
| |
| /* Since there can be trap breakpoints inserted in the same address |
| range, we use `target_write_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 marked |
| the fast tracepoint jump inserted above. Also note that we need |
| to pass the current shadow contents, because |
| target_write_memory updates any shadow memory with what we pass |
| here, and we want that to be a nop. */ |
| buf = (unsigned char *) alloca (jp->length); |
| memcpy (buf, fast_tracepoint_jump_shadow (jp), jp->length); |
| err = target_write_memory (where, buf, jp->length); |
| if (err != 0) |
| { |
| jp->inserted = 0; |
| |
| threads_debug_printf ("Failed to reinsert fast tracepoint jump at" |
| " 0x%s (%s).", |
| paddress (where), safe_strerror (err)); |
| } |
| } |
| |
| /* 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 (raw_type, where, kind, err); |
| |
| if (raw == NULL) |
| { |
| /* warn? */ |
| return NULL; |
| } |
| |
| if (is_gdb_breakpoint (type)) |
| { |
| struct gdb_breakpoint *gdb_bp = XCNEW (struct gdb_breakpoint); |
| |
| bp = (struct breakpoint *) gdb_bp; |
| gdb_assert (handler == NULL); |
| } |
| else if (type == other_breakpoint) |
| { |
| struct other_breakpoint *other_bp = XCNEW (struct other_breakpoint); |
| |
| other_bp->handler = handler; |
| bp = (struct breakpoint *) other_bp; |
| } |
| else if (type == single_step_breakpoint) |
| { |
| struct single_step_breakpoint *ss_bp |
| = XCNEW (struct single_step_breakpoint); |
| |
| bp = (struct breakpoint *) ss_bp; |
| } |
| else |
| gdb_assert_not_reached ("unhandled breakpoint type"); |
| |
| bp->type = type; |
| bp->raw = raw; |
| |
| bp->next = proc->breakpoints; |
| proc->breakpoints = bp; |
| |
| return bp; |
| } |
| |
| /* Set breakpoint of TYPE on address WHERE with handler HANDLER. */ |
| |
| static struct breakpoint * |
| set_breakpoint_type_at (enum bkpt_type type, 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 (type, raw_bkpt_type_sw, |
| placed_address, breakpoint_kind, handler, |
| &err_ignored); |
| } |
| |
| /* See mem-break.h */ |
| |
| struct breakpoint * |
| set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) |
| { |
| return set_breakpoint_type_at (other_breakpoint, where, handler); |
| } |
| |
| |
| 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 > 0) |
| { |
| struct raw_breakpoint *prev_bp_link = *bp_link; |
| |
| *bp_link = bp->next; |
| |
| 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; |
| |
| threads_debug_printf ("Failed to uninsert raw breakpoint " |
| "at 0x%s while deleting it.", |
| paddress (bp->pc)); |
| 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 breakpoint *bp, **bp_link; |
| int err; |
| |
| bp = proc->breakpoints; |
| bp_link = &proc->breakpoints; |
| |
| while (bp) |
| { |
| if (bp == todel) |
| { |
| *bp_link = bp->next; |
| |
| err = release_breakpoint (proc, bp); |
| if (err != 0) |
| return err; |
| |
| bp = *bp_link; |
| return 0; |
| } |
| else |
| { |
| bp_link = &bp->next; |
| bp = *bp_link; |
| } |
| } |
| |
| warning ("Could not find breakpoint in list."); |
| return ENOENT; |
| } |
| |
| int |
| delete_breakpoint (struct breakpoint *todel) |
| { |
| struct process_info *proc = current_process (); |
| return delete_breakpoint_1 (proc, todel); |
| } |
| |
| /* 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 gdb_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 == type && bp->raw->pc == addr |
| && (kind == -1 || bp->raw->kind == kind)) |
| return (struct gdb_breakpoint *) bp; |
| |
| return NULL; |
| } |
| |
| static int |
| z_type_supported (char z_type) |
| { |
| return (z_type >= '0' && z_type <= '4' |
| && 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. */ |
| |
| struct gdb_breakpoint * |
| set_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind, int *err) |
| { |
| struct gdb_breakpoint *bp; |
| enum bkpt_type type; |
| enum raw_bkpt_type raw_type; |
| |
| if (!z_type_supported (z_type)) |
| { |
| *err = 1; |
| return nullptr; |
| } |
| |
| /* 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 (bp != NULL) |
| { |
| if (bp->base.raw->kind != kind) |
| { |
| /* A different kind than previously seen. The previous |
| breakpoint must be gone then. */ |
| bp->base.raw->inserted = -1; |
| delete_breakpoint ((struct 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 (bp != NULL) |
| { |
| /* 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 (struct gdb_breakpoint *) set_breakpoint (type, raw_type, addr, |
| kind, NULL, err); |
| } |
| |
| /* 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. */ |
| |
| int |
| delete_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind) |
| { |
| if (!z_type_supported (z_type)) |
| return 1; |
| |
| gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, kind); |
| if (bp == NULL) |
| return -1; |
| |
| /* Before deleting the breakpoint, make sure to free its condition |
| and command lists. */ |
| clear_breakpoint_conditions_and_commands (bp); |
| int err = delete_breakpoint ((struct breakpoint *) bp); |
| if (err != 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* Clear all conditions associated with a breakpoint. */ |
| |
| static void |
| clear_breakpoint_conditions (struct gdb_breakpoint *bp) |
| { |
| struct point_cond_list *cond; |
| |
| if (bp->cond_list == NULL) |
| return; |
| |
| cond = bp->cond_list; |
| |
| while (cond != NULL) |
| { |
| struct point_cond_list *cond_next; |
| |
| cond_next = cond->next; |
| gdb_free_agent_expr (cond->cond); |
| free (cond); |
| cond = cond_next; |
| } |
| |
| bp->cond_list = NULL; |
| } |
| |
| /* Clear all commands associated with a breakpoint. */ |
| |
| static void |
| clear_breakpoint_commands (struct gdb_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 gdb_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 gdb_breakpoint *bp, |
| struct agent_expr *condition) |
| { |
| struct point_cond_list *new_cond; |
| |
| /* Create new condition. */ |
| new_cond = XCNEW (struct point_cond_list); |
| new_cond->cond = condition; |
| |
| /* Add condition to the list. */ |
| new_cond->next = bp->cond_list; |
| bp->cond_list = new_cond; |
| } |
| |
| /* Add a target-side condition CONDITION to a breakpoint. */ |
| |
| int |
| add_breakpoint_condition (struct gdb_breakpoint *bp, const char **condition) |
| { |
| const char *actparm = *condition; |
| struct agent_expr *cond; |
| |
| if (condition == NULL) |
| return 1; |
| |
| if (bp == NULL) |
| return 0; |
| |
| cond = gdb_parse_agent_expr (&actparm); |
| |
| if (cond == NULL) |
| { |
| warning ("Condition evaluation failed. Assuming unconditional."); |
| return 0; |
| } |
| |
| add_condition_to_breakpoint (bp, cond); |
| |
| *condition = actparm; |
| |
| return 1; |
| } |
| |
| /* Evaluate condition (if any) at breakpoint BP. Return 1 if |
| true and 0 otherwise. */ |
| |
| static int |
| gdb_condition_true_at_breakpoint_z_type (char z_type, CORE_ADDR addr) |
| { |
| /* Fetch registers for the current inferior. */ |
| struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); |
| ULONGEST value = 0; |
| struct point_cond_list *cl; |
| int err = 0; |
| struct eval_agent_expr_context ctx; |
| |
| if (bp == NULL) |
| return 0; |
| |
| /* Check if the breakpoint is unconditional. If it is, |
| the condition always evaluates to TRUE. */ |
| 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. |
| |
| If we failed to evaluate the expression, TRUE is returned. This |
| forces GDB to reevaluate the conditions. */ |
| for (cl = bp->cond_list; |
| cl && !value && !err; cl = cl->next) |
| { |
| /* Evaluate the condition. */ |
| err = gdb_eval_agent_expr (&ctx, cl->cond, &value); |
| } |
| |
| if (err) |
| return 1; |
| |
| 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. */ |
| |
| static void |
| add_commands_to_breakpoint (struct gdb_breakpoint *bp, |
| struct agent_expr *commands, int persist) |
| { |
| struct point_command_list *new_cmd; |
| |
| /* Create new command. */ |
| new_cmd = XCNEW (struct point_command_list); |
| new_cmd->cmd = commands; |
| new_cmd->persistence = persist; |
| |
| /* Add commands to the list. */ |
| new_cmd->next = bp->command_list; |
| bp->command_list = new_cmd; |
| } |
| |
| /* Add a target-side command COMMAND to the breakpoint at ADDR. */ |
| |
| int |
| add_breakpoint_commands (struct gdb_breakpoint *bp, const char **command, |
| int persist) |
| { |
| const char *actparm = *command; |
| struct agent_expr *cmd; |
| |
| if (command == NULL) |
| return 1; |
| |
| if (bp == NULL) |
| return 0; |
| |
| cmd = gdb_parse_agent_expr (&actparm); |
| |
| if (cmd == NULL) |
| { |
| warning ("Command evaluation failed. Disabling."); |
| return 0; |
| } |
| |
| add_commands_to_breakpoint (bp, cmd, persist); |
| |
| *command = actparm; |
| |
| return 1; |
| } |
| |
| /* Return true if there are no commands to run at this location, |
| which likely means we want to report back to GDB. */ |
| |
| static int |
| gdb_no_commands_at_breakpoint_z_type (char z_type, CORE_ADDR addr) |
| { |
| struct gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); |
| |
| if (bp == NULL) |
| return 1; |
| |
| threads_debug_printf ("at 0x%s, type Z%c, bp command_list is 0x%s", |
| paddress (addr), z_type, |
| phex_nz ((uintptr_t) bp->command_list, 0)); |
| return (bp->command_list == NULL); |
| } |
| |
| /* 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 gdb_breakpoint *bp = find_gdb_breakpoint (z_type, addr, -1); |
| ULONGEST value = 0; |
| struct point_command_list *cl; |
| int err = 0; |
| struct eval_agent_expr_context ctx; |
| |
| if (bp == NULL) |
| 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 (&ctx, cl->cmd, &value); |
| |
| /* If one command has a problem, stop digging the hole deeper. */ |
| if (err) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| 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) |
| { |
| /* 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 |
| set_single_step_breakpoint (CORE_ADDR stop_at, ptid_t ptid) |
| { |
| struct single_step_breakpoint *bp; |
| |
| gdb_assert (current_ptid.pid () == ptid.pid ()); |
| |
| bp = (struct single_step_breakpoint *) set_breakpoint_type_at (single_step_breakpoint, |
| stop_at, NULL); |
| bp->ptid = ptid; |
| } |
| |
| void |
| delete_single_step_breakpoints (struct thread_info *thread) |
| { |
| struct process_info *proc = get_thread_process (thread); |
| struct breakpoint *bp, **bp_link; |
| |
| bp = proc->breakpoints; |
| bp_link = &proc->breakpoints; |
| |
| while (bp) |
| { |
| if (bp->type == single_step_breakpoint |
| && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread)) |
| { |
| scoped_restore_current_thread restore_thread; |
| |
| switch_to_thread (thread); |
| *bp_link = bp->next; |
| release_breakpoint (proc, bp); |
| bp = *bp_link; |
| } |
| else |
| { |
| bp_link = &bp->next; |
| bp = *bp_link; |
| } |
| } |
| } |
| |
| static void |
| uninsert_raw_breakpoint (struct raw_breakpoint *bp) |
| { |
| if (bp->inserted < 0) |
| { |
| threads_debug_printf ("Breakpoint at %s is marked insert-disabled.", |
| paddress (bp->pc)); |
| } |
| else if (bp->inserted > 0) |
| { |
| int err; |
| |
| bp->inserted = 0; |
| |
| err = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, bp); |
| if (err != 0) |
| { |
| bp->inserted = 1; |
| |
| threads_debug_printf ("Failed to uninsert raw breakpoint at 0x%s.", |
| paddress (bp->pc)); |
| } |
| } |
| } |
| |
| void |
| uninsert_breakpoints_at (CORE_ADDR pc) |
| { |
| struct process_info *proc = current_process (); |
| struct raw_breakpoint *bp; |
| int found = 0; |
| |
| 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. */ |
| threads_debug_printf ("Could not find breakpoint at 0x%s " |
| "in list (uninserting).", |
| paddress (pc)); |
| } |
| } |
| |
| void |
| uninsert_all_breakpoints (void) |
| { |
| 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->inserted) |
| uninsert_raw_breakpoint (bp); |
| } |
| |
| void |
| uninsert_single_step_breakpoints (struct thread_info *thread) |
| { |
| struct process_info *proc = get_thread_process (thread); |
| struct breakpoint *bp; |
| |
| for (bp = proc->breakpoints; bp != NULL; bp = bp->next) |
| { |
| if (bp->type == single_step_breakpoint |
| && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread)) |
| { |
| gdb_assert (bp->raw->inserted > 0); |
| |
| /* Only uninsert the raw breakpoint if it only belongs to a |
| reinsert breakpoint. */ |
| if (bp->raw->refcount == 1) |
| { |
| scoped_restore_current_thread restore_thread; |
| |
| switch_to_thread (thread); |
| uninsert_raw_breakpoint (bp->raw); |
| } |
| } |
| } |
| } |
| |
| static void |
| reinsert_raw_breakpoint (struct raw_breakpoint *bp) |
| { |
| int err; |
| |
| if (bp->inserted) |
| return; |
| |
| err = the_target->insert_point (bp->raw_type, bp->pc, bp->kind, bp); |
| if (err == 0) |
| bp->inserted = 1; |
| else |
| threads_debug_printf ("Failed to reinsert breakpoint at 0x%s (%d).", |
| paddress (bp->pc), err); |
| } |
| |
| void |
| reinsert_breakpoints_at (CORE_ADDR pc) |
| { |
| struct process_info *proc = current_process (); |
| struct raw_breakpoint *bp; |
| int found = 0; |
| |
| 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. */ |
| threads_debug_printf ("Could not find raw breakpoint at 0x%s " |
| "in list (reinserting).", |
| paddress (pc)); |
| } |
| } |
| |
| int |
| has_single_step_breakpoints (struct thread_info *thread) |
| { |
| struct process_info *proc = get_thread_process (thread); |
| struct breakpoint *bp, **bp_link; |
| |
| bp = proc->breakpoints; |
| bp_link = &proc->breakpoints; |
| |
| while (bp) |
| { |
| if (bp->type == single_step_breakpoint |
| && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread)) |
| return 1; |
| else |
| { |
| bp_link = &bp->next; |
| bp = *bp_link; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void |
| reinsert_all_breakpoints (void) |
| { |
| 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->inserted) |
| reinsert_raw_breakpoint (bp); |
| } |
| |
| void |
| reinsert_single_step_breakpoints (struct thread_info *thread) |
| { |
| struct process_info *proc = get_thread_process (thread); |
| struct breakpoint *bp; |
| |
| for (bp = proc->breakpoints; bp != NULL; bp = bp->next) |
| { |
| if (bp->type == single_step_breakpoint |
| && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread)) |
| { |
| gdb_assert (bp->raw->inserted > 0); |
| |
| if (bp->raw->refcount == 1) |
| { |
| scoped_restore_current_thread restore_thread; |
| |
| switch_to_thread (thread); |
| reinsert_raw_breakpoint (bp->raw); |
| } |
| } |
| } |
| } |
| |
| void |
| check_breakpoints (CORE_ADDR stop_pc) |
| { |
| struct process_info *proc = current_process (); |
| struct breakpoint *bp, **bp_link; |
| |
| bp = proc->breakpoints; |
| bp_link = &proc->breakpoints; |
| |
| while (bp) |
| { |
| 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 (!raw->inserted) |
| { |
| warning ("Hit a removed breakpoint?"); |
| return; |
| } |
| |
| if (bp->type == other_breakpoint) |
| { |
| struct other_breakpoint *other_bp |
| = (struct other_breakpoint *) bp; |
| |
| if (other_bp->handler != NULL && (*other_bp->handler) (stop_pc)) |
| { |
| *bp_link = bp->next; |
| |
| release_breakpoint (proc, bp); |
| |
| bp = *bp_link; |
| continue; |
| } |
| } |
| } |
| |
| bp_link = &bp->next; |
| bp = *bp_link; |
| } |
| } |
| |
| int |
| breakpoint_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->raw_type == raw_bkpt_type_hw) |
| && bp->pc == addr) |
| return 1; |
| |
| return 0; |
| } |
| |
| int |
| 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->raw_type == raw_bkpt_type_hw) |
| && bp->pc == addr |
| && bp->inserted) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* See mem-break.h. */ |
| |
| int |
| 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; |
| |
| 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 |
| single_step_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 == single_step_breakpoint |
| && bp->raw->pc == addr |
| && bp->raw->inserted) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int |
| validate_inserted_breakpoint (struct raw_breakpoint *bp) |
| { |
| unsigned char *buf; |
| int err; |
| |
| gdb_assert (bp->inserted); |
| gdb_assert (bp->raw_type == raw_bkpt_type_sw); |
| |
| 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 = -1; |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void |
| delete_disabled_breakpoints (void) |
| { |
| struct process_info *proc = current_process (); |
| struct breakpoint *bp, *next; |
| |
| for (bp = proc->breakpoints; bp != NULL; bp = next) |
| { |
| next = bp->next; |
| if (bp->raw->inserted < 0) |
| { |
| /* If single_step_breakpoints become disabled, that means the |
| manipulations (insertion and removal) of them are wrong. */ |
| gdb_assert (bp->type != single_step_breakpoint); |
| delete_breakpoint_1 (proc, bp); |
| } |
| } |
| } |
| |
| /* Check if breakpoints we inserted still appear to be inserted. They |
| may disappear due to a shared library unload, and worse, a new |
| shared library may be reloaded at the same address as the |
| previously unloaded one. If that happens, we should make sure that |
| the shadow memory of the old breakpoints isn't used when reading or |
| writing memory. */ |
| |
| void |
| validate_breakpoints (void) |
| { |
| struct process_info *proc = current_process (); |
| struct breakpoint *bp; |
| |
| for (bp = proc->breakpoints; bp != NULL; bp = bp->next) |
| { |
| struct raw_breakpoint *raw = bp->raw; |
| |
| if (raw->raw_type == raw_bkpt_type_sw && raw->inserted > 0) |
| validate_inserted_breakpoint (raw); |
| } |
| |
| delete_disabled_breakpoints (); |
| } |
| |
| void |
| check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) |
| { |
| struct process_info *proc = current_process (); |
| struct raw_breakpoint *bp = proc->raw_breakpoints; |
| struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps; |
| CORE_ADDR mem_end = mem_addr + mem_len; |
| int disabled_one = 0; |
| |
| for (; jp != NULL; jp = jp->next) |
| { |
| CORE_ADDR bp_end = jp->pc + jp->length; |
| CORE_ADDR start, end; |
| int copy_offset, copy_len, buf_offset; |
| |
| gdb_assert (fast_tracepoint_jump_shadow (jp) >= buf + mem_len |
| || buf >= fast_tracepoint_jump_shadow (jp) + (jp)->length); |
| |
| if (mem_addr >= bp_end) |
| continue; |
| if (jp->pc >= mem_end) |
| continue; |
| |
| start = jp->pc; |
| if (mem_addr > start) |
| start = mem_addr; |
| |
| end = bp_end; |
| if (end > mem_end) |
| end = mem_end; |
| |
| copy_len = end - start; |
| copy_offset = start - jp->pc; |
| buf_offset = start - mem_addr; |
| |
| if (jp->inserted) |
| memcpy (buf + buf_offset, |
| fast_tracepoint_jump_shadow (jp) + copy_offset, |
| copy_len); |
| } |
| |
| for (; bp != NULL; bp = bp->next) |
| { |
| 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)]); |
| |
| if (mem_addr >= bp_end) |
| continue; |
| if (bp->pc >= mem_end) |
| continue; |
| |
| start = bp->pc; |
| if (mem_addr > start) |
| start = mem_addr; |
| |
| end = bp_end; |
| if (end > mem_end) |
| end = mem_end; |
| |
| copy_len = end - start; |
| copy_offset = start - bp->pc; |
| buf_offset = start - mem_addr; |
| |
| if (bp->inserted > 0) |
| { |
| if (validate_inserted_breakpoint (bp)) |
| memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); |
| else |
| disabled_one = 1; |
| } |
| } |
| |
| if (disabled_one) |
| delete_disabled_breakpoints (); |
| } |
| |
| void |
| check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, |
| const unsigned char *myaddr, int mem_len) |
| { |
| struct process_info *proc = current_process (); |
| struct raw_breakpoint *bp = proc->raw_breakpoints; |
| struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps; |
| CORE_ADDR mem_end = mem_addr + mem_len; |
| int disabled_one = 0; |
| |
| /* First fast tracepoint jumps, then breakpoint traps on top. */ |
| |
| for (; jp != NULL; jp = jp->next) |
| { |
| CORE_ADDR jp_end = jp->pc + jp->length; |
| CORE_ADDR start, end; |
| int copy_offset, copy_len, buf_offset; |
| |
| gdb_assert (fast_tracepoint_jump_shadow (jp) >= myaddr + mem_len |
| || myaddr >= fast_tracepoint_jump_shadow (jp) + (jp)->length); |
| gdb_assert (fast_tracepoint_jump_insn (jp) >= buf + mem_len |
| || buf >= fast_tracepoint_jump_insn (jp) + (jp)->length); |
| |
| if (mem_addr >= jp_end) |
| continue; |
| if (jp->pc >= mem_end) |
| continue; |
| |
| start = jp->pc; |
| if (mem_addr > start) |
| start = mem_addr; |
| |
| end = jp_end; |
| if (end > mem_end) |
| end = mem_end; |
| |
| copy_len = end - start; |
| copy_offset = start - jp->pc; |
| buf_offset = start - mem_addr; |
| |
| memcpy (fast_tracepoint_jump_shadow (jp) + copy_offset, |
| myaddr + buf_offset, copy_len); |
| if (jp->inserted) |
| memcpy (buf + buf_offset, |
| fast_tracepoint_jump_insn (jp) + copy_offset, copy_len); |
| } |
| |
| for (; bp != NULL; bp = bp->next) |
| { |
| 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)]); |
| |
| if (mem_addr >= bp_end) |
| continue; |
| if (bp->pc >= mem_end) |
| continue; |
| |
| start = bp->pc; |
| if (mem_addr > start) |
| start = mem_addr; |
| |
| end = bp_end; |
| if (end > mem_end) |
| end = mem_end; |
| |
| copy_len = end - start; |
| copy_offset = start - bp->pc; |
| buf_offset = start - mem_addr; |
| |
| memcpy (bp->old_data + copy_offset, myaddr + buf_offset, copy_len); |
| if (bp->inserted > 0) |
| { |
| if (validate_inserted_breakpoint (bp)) |
| memcpy (buf + buf_offset, bp_opcode (bp) + copy_offset, copy_len); |
| else |
| disabled_one = 1; |
| } |
| } |
| |
| if (disabled_one) |
| delete_disabled_breakpoints (); |
| } |
| |
| /* Delete all breakpoints, and un-insert them from the inferior. */ |
| |
| void |
| delete_all_breakpoints (void) |
| { |
| struct process_info *proc = current_process (); |
| |
| while (proc->breakpoints) |
| delete_breakpoint_1 (proc, proc->breakpoints); |
| } |
| |
| /* Clear the "inserted" flag in all breakpoints. */ |
| |
| void |
| mark_breakpoints_out (struct process_info *proc) |
| { |
| struct raw_breakpoint *raw_bp; |
| |
| for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next) |
| raw_bp->inserted = 0; |
| } |
| |
| /* Release all breakpoints, but do not try to un-insert them from the |
| inferior. */ |
| |
| void |
| free_all_breakpoints (struct process_info *proc) |
| { |
| mark_breakpoints_out (proc); |
| |
| /* 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) |
| 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, ptid_t ptid) |
| { |
| struct breakpoint *dest; |
| struct raw_breakpoint *dest_raw; |
| |
| /* 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. */ |
| if (is_gdb_breakpoint (src->type)) |
| { |
| struct gdb_breakpoint *gdb_dest = XCNEW (struct gdb_breakpoint); |
| 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 condition list. */ |
| for (current_cond = ((struct gdb_breakpoint *) 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 (&gdb_dest->cond_list, new_cond, cond_tail); |
| } |
| |
| /* Clone the command list. */ |
| for (current_cmd = ((struct gdb_breakpoint *) 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 (&gdb_dest->command_list, new_cmd, cmd_tail); |
| } |
| |
| dest = (struct breakpoint *) gdb_dest; |
| } |
| else if (src->type == other_breakpoint) |
| { |
| struct other_breakpoint *other_dest = XCNEW (struct other_breakpoint); |
| |
| other_dest->handler = ((struct other_breakpoint *) src)->handler; |
| dest = (struct breakpoint *) other_dest; |
| } |
| else if (src->type == single_step_breakpoint) |
| { |
| struct single_step_breakpoint *ss_dest |
| = XCNEW (struct single_step_breakpoint); |
| |
| dest = (struct breakpoint *) ss_dest; |
| /* Since single-step breakpoint is thread specific, don't copy |
| thread id from SRC, use ID instead. */ |
| ss_dest->ptid = ptid; |
| } |
| else |
| gdb_assert_not_reached ("unhandled breakpoint type"); |
| |
| dest->type = src->type; |
| dest->raw = dest_raw; |
| |
| return dest; |
| } |
| |
| /* See mem-break.h. */ |
| |
| void |
| clone_all_breakpoints (struct thread_info *child_thread, |
| const struct thread_info *parent_thread) |
| { |
| const struct breakpoint *bp; |
| struct breakpoint *new_bkpt; |
| struct breakpoint *bkpt_tail = NULL; |
| struct raw_breakpoint *raw_bkpt_tail = NULL; |
| struct process_info *child_proc = get_thread_process (child_thread); |
| struct process_info *parent_proc = get_thread_process (parent_thread); |
| struct breakpoint **new_list = &child_proc->breakpoints; |
| struct raw_breakpoint **new_raw_list = &child_proc->raw_breakpoints; |
| |
| for (bp = parent_proc->breakpoints; bp != NULL; bp = bp->next) |
| { |
| new_bkpt = clone_one_breakpoint (bp, ptid_of (child_thread)); |
| APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail); |
| APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail); |
| } |
| } |