displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure() = default;
displaced_step_prepare_status
-single_displaced_buffer_manager::prepare (thread_info *thread)
+multiple_displaced_buffer_manager::prepare (thread_info *thread)
{
- /* Is a thread currently using the buffer? */
- if (m_current_thread != nullptr)
+ gdb_assert (!thread->displaced_step_state.in_progress ());
+ displaced_step_buffer_state *buffer = nullptr;
+
+ /* Sanity check. */
+ for (displaced_step_buffer_state &buf : m_buffers)
+ gdb_assert (buf.m_current_thread != thread);
+
+ /* Search for an unused buffer. */
+ for (displaced_step_buffer_state &candidate : m_buffers)
{
- /* If so, it better not be this thread. */
- gdb_assert (thread != m_current_thread);
- return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
+ if (candidate.m_current_thread == nullptr)
+ {
+ buffer = &candidate;
+ break;
+ }
}
+ if (buffer == nullptr)
+ return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
+
gdbarch *arch = thread->arch ();
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: selected buffer at %s\n",
+ paddress (arch, buffer->m_buffer_addr));
+
struct regcache *regcache = thread->regcache ();
ULONGEST len = gdbarch_max_insn_length (arch);
- m_original_pc = regcache_read_pc (regcache);
+ buffer->m_original_pc = regcache_read_pc (regcache);
/* Save the original contents of the displaced stepping buffer. */
- m_saved_copy.resize (len);
+ buffer->m_saved_copy.resize (len);
- int status = target_read_memory (m_buffer_addr, m_saved_copy.data (), len);
+ int status = target_read_memory (buffer->m_buffer_addr, buffer->m_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_buffer_addr), safe_strerror (status));
+ paddress (arch, buffer->m_buffer_addr), safe_strerror (status));
if (debug_displaced)
{
fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ",
- paddress (arch, m_buffer_addr));
- displaced_step_dump_bytes (gdb_stdlog, m_saved_copy.data (), len);
+ paddress (arch, buffer->m_buffer_addr));
+ displaced_step_dump_bytes (gdb_stdlog, buffer->m_saved_copy.data (), len);
};
- m_copy_insn_closure = gdbarch_displaced_step_copy_insn (arch,
- m_original_pc,
- m_buffer_addr,
- regcache);
- if (m_copy_insn_closure == nullptr)
+ buffer->m_copy_insn_closure
+ = gdbarch_displaced_step_copy_insn (arch, buffer->m_original_pc,
+ buffer->m_buffer_addr, regcache);
+ if (buffer->m_copy_insn_closure == nullptr)
{
/* The architecture doesn't know how or want to displaced step
this instruction or instruction sequence. Fallback to
try
{
/* Resume execution at the copy. */
- regcache_write_pc (regcache, m_buffer_addr);
+ regcache_write_pc (regcache, buffer->m_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 ();
+ buffer->m_copy_insn_closure.reset ();
return DISPLACED_STEP_PREPARE_STATUS_ERROR;
}
/* This marks the buffer as being in use. */
- m_current_thread = thread;
+ buffer->m_current_thread = thread;
return DISPLACED_STEP_PREPARE_STATUS_OK;
}
}
displaced_step_finish_status
-single_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread,
- gdb_signal sig)
+multiple_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread,
+ gdb_signal sig)
{
displaced_step_finish_status status;
+ displaced_step_buffer_state *buffer = nullptr;
- gdb_assert (thread == m_current_thread);
+ gdb_assert (thread->displaced_step_state.in_progress ());
+
+ /* Find the buffer this thread was using. */
+ for (displaced_step_buffer_state &candidate : m_buffers)
+ {
+ if (thread == candidate.m_current_thread)
+ {
+ buffer = &candidate;
+ break;
+ }
+ }
+
+ gdb_assert (buffer != nullptr);
ULONGEST len = gdbarch_max_insn_length (arch);
- write_memory_ptid (thread->ptid, m_buffer_addr,
- m_saved_copy.data (), len);
+ /* Restore memory of the buffer. */
+ write_memory_ptid (thread->ptid, buffer->m_buffer_addr,
+ buffer->m_saved_copy.data (), len);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n",
target_pid_to_str (thread->ptid).c_str (),
- paddress (arch, m_buffer_addr));
+ paddress (arch, buffer->m_buffer_addr));
regcache *rc = get_thread_regcache (thread);
bool instruction_executed_successfully
= displaced_step_instruction_executed_successfully (arch, sig);
-
if (instruction_executed_successfully)
{
- gdbarch_displaced_step_fixup (arch, m_copy_insn_closure.get (), m_original_pc,
- m_buffer_addr, rc);
+ gdbarch_displaced_step_fixup (arch, buffer->m_copy_insn_closure.get (),
+ buffer->m_original_pc,
+ buffer->m_buffer_addr, rc);
status = DISPLACED_STEP_FINISH_STATUS_OK;
}
else
/* 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_buffer_addr);
+ pc = buffer->m_original_pc + (pc - buffer->m_buffer_addr);
regcache_write_pc (rc, pc);
status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
}
- m_copy_insn_closure.reset ();
- m_current_thread = nullptr;
+ buffer->m_copy_insn_closure.reset ();
+ buffer->m_current_thread = nullptr;
return status;
}
#ifndef DISPLACED_STEPPING_H
#define DISPLACED_STEPPING_H
+#include "gdbsupport/array-view.h"
#include "gdbsupport/byte-vector.h"
struct gdbarch;
gdbarch *m_original_gdbarch = nullptr;
};
+struct displaced_step_buffer_state
+{
+ displaced_step_buffer_state (CORE_ADDR buffer_addr)
+ : m_buffer_addr (buffer_addr)
+ {}
+
+ const CORE_ADDR m_buffer_addr;
+
+ /* When a displaced step operation is using this buffer, this is the original
+ PC of the instruction currently begin stepped. */
+ CORE_ADDR m_original_pc = 0;
+
+ /* If set, the thread currently using the buffer. If unset, the buffer is not
+ used. */
+ thread_info *m_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 m_saved_copy;
+
+ /* Closure obtained from gdbarch_displaced_step_copy_insn, to be passed to
+ gdbarch_displaced_step_fixup_insn. */
+ displaced_step_copy_insn_closure_up m_copy_insn_closure;
+};
+
/* Manage access to a single displaced stepping buffer, without any
sharing. */
-struct single_displaced_buffer_manager
+struct multiple_displaced_buffer_manager
{
- single_displaced_buffer_manager (CORE_ADDR buffer_addr)
- : m_buffer_addr (buffer_addr)
- {}
+ multiple_displaced_buffer_manager (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);
gdb_signal sig);
private:
-
- CORE_ADDR m_original_pc;
- CORE_ADDR m_buffer_addr;
-
- /* If set, the thread currently using the buffer. */
- thread_info *m_current_thread = nullptr;
-
- gdb::byte_vector m_saved_copy;
- displaced_step_copy_insn_closure_up m_copy_insn_closure;
+ std::vector<displaced_step_buffer_state> m_buffers;
};