|  | /* Displaced stepping related things. | 
|  |  | 
|  | Copyright (C) 2020-2023 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/>.  */ | 
|  |  | 
|  | #ifndef DISPLACED_STEPPING_H | 
|  | #define DISPLACED_STEPPING_H | 
|  |  | 
|  | #include "gdbsupport/array-view.h" | 
|  | #include "gdbsupport/byte-vector.h" | 
|  |  | 
|  | struct gdbarch; | 
|  | struct thread_info; | 
|  |  | 
|  | /* True if we are debugging displaced stepping.  */ | 
|  |  | 
|  | extern bool debug_displaced; | 
|  |  | 
|  | /* Print a "displaced" debug statement.  */ | 
|  |  | 
|  | #define displaced_debug_printf(fmt, ...) \ | 
|  | debug_prefixed_printf_cond (debug_displaced, "displaced",fmt, ##__VA_ARGS__) | 
|  |  | 
|  | enum displaced_step_prepare_status | 
|  | { | 
|  | /* A displaced stepping buffer was successfully allocated and prepared.  */ | 
|  | DISPLACED_STEP_PREPARE_STATUS_OK, | 
|  |  | 
|  | /* This particular instruction can't be displaced stepped, GDB should fall | 
|  | back on in-line stepping.  */ | 
|  | DISPLACED_STEP_PREPARE_STATUS_CANT, | 
|  |  | 
|  | /* Not enough resources are available at this time, try again later.  */ | 
|  | DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE, | 
|  | }; | 
|  |  | 
|  | enum displaced_step_finish_status | 
|  | { | 
|  | /* Either the instruction was stepped and fixed up, or the specified thread | 
|  | wasn't executing a displaced step (in which case there's nothing to | 
|  | finish). */ | 
|  | DISPLACED_STEP_FINISH_STATUS_OK, | 
|  |  | 
|  | /* The thread started a displaced step, but didn't complete it.  */ | 
|  | DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED, | 
|  | }; | 
|  |  | 
|  | /* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to | 
|  | the matching displaced_step_fixup method.  */ | 
|  |  | 
|  | struct displaced_step_copy_insn_closure | 
|  | { | 
|  | virtual ~displaced_step_copy_insn_closure () = 0; | 
|  | }; | 
|  |  | 
|  | using displaced_step_copy_insn_closure_up | 
|  | = std::unique_ptr<displaced_step_copy_insn_closure>; | 
|  |  | 
|  | /* A simple displaced step closure that contains only a byte buffer.  */ | 
|  |  | 
|  | struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure | 
|  | { | 
|  | buf_displaced_step_copy_insn_closure (int buf_size) | 
|  | : buf (buf_size) | 
|  | {} | 
|  |  | 
|  | /* The content of this buffer is up to the user of the class, but typically | 
|  | original instruction bytes, used during fixup to determine what needs to | 
|  | be fixed up.  */ | 
|  | gdb::byte_vector buf; | 
|  | }; | 
|  |  | 
|  | /* Per-inferior displaced stepping state.  */ | 
|  |  | 
|  | struct displaced_step_inferior_state | 
|  | { | 
|  | displaced_step_inferior_state () | 
|  | { | 
|  | reset (); | 
|  | } | 
|  |  | 
|  | /* Put this object back in its original state.  */ | 
|  | void reset () | 
|  | { | 
|  | failed_before = false; | 
|  | in_progress_count = 0; | 
|  | unavailable = false; | 
|  | } | 
|  |  | 
|  | /* True if preparing a displaced step ever failed.  If so, we won't | 
|  | try displaced stepping for this inferior again.  */ | 
|  | bool failed_before; | 
|  |  | 
|  | /* Number of displaced steps in progress for this inferior.  */ | 
|  | unsigned int in_progress_count; | 
|  |  | 
|  | /* If true, this tells GDB that it's not worth asking the gdbarch displaced | 
|  | stepping implementation to prepare a displaced step, because it would | 
|  | return UNAVAILABLE.  This is set and reset by the gdbarch in the | 
|  | displaced_step_prepare and displaced_step_finish methods.  */ | 
|  | bool unavailable; | 
|  | }; | 
|  |  | 
|  | /* Per-thread displaced stepping state.  */ | 
|  |  | 
|  | struct displaced_step_thread_state | 
|  | { | 
|  | /* Return true if this thread is currently executing a displaced step.  */ | 
|  | bool in_progress () const | 
|  | { | 
|  | return m_original_gdbarch != nullptr; | 
|  | } | 
|  |  | 
|  | /* Return the gdbarch of the thread prior to the step.  */ | 
|  | gdbarch *get_original_gdbarch () const | 
|  | { | 
|  | return m_original_gdbarch; | 
|  | } | 
|  |  | 
|  | /* Mark this thread as currently executing a displaced step. | 
|  |  | 
|  | ORIGINAL_GDBARCH is the current gdbarch of the thread (before the step | 
|  | is executed).  */ | 
|  | void set (gdbarch *original_gdbarch) | 
|  | { | 
|  | m_original_gdbarch = original_gdbarch; | 
|  | } | 
|  |  | 
|  | /* Mark this thread as no longer executing a displaced step.  */ | 
|  | void reset () | 
|  | { | 
|  | m_original_gdbarch = nullptr; | 
|  | } | 
|  |  | 
|  | private: | 
|  | gdbarch *m_original_gdbarch = nullptr; | 
|  | }; | 
|  |  | 
|  | /* Control access to multiple displaced stepping buffers at fixed addresses.  */ | 
|  |  | 
|  | struct displaced_step_buffers | 
|  | { | 
|  | explicit displaced_step_buffers (gdb::array_view<CORE_ADDR> buffer_addrs) | 
|  | { | 
|  | gdb_assert (buffer_addrs.size () > 0); | 
|  |  | 
|  | m_buffers.reserve (buffer_addrs.size ()); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | displaced_step_finish_status finish (gdbarch *arch, thread_info *thread, | 
|  | const target_waitstatus &status); | 
|  |  | 
|  | const displaced_step_copy_insn_closure * | 
|  | copy_insn_closure_by_addr (CORE_ADDR addr); | 
|  |  | 
|  | void restore_in_ptid (ptid_t ptid); | 
|  |  | 
|  | private: | 
|  |  | 
|  | /* State of a single buffer.  */ | 
|  |  | 
|  | struct displaced_step_buffer | 
|  | { | 
|  | explicit displaced_step_buffer (CORE_ADDR addr) | 
|  | : addr (addr) | 
|  | {} | 
|  |  | 
|  | /* Address of the buffer.  */ | 
|  | const CORE_ADDR addr; | 
|  |  | 
|  | /* The original PC of the instruction currently being 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; | 
|  |  | 
|  | /* 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; | 
|  | }; | 
|  |  | 
|  | std::vector<displaced_step_buffer> m_buffers; | 
|  | }; | 
|  |  | 
|  | #endif /* DISPLACED_STEPPING_H */ |