[PATCH 11/12] gdb: make displaced stepping implementation capable of managing multiple buffers

Simon Marchi via Gdb-patches
2020-11-10T21:47:27Z

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
---
gdb/aarch64-linux-tdep.c | 2 +-
gdb/alpha-linux-tdep.c | 2 +-
gdb/amd64-linux-tdep.c | 8 +-
gdb/arc-linux-tdep.c | 2 +-
gdb/arm-linux-tdep.c | 2 +-
gdb/bfin-linux-tdep.c | 2 +-
gdb/cris-linux-tdep.c | 2 +-
gdb/csky-linux-tdep.c | 2 +-
gdb/displaced-stepping.c | 171 +++++++++++++++++++++++-------------
gdb/displaced-stepping.h | 46 +++++++---
gdb/frv-linux-tdep.c | 2 +-
gdb/hppa-linux-tdep.c | 2 +-
gdb/i386-linux-tdep.c | 2 +-
gdb/ia64-linux-tdep.c | 2 +-
gdb/linux-tdep.c | 54 ++++++++----
gdb/linux-tdep.h | 2 +-
gdb/m32r-linux-tdep.c | 2 +-
gdb/m68k-linux-tdep.c | 2 +-
gdb/microblaze-linux-tdep.c | 2 +-
gdb/mips-linux-tdep.c | 2 +-
gdb/mn10300-linux-tdep.c | 2 +-
gdb/nios2-linux-tdep.c | 2 +-
gdb/or1k-linux-tdep.c | 2 +-
gdb/ppc-linux-tdep.c | 2 +-
gdb/riscv-linux-tdep.c | 2 +-
gdb/rs6000-tdep.c | 2 +-
gdb/s390-linux-tdep.c | 2 +-
gdb/sh-linux-tdep.c | 2 +-
gdb/sparc-linux-tdep.c | 2 +-
gdb/sparc64-linux-tdep.c | 2 +-
gdb/tic6x-linux-tdep.c | 2 +-
gdb/tilegx-linux-tdep.c | 2 +-
gdb/xtensa-linux-tdep.c | 2 +-
33 files changed, 214 insertions(+), 123 deletions(-)

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 4fe7babe59c..ce697ff246e 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -1445,7 +1445,7 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)

tdep->lowest_pc = 0x8000;

- linux_init_abi (info, gdbarch, true);
+ linux_init_abi (info, gdbarch, 1);

set_solib_svr4_fetch_link_map_offsets (gdbarch,
svr4_lp64_fetch_link_map_offsets);
diff --git a/gdb/alpha-linux-tdep.c b/gdb/alpha-linux-tdep.c
index a6d6b15e9fd..520dd980d88 100644
--- a/gdb/alpha-linux-tdep.c
+++ b/gdb/alpha-linux-tdep.c
@@ -356,7 +356,7 @@ alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep;

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Hook into the DWARF CFI frame unwinder. */
alpha_dwarf2_init_abi (info, gdbarch);
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index a81bb9039df..60707ed7aaf 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1796,11 +1796,11 @@ amd64_dtrace_parse_probe_argument (struct gdbarch *gdbarch,

static void
amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
- bool supports_displaced_step)
+ int num_disp_step_buffers)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, supports_displaced_step);
+ linux_init_abi (info, gdbarch, num_disp_step_buffers);

tdep->sigtramp_p = amd64_linux_sigtramp_p;
tdep->sigcontext_addr = amd64_linux_sigcontext_addr;
@@ -1880,7 +1880,7 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
if (!valid_p)
return;

- amd64_linux_init_abi_common (info, gdbarch, true);
+ amd64_linux_init_abi_common (info, gdbarch, 1);

