123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- /* Displaced stepping related things.
- Copyright (C) 2020-2022 Free Software Foundation, Inc.
- 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 "defs.h"
- #include "displaced-stepping.h"
- #include "cli/cli-cmds.h"
- #include "command.h"
- #include "gdbarch.h"
- #include "gdbcore.h"
- #include "gdbthread.h"
- #include "inferior.h"
- #include "regcache.h"
- #include "target/target.h"
- /* Default destructor for displaced_step_copy_insn_closure. */
- displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
- = default;
- bool debug_displaced = false;
- static void
- show_debug_displaced (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
- {
- gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
- }
- displaced_step_prepare_status
- displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
- {
- gdb_assert (!thread->displaced_step_state.in_progress ());
- /* 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);
- /* Search for an unused buffer. */
- displaced_step_buffer *buffer = nullptr;
- displaced_step_prepare_status fail_status
- = 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));
- }
- }
- 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. */
- buffer->saved_copy.resize (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, buffer->addr), safe_strerror (status));
- displaced_debug_printf ("saved %s: %s",
- paddress (arch, buffer->addr),
- displaced_step_dump_bytes
- (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, 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. */
- return DISPLACED_STEP_PREPARE_STATUS_CANT;
- }
- /* Resume execution at the copy. */
- regcache_write_pc (regcache, buffer->addr);
- /* This marks the buffer as being in use. */
- buffer->current_thread = thread;
- /* Save this, now that we know everything went fine. */
- buffer->copy_insn_closure = std::move (copy_insn_closure);
- /* 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;
- }
- static void
- write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
- const gdb_byte *myaddr, int len)
- {
- scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = ptid;
- write_memory (memaddr, myaddr, len);
- }
- static bool
- displaced_step_instruction_executed_successfully (gdbarch *arch,
- gdb_signal signal)
- {
- if (signal != GDB_SIGNAL_TRAP)
- return false;
- if (target_stopped_by_watchpoint ())
- {
- if (gdbarch_have_nonsteppable_watchpoint (arch)
- || target_have_steppable_watchpoint ())
- return false;
- }
- return true;
- }
- displaced_step_finish_status
- displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
- gdb_signal sig)
- {
- gdb_assert (thread->displaced_step_state.in_progress ());
- /* 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 (buffer->copy_insn_closure);
- gdb_assert (copy_insn_closure != 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
- below. */
- thread->inf->displaced_step_state.unavailable = false;
- ULONGEST len = gdbarch_max_insn_length (arch);
- /* Restore memory of the buffer. */
- write_memory_ptid (thread->ptid, buffer->addr,
- buffer->saved_copy.data (), len);
- displaced_debug_printf ("restored %s %s",
- thread->ptid.to_string ().c_str (),
- paddress (arch, 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, copy_insn_closure.get (),
- buffer->original_pc,
- buffer->addr, rc);
- return 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 = 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_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
- {
- for (const displaced_step_buffer &buffer : m_buffers)
- {
- if (addr == buffer.addr)
- return buffer.copy_insn_closure.get ();
- }
- return nullptr;
- }
- void
- displaced_step_buffers::restore_in_ptid (ptid_t ptid)
- {
- for (const displaced_step_buffer &buffer : m_buffers)
- {
- 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, buffer.addr, buffer.saved_copy.data (), len);
- displaced_debug_printf ("restored in ptid %s %s",
- ptid.to_string ().c_str (),
- paddress (arch, buffer.addr));
- }
- }
- void _initialize_displaced_stepping ();
- void
- _initialize_displaced_stepping ()
- {
- add_setshow_boolean_cmd ("displaced", class_maintenance,
- &debug_displaced, _("\
- Set displaced stepping debugging."), _("\
- Show displaced stepping debugging."), _("\
- When non-zero, displaced stepping specific debugging is enabled."),
- NULL,
- show_debug_displaced,
- &setdebuglist, &showdebuglist);
- }
|