diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2020-12-04 16:43:56 -0500 |
---|---|---|
committer | Simon Marchi <simon.marchi@efficios.com> | 2020-12-04 16:43:56 -0500 |
commit | 480af54cf68b5bb079f070b587f95620d114e87c (patch) | |
tree | 23d825a69feb63ad3a4b4f385bdd62d35c35b67e /gdb/displaced-stepping.c | |
parent | d965505887c27e8b1d6e84cb06c2b131aad08093 (diff) | |
download | binutils-480af54cf68b5bb079f070b587f95620d114e87c.zip binutils-480af54cf68b5bb079f070b587f95620d114e87c.tar.gz binutils-480af54cf68b5bb079f070b587f95620d114e87c.tar.bz2 |
gdb: make displaced stepping implementation capable of managing multiple buffers
The displaced_step_buffer class, introduced in the previous patch,
manages access to a single displaced step buffer. Change it into
displaced_step_buffers (note the plural), which manages access to
multiple displaced step buffers.
When preparing a displaced step for a thread, it looks for an unused
buffer.
For now, all users still pass a single displaced step buffer, so no real
behavior change is expected here. The following patch makes a user pass
more than one buffer, so the functionality introduced by this patch is
going to be useful in the next one.
gdb/ChangeLog:
* displaced-stepping.h (struct displaced_step_buffer): Rename
to...
(struct displaced_step_buffers): ... this.
<m_addr, m_current_thread, m_copy_insn_closure>: Remove.
<struct displaced_step_buffer>: New inner class.
<m_buffers>: New.
* displaced-stepping.c (displaced_step_buffer::prepare): Rename
to...
(displaced_step_buffers::prepare): ... this, adjust for multiple
buffers.
(displaced_step_buffer::finish): Rename to...
(displaced_step_buffers::finish): ... this, adjust for multiple
buffers.
(displaced_step_buffer::copy_insn_closure_by_addr): Rename to...
(displaced_step_buffers::copy_insn_closure_by_addr): ... this,
adjust for multiple buffers.
(displaced_step_buffer::restore_in_ptid): Rename to...
(displaced_step_buffers::restore_in_ptid): ... this, adjust for
multiple buffers.
* linux-tdep.h (linux_init_abi): Change supports_displaced_step
for num_disp_step_buffers.
* linux-tdep.c (struct linux_gdbarch_data)
<num_disp_step_buffers>: New field.
(struct linux_info) <disp_step_buf>: Rename to...
<disp_step_bufs>: ... this, change type to
displaced_step_buffers.
(linux_displaced_step_prepare): Use
linux_gdbarch_data::num_disp_step_buffers to create that number
of buffers.
(linux_displaced_step_finish): Adjust.
(linux_displaced_step_copy_insn_closure_by_addr): Adjust.
(linux_displaced_step_restore_all_in_ptid): Adjust.
(linux_init_abi): Change supports_displaced_step parameter for
num_disp_step_buffers, save it in linux_gdbarch_data.
* aarch64-linux-tdep.c (aarch64_linux_init_abi): Adjust.
* alpha-linux-tdep.c (alpha_linux_init_abi): Adjust.
* amd64-linux-tdep.c (amd64_linux_init_abi_common): Change
supports_displaced_step parameter for num_disp_step_buffers.
(amd64_linux_init_abi): Adjust.
(amd64_x32_linux_init_abi): Adjust.
* arc-linux-tdep.c (arc_linux_init_osabi): Adjust.
* arm-linux-tdep.c (arm_linux_init_abi): Adjust.
* bfin-linux-tdep.c (bfin_linux_init_abi): Adjust.
* cris-linux-tdep.c (cris_linux_init_abi): Adjust.
* csky-linux-tdep.c (csky_linux_init_abi): Adjust.
* frv-linux-tdep.c (frv_linux_init_abi): Adjust.
* hppa-linux-tdep.c (hppa_linux_init_abi): Adjust.
* i386-linux-tdep.c (i386_linux_init_abi): Adjust.
* ia64-linux-tdep.c (ia64_linux_init_abi): Adjust.
* m32r-linux-tdep.c (m32r_linux_init_abi): Adjust.
* m68k-linux-tdep.c (m68k_linux_init_abi):
* microblaze-linux-tdep.c (microblaze_linux_init_abi):
* mips-linux-tdep.c (mips_linux_init_abi): Adjust.
* mn10300-linux-tdep.c (am33_linux_init_osabi): Adjust.
* nios2-linux-tdep.c (nios2_linux_init_abi): Adjust.
* or1k-linux-tdep.c (or1k_linux_init_abi): Adjust.
* ppc-linux-tdep.c (ppc_linux_init_abi): Adjust.
* riscv-linux-tdep.c (riscv_linux_init_abi): Adjust.
* rs6000-tdep.c (struct ppc_inferior_data) <disp_step_buf>:
Change type to displaced_step_buffers.
* s390-linux-tdep.c (s390_linux_init_abi_any): Adjust.
* sh-linux-tdep.c (sh_linux_init_abi): Adjust.
* sparc-linux-tdep.c (sparc32_linux_init_abi): Adjust.
* sparc64-linux-tdep.c (sparc64_linux_init_abi): Adjust.
* tic6x-linux-tdep.c (tic6x_uclinux_init_abi): Adjust.
* tilegx-linux-tdep.c (tilegx_linux_init_abi): Adjust.
* xtensa-linux-tdep.c (xtensa_linux_init_abi): Adjust.
Change-Id: Ia9c02f207da2c9e1d9188020139619122392bb70
Diffstat (limited to 'gdb/displaced-stepping.c')
-rw-r--r-- | gdb/displaced-stepping.c | 178 |
1 files changed, 119 insertions, 59 deletions
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c index fb5d23f..10aa3da 100644 --- a/gdb/displaced-stepping.c +++ b/gdb/displaced-stepping.c @@ -44,82 +44,121 @@ show_debug_displaced (struct ui_file *file, int from_tty, } displaced_step_prepare_status -displaced_step_buffer::prepare (thread_info *thread, CORE_ADDR &displaced_pc) +displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc) { gdb_assert (!thread->displaced_step_state.in_progress ()); - /* Is a thread currently using the buffer? */ - if (m_current_thread != nullptr) - { - /* If so, it better not be this thread. */ - gdb_assert (thread != m_current_thread); - return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; - } + /* Sanity check: the thread should not be using a buffer at this point. */ + for (displaced_step_buffer &buf : m_buffers) + gdb_assert (buf.current_thread != thread); regcache *regcache = get_thread_regcache (thread); const address_space *aspace = regcache->aspace (); gdbarch *arch = regcache->arch (); ULONGEST len = gdbarch_max_insn_length (arch); - if (breakpoint_in_range_p (aspace, m_addr, len)) - { - /* There's a breakpoint set in the scratch pad location range - (which is usually around the entry point). We'd either - install it before resuming, which would overwrite/corrupt the - scratch pad, or if it was already inserted, this displaced - step would overwrite it. The latter is OK in the sense that - we already assume that no thread is going to execute the code - in the scratch pad range (after initial startup) anyway, but - the former is unacceptable. Simply punt and fallback to - stepping over this breakpoint in-line. */ - displaced_debug_printf ("breakpoint set in scratch pad. " - "Stepping over breakpoint in-line instead."); + /* Search for an unused buffer. */ + displaced_step_buffer *buffer = nullptr; + displaced_step_prepare_status fail_status + = DISPLACED_STEP_PREPARE_STATUS_CANT; - return DISPLACED_STEP_PREPARE_STATUS_CANT; + for (displaced_step_buffer &candidate : m_buffers) + { + bool bp_in_range = breakpoint_in_range_p (aspace, candidate.addr, len); + bool is_free = candidate.current_thread == nullptr; + + if (!bp_in_range) + { + if (is_free) + { + buffer = &candidate; + break; + } + else + { + /* This buffer would be suitable, but it's used right now. */ + fail_status = DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; + } + } + else + { + /* There's a breakpoint set in the scratch pad location range + (which is usually around the entry point). We'd either + install it before resuming, which would overwrite/corrupt the + scratch pad, or if it was already inserted, this displaced + step would overwrite it. The latter is OK in the sense that + we already assume that no thread is going to execute the code + in the scratch pad range (after initial startup) anyway, but + the former is unacceptable. Simply punt and fallback to + stepping over this breakpoint in-line. */ + displaced_debug_printf ("breakpoint set in displaced stepping " + "buffer at %s, can't use.", + paddress (arch, candidate.addr)); + } } - m_original_pc = regcache_read_pc (regcache); - displaced_pc = m_addr; + if (buffer == nullptr) + return fail_status; + + displaced_debug_printf ("selected buffer at %s", + paddress (arch, buffer->addr)); + + /* Save the original PC of the thread. */ + buffer->original_pc = regcache_read_pc (regcache); + + /* Return displaced step buffer address to caller. */ + displaced_pc = buffer->addr; /* Save the original contents of the displaced stepping buffer. */ - m_saved_copy.resize (len); + buffer->saved_copy.resize (len); - int status = target_read_memory (m_addr, m_saved_copy.data (), len); + int status = target_read_memory (buffer->addr, + buffer->saved_copy.data (), len); if (status != 0) throw_error (MEMORY_ERROR, _("Error accessing memory address %s (%s) for " "displaced-stepping scratch space."), - paddress (arch, m_addr), safe_strerror (status)); + paddress (arch, buffer->addr), safe_strerror (status)); displaced_debug_printf ("saved %s: %s", - paddress (arch, m_addr), + paddress (arch, buffer->addr), displaced_step_dump_bytes - (m_saved_copy.data (), len).c_str ()); + (buffer->saved_copy.data (), len).c_str ()); /* Save this in a local variable first, so it's released if code below throws. */ displaced_step_copy_insn_closure_up copy_insn_closure - = gdbarch_displaced_step_copy_insn (arch, m_original_pc, m_addr, regcache); + = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc, + buffer->addr, regcache); if (copy_insn_closure == nullptr) { /* The architecture doesn't know how or want to displaced step - this instruction or instruction sequence. Fallback to - stepping over the breakpoint in-line. */ + this instruction or instruction sequence. Fallback to + stepping over the breakpoint in-line. */ return DISPLACED_STEP_PREPARE_STATUS_CANT; } /* Resume execution at the copy. */ - regcache_write_pc (regcache, m_addr); + regcache_write_pc (regcache, buffer->addr); /* This marks the buffer as being in use. */ - m_current_thread = thread; + buffer->current_thread = thread; /* Save this, now that we know everything went fine. */ - m_copy_insn_closure = std::move (copy_insn_closure); + buffer->copy_insn_closure = std::move (copy_insn_closure); - /* Tell infrun not to try preparing a displaced step again for this inferior. */ + /* Tell infrun not to try preparing a displaced step again for this inferior if + all buffers are taken. */ thread->inf->displaced_step_state.unavailable = true; + for (const displaced_step_buffer &buf : m_buffers) + { + if (buf.current_thread == nullptr) + { + thread->inf->displaced_step_state.unavailable = false; + break; + } + } return DISPLACED_STEP_PREPARE_STATUS_OK; } @@ -152,21 +191,34 @@ displaced_step_instruction_executed_successfully (gdbarch *arch, } displaced_step_finish_status -displaced_step_buffer::finish (gdbarch *arch, thread_info *thread, - gdb_signal sig) +displaced_step_buffers::finish (gdbarch *arch, thread_info *thread, + gdb_signal sig) { gdb_assert (thread->displaced_step_state.in_progress ()); - gdb_assert (thread == m_current_thread); + + /* Find the buffer this thread was using. */ + displaced_step_buffer *buffer = nullptr; + + for (displaced_step_buffer &candidate : m_buffers) + { + if (candidate.current_thread == thread) + { + buffer = &candidate; + break; + } + } + + gdb_assert (buffer != nullptr); /* Move this to a local variable so it's released in case something goes wrong. */ displaced_step_copy_insn_closure_up copy_insn_closure - = std::move (m_copy_insn_closure); + = std::move (buffer->copy_insn_closure); gdb_assert (copy_insn_closure != nullptr); - /* Reset M_CURRENT_THREAD immediately to mark the buffer as available, in case - something goes wrong below. */ - m_current_thread = nullptr; + /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available, + in case something goes wrong below. */ + buffer->current_thread = nullptr; /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced step again for this inferior. Do that here in case something goes wrong @@ -175,12 +227,13 @@ displaced_step_buffer::finish (gdbarch *arch, thread_info *thread, ULONGEST len = gdbarch_max_insn_length (arch); - write_memory_ptid (thread->ptid, m_addr, - m_saved_copy.data (), len); + /* Restore memory of the buffer. */ + write_memory_ptid (thread->ptid, buffer->addr, + buffer->saved_copy.data (), len); displaced_debug_printf ("restored %s %s", target_pid_to_str (thread->ptid).c_str (), - paddress (arch, m_addr)); + paddress (arch, buffer->addr)); regcache *rc = get_thread_regcache (thread); @@ -189,8 +242,9 @@ displaced_step_buffer::finish (gdbarch *arch, thread_info *thread, if (instruction_executed_successfully) { - gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (), m_original_pc, - m_addr, rc); + gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (), + buffer->original_pc, + buffer->addr, rc); return DISPLACED_STEP_FINISH_STATUS_OK; } else @@ -198,35 +252,41 @@ displaced_step_buffer::finish (gdbarch *arch, thread_info *thread, /* Since the instruction didn't complete, all we can do is relocate the PC. */ CORE_ADDR pc = regcache_read_pc (rc); - pc = m_original_pc + (pc - m_addr); + pc = buffer->original_pc + (pc - buffer->addr); regcache_write_pc (rc, pc); return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED; } } const displaced_step_copy_insn_closure * -displaced_step_buffer::copy_insn_closure_by_addr (CORE_ADDR addr) +displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr) { - if (addr == m_addr) - return m_copy_insn_closure.get (); - else - return nullptr; + for (const displaced_step_buffer &buffer : m_buffers) + { + if (addr == buffer.addr) + return buffer.copy_insn_closure.get (); + } + + return nullptr; } void -displaced_step_buffer::restore_in_ptid (ptid_t ptid) +displaced_step_buffers::restore_in_ptid (ptid_t ptid) { - if (m_current_thread != nullptr) + for (const displaced_step_buffer &buffer : m_buffers) { - regcache *regcache = get_thread_regcache (m_current_thread); + if (buffer.current_thread == nullptr) + continue; + + regcache *regcache = get_thread_regcache (buffer.current_thread); gdbarch *arch = regcache->arch (); ULONGEST len = gdbarch_max_insn_length (arch); - write_memory_ptid (ptid, m_addr, m_saved_copy.data (), len); + write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len); displaced_debug_printf ("restored in ptid %s %s", target_pid_to_str (ptid).c_str (), - paddress (arch, m_addr)); + paddress (arch, buffer.addr)); } } |