/* Initialize the amd64_linux_record_tdep. */
/* These values are the size of the type that will be used in a system
@@ -2095,7 +2095,7 @@ amd64_x32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
if (!valid_p)
return;

- amd64_linux_init_abi_common (info, gdbarch, false);
+ amd64_linux_init_abi_common (info, gdbarch, 0);

/* Initialize the amd64_x32_linux_record_tdep. */
/* These values are the size of the type that will be used in a system
diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c
index 71dcdcc1811..5ad66bd5eec 100644
--- a/gdb/arc-linux-tdep.c
+++ b/gdb/arc-linux-tdep.c
@@ -434,7 +434,7 @@ arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
*/
tdep->jb_pc = 15;

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Set up target dependent GDB architecture entries. */
set_gdbarch_cannot_fetch_register (gdbarch, arc_linux_cannot_fetch_register);
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index d164cff3dff..11e71e56b46 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -1721,7 +1721,7 @@ arm_linux_init_abi (struct gdbarch_info info,
NULL };
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, true);
+ linux_init_abi (info, gdbarch, 1);

tdep->lowest_pc = 0x8000;
if (info.byte_order_for_code == BFD_ENDIAN_BIG)
diff --git a/gdb/bfin-linux-tdep.c b/gdb/bfin-linux-tdep.c
index fc2f1d9ac65..bc2bb1aad45 100644
--- a/gdb/bfin-linux-tdep.c
+++ b/gdb/bfin-linux-tdep.c
@@ -150,7 +150,7 @@ bfin_linux_get_syscall_number (struct gdbarch *gdbarch,
static void
bfin_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Set the sigtramp frame sniffer. */
tramp_frame_prepend_unwinder (gdbarch, &bfin_linux_sigframe);
diff --git a/gdb/cris-linux-tdep.c b/gdb/cris-linux-tdep.c
index 85cbf4cc093..4ae1cdd1390 100644
--- a/gdb/cris-linux-tdep.c
+++ b/gdb/cris-linux-tdep.c
@@ -35,7 +35,7 @@ cris_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

if (tdep->cris_version == 32)
/* Threaded debugging is only supported on CRISv32 for now. */
diff --git a/gdb/csky-linux-tdep.c b/gdb/csky-linux-tdep.c
index 184fa5ffb23..a0d32b5f2d0 100644
--- a/gdb/csky-linux-tdep.c
+++ b/gdb/csky-linux-tdep.c
@@ -233,7 +233,7 @@ csky_linux_rt_sigreturn_tramp_frame = {
static void
csky_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c
index f92eb52293f..1d7c6d8ae4d 100644
--- a/gdb/displaced-stepping.c
+++ b/gdb/displaced-stepping.c
@@ -44,86 +44,114 @@ 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_ERROR;

- return DISPLACED_STEP_PREPARE_STATUS_ERROR;
+ 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 ());

- m_copy_insn_closure = gdbarch_displaced_step_copy_insn (arch,
- m_original_pc,
- m_addr,
- regcache);
- if (m_copy_insn_closure == nullptr)
+ buffer->copy_insn_closure
+ = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc,
+ buffer->addr, regcache);
+ if (buffer->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_ERROR;
}

try
{
/* Resume execution at the copy. */
- regcache_write_pc (regcache, m_addr);
+ regcache_write_pc (regcache, buffer->addr);
}
catch (...)
{
/* Failed to write the PC. Release the architecture's displaced
- stepping resources and the thread's displaced stepping state. */
- m_copy_insn_closure.reset ();
+ stepping resources and the thread's displaced stepping state. */
+ buffer->copy_insn_closure.reset ();

return DISPLACED_STEP_PREPARE_STATUS_ERROR;
}

/* This marks the buffer as being in use. */
- m_current_thread = thread;
+ buffer->current_thread = thread;

return DISPLACED_STEP_PREPARE_STATUS_OK;
}
@@ -156,20 +184,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 (thread == candidate.current_thread)
+ {
+ buffer = &candidate;
+ break;
+ }
+ }
+
+ gdb_assert (buffer != nullptr);

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);

@@ -179,8 +221,9 @@ displaced_step_buffer::finish (gdbarch *arch, thread_info *thread,

if (instruction_executed_successfully)
{
- gdbarch_displaced_step_fixup (arch, m_copy_insn_closure.get (), m_original_pc,
- m_addr, rc);
+ gdbarch_displaced_step_fixup (arch, buffer->copy_insn_closure.get (),
+ buffer->original_pc,
+ buffer->addr, rc);
status = DISPLACED_STEP_FINISH_STATUS_OK;
}
else
@@ -188,40 +231,46 @@ 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);
status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
}

