aboutsummaryrefslogtreecommitdiff
path: root/gdb/displaced-stepping.c
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@efficios.com>2020-12-04 16:43:56 -0500
committerSimon Marchi <simon.marchi@efficios.com>2020-12-04 16:43:56 -0500
commit480af54cf68b5bb079f070b587f95620d114e87c (patch)
tree23d825a69feb63ad3a4b4f385bdd62d35c35b67e /gdb/displaced-stepping.c
parentd965505887c27e8b1d6e84cb06c2b131aad08093 (diff)
downloadfsf-binutils-gdb-480af54cf68b5bb079f070b587f95620d114e87c.zip
fsf-binutils-gdb-480af54cf68b5bb079f070b587f95620d114e87c.tar.gz
fsf-binutils-gdb-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.c178
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));
}
}