52405d85ec
Now that 'process_stratum_target' has a single field left, namely 'pt' of type 'process_target', and that all the requests to a 'process_stratum_target' are forwarded to 'pt', meld the 'process_target' class into 'process_stratum_target'. This essentially means 1. All the references of the form 'the_target->pt' become 'the_target'. 2. All the uses of the name 'process_target' become 'process_stratum_target'. 3. The platform-specific target op vectors (e.g. linux_target_ops) are removed and instances of their "process target" classes are used instead. gdbserver/ChangeLog: 2020-02-20 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com> * target.h (struct process_stratum_target): Remove. (class process_target): Rename to ... (class process_stratum_target): ... this. * linux-low.h (class linux_process_target): Derive from 'process_stratum_target'. * linux-low.cc (linux_target_ops): Remove. (initialize_low): Set the_target to the singleton instance of linux_process_target. * lynx-low.h (class lynx_process_target): Derive from 'process_stratum_target'. * lynx-low.cc (lynx_target_ops): Remove. (initialize_low): Set the_target to the singleton instance of lynx_process_target. * nto-low.h (class nto_process_target): Derive from 'process_stratum_target'. * nto-low.cc (nto_target_ops): Remove. (initialize_low): Set the_target to the singleton instance of nto_process_target. * win32-low.h (class win32_process_target): Derive from 'process_stratum_target'. * win32-low.cc (win32_target_ops): Remove. (initialize_low): Set the_target to the singleton instance of win32_process_target. Replace 'the_target->pt' with 'the_target' in the uses below. * hostio.cc (hostio_error) (handle_setfs) (handle_open) (handle_unlink) (handle_readlink) * linux-aarch32-low.cc (arm_breakpoint_at) * linux-aarch64-low.cc (aarch64_breakpoint_at) * linux-arm-low.cc (arm_sigreturn_next_pc) (arm_get_hwcap) (arm_get_syscall_trapinfo) * linux-cris-low.cc (cris_breakpoint_at) * linux-crisv32-low.cc (cris_breakpoint_at) * linux-low.cc (handle_extended_wait) (linux_wait_1) (linux_read_memory) (linux_process_target::breakpoint_kind_from_pc) (linux_get_auxv) * linux-m32r-low.cc (m32r_breakpoint_at) * linux-mips-low.cc (mips_breakpoint_at) * linux-nios2-low.cc (nios2_breakpoint_at) * linux-ppc-low.cc (ppc_breakpoint_at) * linux-s390-low.cc (s390_get_hwcap) * linux-sh-low.cc (sh_breakpoint_at) * linux-sparc-low.cc (sparc_fill_gregset_to_stack) (sparc_store_gregset_from_stack) (sparc_breakpoint_at) * linux-tic6x-low.cc (tic6x_breakpoint_at) * linux-tile-low.cc (tile_breakpoint_at) * linux-x86-low.cc (x86_breakpoint_at) * linux-xtensa-low.cc (xtensa_breakpoint_at) * mem-break.cc (bp_size) (bp_opcode) (insert_memory_breakpoint) (set_raw_breakpoint_at) (delete_raw_breakpoint) (z_type_supported) (uninsert_raw_breakpoint) (reinsert_raw_breakpoint) (validate_inserted_breakpoint) * regcache.cc (regcache_read_pc) (regcache_write_pc) * remote-utils.cc (putpkt_binary_1) (input_interrupt) (getpkt) (prepare_resume_reply) * server.cc (handle_general_set) (handle_detach) (handle_qxfer_auxv) (handle_qxfer_exec_file) (handle_qxfer_libraries_svr4) (handle_qxfer_osdata) (handle_qxfer_siginfo) (handle_qxfer_fdpic) (handle_query) (resume) (handle_v_requests) (queue_stop_reply_callback) (captured_main) * target.cc (prepare_to_access_memory) (done_accessing_memory) (read_inferior_memory) (target_write_memory) (target_stop_and_wait) (target_wait) (target_mourn_inferior) (target_continue_no_signal) (target_continue) (target_supports_multi_process) (kill_inferior) * target.h (target_create_inferior) (target_post_create_inferior) (myattach) (target_supports_fork_events) (target_supports_vfork_events) (target_supports_exec_events) (target_handle_new_gdb_connection) (detach_inferior) (mythread_alive) (fetch_inferior_registers) (store_inferior_registers) (join_inferior) (target_supports_non_stop) (target_async) (target_process_qsupported) (target_supports_catch_syscall) (target_get_ipa_tdesc_idx) (target_supports_tracepoints) (target_supports_fast_tracepoints) (target_get_min_fast_tracepoint_insn_len) (target_thread_stopped) (target_pause_all) (target_unpause_all) (target_stabilize_threads) (target_install_fast_tracepoint_jump_pad) (target_emit_ops) (target_supports_disable_randomization) (target_supports_agent) (target_enable_btrace) (target_disable_btrace) (target_read_btrace) (target_read_btrace_conf) (target_supports_range_stepping) (target_supports_stopped_by_sw_breakpoint) (target_stopped_by_sw_breakpoint) (target_supports_stopped_by_hw_breakpoint) (target_supports_hardware_single_step) (target_stopped_by_hw_breakpoint) (target_breakpoint_kind_from_pc) (target_breakpoint_kind_from_current_state) (target_supports_software_single_step) (target_core_of_thread) (target_thread_name) (target_thread_handle) * win32-low.cc (do_initial_child_stuff) Rename target op default definitions listed below. * target.cc (process_target::post_create_inferior): Rename as ... (process_stratum_target::post_create_inferior): ... this. (process_target::prepare_to_access_memory): Rename as ... (process_stratum_target::prepare_to_access_memory): ... this. (process_target::done_accessing_memory): Rename as ... (process_stratum_target::done_accessing_memory): ... this. (process_target::look_up_symbols): Rename as ... (process_stratum_target::look_up_symbols): ... this. (process_target::supports_read_auxv): Rename as ... (process_stratum_target::supports_read_auxv): ... this. (process_target::read_auxv): Rename as ... (process_stratum_target::read_auxv): ... this. (process_target::supports_z_point_type): Rename as ... (process_stratum_target::supports_z_point_type): ... this. (process_target::insert_point): Rename as ... (process_stratum_target::insert_point): ... this. (process_target::remove_point): Rename as ... (process_stratum_target::remove_point): ... this. (process_target::stopped_by_sw_breakpoint): Rename as ... (process_stratum_target::stopped_by_sw_breakpoint): ... this. (process_target::supports_stopped_by_sw_breakpoint): Rename as ... (process_stratum_target::supports_stopped_by_sw_breakpoint): ... this. (process_target::stopped_by_hw_breakpoint): Rename as ... (process_stratum_target::stopped_by_hw_breakpoint): ... this. (process_target::supports_stopped_by_hw_breakpoint): Rename as ... (process_stratum_target::supports_stopped_by_hw_breakpoint): ... this. (process_target::supports_hardware_single_step): Rename as ... (process_stratum_target::supports_hardware_single_step): ... this. (process_target::stopped_by_watchpoint): Rename as ... (process_stratum_target::stopped_by_watchpoint): ... this. (process_target::stopped_data_address): Rename as ... (process_stratum_target::stopped_data_address): ... this. (process_target::supports_read_offsets): Rename as ... (process_stratum_target::supports_read_offsets): ... this. (process_target::read_offsets): Rename as ... (process_stratum_target::read_offsets): ... this. (process_target::supports_get_tls_address): Rename as ... (process_stratum_target::supports_get_tls_address): ... this. (process_target::get_tls_address): Rename as ... (process_stratum_target::get_tls_address): ... this. (process_target::hostio_last_error): Rename as ... (process_stratum_target::hostio_last_error): ... this. (process_target::supports_qxfer_osdata): Rename as ... (process_stratum_target::supports_qxfer_osdata): ... this. (process_target::qxfer_osdata): Rename as ... (process_stratum_target::qxfer_osdata): ... this. (process_target::supports_qxfer_siginfo): Rename as ... (process_stratum_target::supports_qxfer_siginfo): ... this. (process_target::qxfer_siginfo): Rename as ... (process_stratum_target::qxfer_siginfo): ... this. (process_target::supports_non_stop): Rename as ... (process_stratum_target::supports_non_stop): ... this. (process_target::async): Rename as ... (process_stratum_target::async): ... this. (process_target::start_non_stop): Rename as ... (process_stratum_target::start_non_stop): ... this. (process_target::supports_multi_process): Rename as ... (process_stratum_target::supports_multi_process): ... this. (process_target::supports_fork_events): Rename as ... (process_stratum_target::supports_fork_events): ... this. (process_target::supports_vfork_events): Rename as ... (process_stratum_target::supports_vfork_events): ... this. (process_target::supports_exec_events): Rename as ... (process_stratum_target::supports_exec_events): ... this. (process_target::handle_new_gdb_connection): Rename as ... (process_stratum_target::handle_new_gdb_connection): ... this. (process_target::handle_monitor_command): Rename as ... (process_stratum_target::handle_monitor_command): ... this. (process_target::core_of_thread): Rename as ... (process_stratum_target::core_of_thread): ... this. (process_target::supports_read_loadmap): Rename as ... (process_stratum_target::supports_read_loadmap): ... this. (process_target::read_loadmap): Rename as ... (process_stratum_target::read_loadmap): ... this. (process_target::process_qsupported): Rename as ... (process_stratum_target::process_qsupported): ... this. (process_target::supports_tracepoints): Rename as ... (process_stratum_target::supports_tracepoints): ... this. (process_target::read_pc): Rename as ... (process_stratum_target::read_pc): ... this. (process_target::write_pc): Rename as ... (process_stratum_target::write_pc): ... this. (process_target::supports_thread_stopped): Rename as ... (process_stratum_target::supports_thread_stopped): ... this. (process_target::thread_stopped): Rename as ... (process_stratum_target::thread_stopped): ... this. (process_target::supports_get_tib_address): Rename as ... (process_stratum_target::supports_get_tib_address): ... this. (process_target::get_tib_address): Rename as ... (process_stratum_target::get_tib_address): ... this. (process_target::pause_all): Rename as ... (process_stratum_target::pause_all): ... this. (process_target::unpause_all): Rename as ... (process_stratum_target::unpause_all): ... this. (process_target::stabilize_threads): Rename as ... (process_stratum_target::stabilize_threads): ... this. (process_target::supports_fast_tracepoints): Rename as ... (process_stratum_target::supports_fast_tracepoints): ... this. (process_target::get_min_fast_tracepoint_insn_len): Rename as ... (process_stratum_target::get_min_fast_tracepoint_insn_len): ... this. (process_target::emit_ops): Rename as ... (process_stratum_target::emit_ops): ... this. (process_target::supports_disable_randomization): Rename as ... (process_stratum_target::supports_disable_randomization): ... this. (process_target::supports_qxfer_libraries_svr4): Rename as ... (process_stratum_target::supports_qxfer_libraries_svr4): ... this. (process_target::qxfer_libraries_svr4): Rename as ... (process_stratum_target::qxfer_libraries_svr4): ... this. (process_target::supports_agent): Rename as ... (process_stratum_target::supports_agent): ... this. (process_target::enable_btrace): Rename as ... (process_stratum_target::enable_btrace): ... this. (process_target::disable_btrace): Rename as ... (process_stratum_target::disable_btrace): ... this. (process_target::read_btrace): Rename as ... (process_stratum_target::read_btrace): ... this. (process_target::read_btrace_conf): Rename as ... (process_stratum_target::read_btrace_conf): ... this. (process_target::supports_range_stepping): Rename as ... (process_stratum_target::supports_range_stepping): ... this. (process_target::supports_pid_to_exec_file): Rename as ... (process_stratum_target::supports_pid_to_exec_file): ... this. (process_target::pid_to_exec_file): Rename as ... (process_stratum_target::pid_to_exec_file): ... this. (process_target::supports_multifs): Rename as ... (process_stratum_target::supports_multifs): ... this. (process_target::multifs_open): Rename as ... (process_stratum_target::multifs_open): ... this. (process_target::multifs_unlink): Rename as ... (process_stratum_target::multifs_unlink): ... this. (process_target::multifs_readlink): Rename as ... (process_stratum_target::multifs_readlink): ... this. (process_target::breakpoint_kind_from_pc): Rename as ... (process_stratum_target::breakpoint_kind_from_pc): ... this. (process_target::breakpoint_kind_from_current_state): Rename as ... (process_stratum_target::breakpoint_kind_from_current_state): ... this. (process_target::thread_name): Rename as ... (process_stratum_target::thread_name): ... this. (process_target::thread_handle): Rename as ... (process_stratum_target::thread_handle): ... this. (process_target::supports_software_single_step): Rename as ... (process_stratum_target::supports_software_single_step): ... this. (process_target::supports_catch_syscall): Rename as ... (process_stratum_target::supports_catch_syscall): ... this. (process_target::get_ipa_tdesc_idx): Rename as ... (process_stratum_target::get_ipa_tdesc_idx): ... this.
2237 lines
57 KiB
C++
2237 lines
57 KiB
C++
/* Memory breakpoint operations for the remote server for GDB.
|
|
Copyright (C) 2002-2020 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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to read shadow memory of"
|
|
" breakpoint at 0x%s (%s).\n",
|
|
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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to insert breakpoint at 0x%s (%s).\n",
|
|
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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to uninsert raw breakpoint "
|
|
"at 0x%s (%s) while deleting it.\n",
|
|
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. */
|
|
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);
|
|
|
|
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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to insert breakpoint at 0x%s (%d).\n",
|
|
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;
|
|
|
|
if (debug_threads)
|
|
debug_printf ("Failed to uninsert fast tracepoint jump "
|
|
"at 0x%s (%s) while deleting it.\n",
|
|
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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to read shadow memory of"
|
|
" fast tracepoint at 0x%s (%s).\n",
|
|
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)
|
|
{
|
|
if (debug_threads)
|
|
debug_printf ("Failed to insert fast tracepoint jump at 0x%s (%s).\n",
|
|
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. */
|
|
if (debug_threads)
|
|
debug_printf ("Could not find fast tracepoint jump at 0x%s "
|
|
"in list (uninserting).\n",
|
|
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;
|
|
|
|
if (debug_threads)
|
|
debug_printf ("Failed to uninsert fast tracepoint jump at"
|
|
" 0x%s (%s).\n",
|
|
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. */
|
|
if (debug_threads)
|
|
debug_printf ("Could not find fast tracepoint jump at 0x%s "
|
|
"in list (reinserting).\n",
|
|
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;
|
|
|
|
if (debug_threads)
|
|
debug_printf ("Failed to reinsert fast tracepoint jump at"
|
|
" 0x%s (%s).\n",
|
|
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;
|
|
|
|
if (debug_threads)
|
|
debug_printf ("Failed to uninsert raw breakpoint "
|
|
"at 0x%s while deleting it.\n",
|
|
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. */
|
|
|
|
static struct gdb_breakpoint *
|
|
set_gdb_breakpoint_1 (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 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* See mem-break.h. This is a wrapper for set_gdb_breakpoint_1 that
|
|
knows to prepare to access memory for Z0 breakpoints. */
|
|
|
|
struct gdb_breakpoint *
|
|
set_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind, int *err)
|
|
{
|
|
struct gdb_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;
|
|
}
|
|
|
|
/* 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 gdb_breakpoint *bp;
|
|
int err;
|
|
|
|
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);
|
|
err = delete_breakpoint ((struct breakpoint *) bp);
|
|
if (err != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* See mem-break.h. This is a wrapper for delete_gdb_breakpoint that
|
|
knows to prepare to access memory for Z0 breakpoints. */
|
|
|
|
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 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;
|
|
|
|
if (debug_threads)
|
|
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);
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
struct thread_info *saved_thread = current_thread;
|
|
|
|
current_thread = thread;
|
|
*bp_link = bp->next;
|
|
release_breakpoint (proc, bp);
|
|
bp = *bp_link;
|
|
current_thread = saved_thread;
|
|
}
|
|
else
|
|
{
|
|
bp_link = &bp->next;
|
|
bp = *bp_link;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
uninsert_raw_breakpoint (struct raw_breakpoint *bp)
|
|
{
|
|
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;
|
|
|
|
bp->inserted = 0;
|
|
|
|
err = the_target->remove_point (bp->raw_type, bp->pc, bp->kind, bp);
|
|
if (err != 0)
|
|
{
|
|
bp->inserted = 1;
|
|
|
|
if (debug_threads)
|
|
debug_printf ("Failed to uninsert raw breakpoint at 0x%s.\n",
|
|
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. */
|
|
if (debug_threads)
|
|
debug_printf ("Could not find breakpoint at 0x%s "
|
|
"in list (uninserting).\n",
|
|
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)
|
|
{
|
|
struct thread_info *saved_thread = current_thread;
|
|
|
|
current_thread = thread;
|
|
uninsert_raw_breakpoint (bp->raw);
|
|
current_thread = saved_thread;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 if (debug_threads)
|
|
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;
|
|
|
|
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)
|
|
debug_printf ("Could not find raw breakpoint at 0x%s "
|
|
"in list (reinserting).\n",
|
|
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)
|
|
{
|
|
struct thread_info *saved_thread = current_thread;
|
|
|
|
current_thread = thread;
|
|
reinsert_raw_breakpoint (bp->raw);
|
|
current_thread = saved_thread;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|