- m_copy_insn_closure.reset ();
- m_current_thread = nullptr;
+ buffer->copy_insn_closure.reset ();
+ buffer->current_thread = nullptr;

return status;
}

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));
}
}

diff --git a/gdb/displaced-stepping.h b/gdb/displaced-stepping.h
index 6c1da46777c..ce9abcd2307 100644
--- a/gdb/displaced-stepping.h
+++ b/gdb/displaced-stepping.h
@@ -20,6 +20,7 @@
#ifndef DISPLACED_STEPPING_H
#define DISPLACED_STEPPING_H

+#include "gdbsupport/array-view.h"
#include "gdbsupport/byte-vector.h"

struct gdbarch;
@@ -142,13 +143,17 @@ struct displaced_step_thread_state
gdbarch *m_original_gdbarch = nullptr;
};

-/* Manage access to a single displaced stepping buffer. */
+/* Control access to multiple displaced stepping buffers at fixed addresses. */

-struct displaced_step_buffer
+struct displaced_step_buffers
{
- displaced_step_buffer (CORE_ADDR buffer_addr)
- : m_addr (buffer_addr)
- {}
+ displaced_step_buffers (gdb::array_view<CORE_ADDR> buffer_addrs)
+ {
+ gdb_assert (buffer_addrs.size () > 0);
+
+ for (CORE_ADDR buffer_addr : buffer_addrs)
+ m_buffers.emplace_back (buffer_addr);
+ }

displaced_step_prepare_status prepare (thread_info *thread,
CORE_ADDR &displaced_pc);
@@ -163,14 +168,33 @@ struct displaced_step_buffer

private:

- CORE_ADDR m_original_pc = 0;
- const CORE_ADDR m_addr;
+ /* State of a single buffer. */
+
+ struct displaced_step_buffer
+ {
+ displaced_step_buffer (CORE_ADDR addr)
+ : addr (addr)
+ {}
+
+ const CORE_ADDR addr;
+
+ /* The original PC of the instruction currently begin stepped. */
+ CORE_ADDR original_pc = 0;
+
+ /* If set, the thread currently using the buffer. If unset, the buffer is not
+ used. */
+ thread_info *current_thread = nullptr;
+
+ /* Saved copy of the bytes in the displaced buffer, to be restored once the
+ buffer is no longer used. */
+ gdb::byte_vector saved_copy;

- /* If set, the thread currently using the buffer. */
- thread_info *m_current_thread = nullptr;
+ /* Closure obtained from gdbarch_displaced_step_copy_insn, to be passed to
+ gdbarch_displaced_step_fixup_insn. */
+ displaced_step_copy_insn_closure_up copy_insn_closure;
+ };

- gdb::byte_vector m_saved_copy;
- displaced_step_copy_insn_closure_up m_copy_insn_closure;
+ std::vector<displaced_step_buffer> m_buffers;
};

#endif /* DISPLACED_STEPPING_H */
diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c
index c5ae4212ab4..c5b20737654 100644
--- a/gdb/frv-linux-tdep.c
+++ b/gdb/frv-linux-tdep.c
@@ -456,7 +456,7 @@ frv_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
static void
frv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Set the sigtramp frame sniffer. */
frame_unwind_append_unwinder (gdbarch, &frv_linux_sigtramp_frame_unwind);
diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c
index a171e582e59..ce85f327d1e 100644
--- a/gdb/hppa-linux-tdep.c
+++ b/gdb/hppa-linux-tdep.c
@@ -489,7 +489,7 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* GNU/Linux is always ELF. */
tdep->is_elf = 1;
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 1b209fd3eff..90ee30969aa 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -832,7 +832,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)

gdb_assert (tdesc_data);

- linux_init_abi (info, gdbarch, true);
+ linux_init_abi (info, gdbarch, 1);

/* GNU/Linux uses ELF. */
i386_elf_init_abi (info, gdbarch);
diff --git a/gdb/ia64-linux-tdep.c b/gdb/ia64-linux-tdep.c
index d6d581ab3dd..3a69f758c11 100644
--- a/gdb/ia64-linux-tdep.c
+++ b/gdb/ia64-linux-tdep.c
@@ -223,7 +223,7 @@ ia64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
static const char *const stap_register_indirection_suffixes[] = { "]",
NULL };

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Set the method of obtaining the sigcontext addresses at which
registers are saved. */
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 767cca4afd0..a3af21790ff 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -166,9 +166,10 @@ enum
static struct gdbarch_data *linux_gdbarch_data_handle;

struct linux_gdbarch_data
- {
- struct type *siginfo_type;
- };
+{
+ struct type *siginfo_type;
+ int num_disp_step_buffers;
+};

static void *
init_linux_gdbarch_data (struct obstack *obstack)
@@ -200,8 +201,8 @@ struct linux_info
if we tried looking it up but failed. */
int vsyscall_range_p = 0;

- /* Inferior's displaced step buffer. */
- gdb::optional<displaced_step_buffer> disp_step_buf;
+ /* Inferior's displaced step buffers. */
+ gdb::optional<displaced_step_buffers> disp_step_bufs;
};

/* Per-inferior data key. */
@@ -2541,15 +2542,25 @@ linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
{
linux_info *per_inferior = get_linux_inferior_data (thread->inf);

- if (!per_inferior->disp_step_buf.has_value ())
+ if (!per_inferior->disp_step_bufs.has_value ())
{
+ /* Figure out the location of the buffers. They are contiguous, starting
+ at DISP_STEP_BUF_ADDR. They are all of size BUF_LEN. */
CORE_ADDR disp_step_buf_addr
= linux_displaced_step_location (thread->inf->gdbarch);
+ int buf_len = gdbarch_max_insn_length (arch);

- per_inferior->disp_step_buf.emplace (disp_step_buf_addr);
+ linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (arch);
+ gdb_assert (gdbarch_data->num_disp_step_buffers > 0);
+
+ std::vector<CORE_ADDR> buffers;
+ for (int i = 0; i < gdbarch_data->num_disp_step_buffers; i++)
+ buffers.push_back (disp_step_buf_addr + i * buf_len);
+
+ per_inferior->disp_step_bufs.emplace (buffers);
}

- return per_inferior->disp_step_buf->prepare (thread, displaced_pc);
+ return per_inferior->disp_step_bufs->prepare (thread, displaced_pc);
}

/* Implementation gdbarch_displaced_step_finish. */
@@ -2559,9 +2570,9 @@ linux_displaced_step_finish (gdbarch *arch, thread_info *thread, gdb_signal sig)
{
linux_info *per_inferior = get_linux_inferior_data (thread->inf);

- gdb_assert (per_inferior->disp_step_buf.has_value ());
+ gdb_assert (per_inferior->disp_step_bufs.has_value ());

- return per_inferior->disp_step_buf->finish (arch, thread, sig);
+ return per_inferior->disp_step_bufs->finish (arch, thread, sig);
}

const displaced_step_copy_insn_closure *
@@ -2570,10 +2581,10 @@ linux_displaced_step_copy_insn_closure_by_addr (inferior *inf, CORE_ADDR addr)
linux_info *per_inferior = linux_inferior_data.get (inf);

if (per_inferior == nullptr
- || !per_inferior->disp_step_buf.has_value ())
+ || !per_inferior->disp_step_bufs.has_value ())
return nullptr;

- return per_inferior->disp_step_buf->copy_insn_closure_by_addr (addr);
+ return per_inferior->disp_step_bufs->copy_insn_closure_by_addr (addr);
}

void
@@ -2582,10 +2593,10 @@ linux_displaced_step_restore_all_in_ptid (inferior *parent_inf, ptid_t ptid)
linux_info *per_inferior = linux_inferior_data.get (parent_inf);

if (per_inferior == nullptr
- || !per_inferior->disp_step_buf.has_value ())
+ || !per_inferior->disp_step_bufs.has_value ())
return;

- per_inferior->disp_step_buf->restore_in_ptid (ptid);
+ per_inferior->disp_step_bufs->restore_in_ptid (ptid);
}

/* See linux-tdep.h. */
@@ -2633,15 +2644,22 @@ show_dump_excluded_mappings (struct ui_file *file, int from_tty,
}

/* To be called from the various GDB_OSABI_LINUX handlers for the
- various GNU/Linux architectures and machine types. */
+ various GNU/Linux architectures and machine types.
+
+ NUM_DISP_STEP_BUFFERS is the number of displaced step buffers to use. If 0,
+ displaced stepping is not supported. */

void
linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
- bool supports_displaced_step)
+ int num_disp_step_buffers)
{
- if (supports_displaced_step)
+ if (num_disp_step_buffers > 0)
{
- set_gdbarch_displaced_step_prepare (gdbarch, linux_displaced_step_prepare);
+ linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (gdbarch);
+ gdbarch_data->num_disp_step_buffers = num_disp_step_buffers;
+
+ set_gdbarch_displaced_step_prepare (gdbarch,
+ linux_displaced_step_prepare);
set_gdbarch_displaced_step_finish (gdbarch, linux_displaced_step_finish);
set_gdbarch_displaced_step_copy_insn_closure_by_addr
(gdbarch, linux_displaced_step_copy_insn_closure_by_addr);
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 0f83dc3c781..723eec3dc10 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -82,7 +82,7 @@ extern void linux_displaced_step_restore_all_in_ptid (inferior *parent_inf,
ptid_t ptid);

extern void linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
- bool supports_displaced_step);
+ int num_disp_step_buffers);

extern int linux_is_uclinux (void);

diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c
index 961d54a2ca2..0a1ff780d8c 100644
--- a/gdb/m32r-linux-tdep.c
+++ b/gdb/m32r-linux-tdep.c
@@ -449,7 +449,7 @@ static void
m32r_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Since EVB register is not available for native debug, we reduce
the number of registers. */
diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c
index 509333558ec..f057915965f 100644
--- a/gdb/m68k-linux-tdep.c
+++ b/gdb/m68k-linux-tdep.c
@@ -385,7 +385,7 @@ m68k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

tdep->jb_pc = M68K_LINUX_JB_PC;
tdep->jb_elt_size = M68K_LINUX_JB_ELEMENT_SIZE;
diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
index 2a91e1bb39a..54f89ec0377 100644
--- a/gdb/microblaze-linux-tdep.c
+++ b/gdb/microblaze-linux-tdep.c
@@ -117,7 +117,7 @@ static void
microblaze_linux_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

set_gdbarch_memory_remove_breakpoint (gdbarch,
microblaze_linux_memory_remove_breakpoint);
diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c
index 9ca59e5b296..ed95c220084 100644
--- a/gdb/mips-linux-tdep.c
+++ b/gdb/mips-linux-tdep.c
@@ -1531,7 +1531,7 @@ mips_linux_init_abi (struct gdbarch_info info,
enum mips_abi abi = mips_abi (gdbarch);
struct tdesc_arch_data *tdesc_data = info.tdesc_data;

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Get the syscall number from the arch's register. */
set_gdbarch_get_syscall_number (gdbarch, mips_linux_get_syscall_number);
diff --git a/gdb/mn10300-linux-tdep.c b/gdb/mn10300-linux-tdep.c
index 27645b1260c..f7586bf3eae 100644
--- a/gdb/mn10300-linux-tdep.c
+++ b/gdb/mn10300-linux-tdep.c
@@ -704,7 +704,7 @@ am33_linux_sigframe_cache_init (const struct tramp_frame *self,
static void
am33_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

set_gdbarch_iterate_over_regset_sections
(gdbarch, am33_iterate_over_regset_sections);
diff --git a/gdb/nios2-linux-tdep.c b/gdb/nios2-linux-tdep.c
index b5c12852c70..0a28c9cca6d 100644
--- a/gdb/nios2-linux-tdep.c
+++ b/gdb/nios2-linux-tdep.c
@@ -219,7 +219,7 @@ nios2_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
diff --git a/gdb/or1k-linux-tdep.c b/gdb/or1k-linux-tdep.c
index 33ddd10e85f..2779fd0d1e6 100644
--- a/gdb/or1k-linux-tdep.c
+++ b/gdb/or1k-linux-tdep.c
@@ -140,7 +140,7 @@ or1k_linux_sigframe_init (const struct tramp_frame *self,
static void
or1k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

set_solib_svr4_fetch_link_map_offsets (gdbarch,
svr4_ilp32_fetch_link_map_offsets);
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index e6c10dd83db..57bdd2d7a72 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -1993,7 +1993,7 @@ ppc_linux_init_abi (struct gdbarch_info info,
static const char *const stap_register_indirection_suffixes[] = { ")",
NULL };

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* PPC GNU/Linux uses either 64-bit or 128-bit long doubles; where
128-bit, they can be either IBM long double or IEEE quad long double.
diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
index a2238ad786e..623c7d9382c 100644
--- a/gdb/riscv-linux-tdep.c
+++ b/gdb/riscv-linux-tdep.c
@@ -159,7 +159,7 @@ riscv_linux_sigframe_init (const struct tramp_frame *self,
static void
riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

set_gdbarch_software_single_step (gdbarch, riscv_software_single_step);

diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 1a7c7fcb9f1..235abd873f7 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -160,7 +160,7 @@ struct ppc_inferior_data
/* This is an optional in case we add more fields to ppc_inferior_data, we
don't want it instantiated as soon as we get the ppc_inferior_data for an
inferior. */
- gdb::optional<displaced_step_buffer> disp_step_buf;
+ gdb::optional<displaced_step_buffers> disp_step_buf;
};

static inferior_key<ppc_inferior_data> ppc_inferior_data_key;
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index 14e92d2c6f3..8588d046bd6 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -1119,7 +1119,7 @@ s390_linux_init_abi_any (struct gdbarch_info info, struct gdbarch *gdbarch)

tdep->s390_syscall_record = s390_linux_syscall_record;

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Register handling. */
set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
diff --git a/gdb/sh-linux-tdep.c b/gdb/sh-linux-tdep.c
index b7c66b70294..84bcd300069 100644
--- a/gdb/sh-linux-tdep.c
+++ b/gdb/sh-linux-tdep.c
@@ -184,7 +184,7 @@ static struct tramp_frame sh_linux_rt_sigreturn_tramp_frame = {
static void
sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* GNU/Linux uses SVR4-style shared libraries. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
diff --git a/gdb/sparc-linux-tdep.c b/gdb/sparc-linux-tdep.c
index 3dbc65ca24d..71759e19fa8 100644
--- a/gdb/sparc-linux-tdep.c
+++ b/gdb/sparc-linux-tdep.c
@@ -422,7 +422,7 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

tdep->gregset = &sparc32_linux_gregset;
tdep->sizeof_gregset = 152;
diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c
index 10a6eb52778..fe1b276bc4e 100644
--- a/gdb/sparc64-linux-tdep.c
+++ b/gdb/sparc64-linux-tdep.c
@@ -365,7 +365,7 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

tdep->gregset = &sparc64_linux_gregset;
tdep->sizeof_gregset = 288;
diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c
index 5a8d7c7f825..7820e2da5a0 100644
--- a/gdb/tic6x-linux-tdep.c
+++ b/gdb/tic6x-linux-tdep.c
@@ -167,7 +167,7 @@ tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

/* Shared library handling. */
set_solib_ops (gdbarch, &dsbt_so_ops);
diff --git a/gdb/tilegx-linux-tdep.c b/gdb/tilegx-linux-tdep.c
index 14cfafe33de..37c0790a190 100644
--- a/gdb/tilegx-linux-tdep.c
+++ b/gdb/tilegx-linux-tdep.c
@@ -111,7 +111,7 @@ tilegx_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
int arch_size = gdbarch_addr_bit (gdbarch);

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

tramp_frame_prepend_unwinder (gdbarch, &tilegx_linux_rt_sigframe);

diff --git a/gdb/xtensa-linux-tdep.c b/gdb/xtensa-linux-tdep.c
index c2aeb8e9397..fccac7d49fc 100644
--- a/gdb/xtensa-linux-tdep.c
+++ b/gdb/xtensa-linux-tdep.c
@@ -110,7 +110,7 @@ xtensa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_gdbarch_num_pseudo_regs (gdbarch, tdep->num_pseudo_regs);
}

- linux_init_abi (info, gdbarch, false);
+ linux_init_abi (info, gdbarch, 0);

set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_ilp32_fetch_link_map_offsets);
--
2.28.0