1 /* Displaced stepping related things. 2 3 Copyright (C) 2020-2021 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #ifndef DISPLACED_STEPPING_H 21 #define DISPLACED_STEPPING_H 22 23 #include "gdbsupport/array-view.h" 24 #include "gdbsupport/byte-vector.h" 25 26 struct gdbarch; 27 struct thread_info; 28 29 /* True if we are debugging displaced stepping. */ 30 31 extern bool debug_displaced; 32 33 /* Print a "displaced" debug statement. */ 34 35 #define displaced_debug_printf(fmt, ...) \ 36 debug_prefixed_printf_cond (debug_displaced, "displaced",fmt, ##__VA_ARGS__) 37 38 enum displaced_step_prepare_status 39 { 40 /* A displaced stepping buffer was successfully allocated and prepared. */ 41 DISPLACED_STEP_PREPARE_STATUS_OK, 42 43 /* This particular instruction can't be displaced stepped, GDB should fall 44 back on in-line stepping. */ 45 DISPLACED_STEP_PREPARE_STATUS_CANT, 46 47 /* Not enough resources are available at this time, try again later. */ 48 DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE, 49 }; 50 51 enum displaced_step_finish_status 52 { 53 /* Either the instruction was stepped and fixed up, or the specified thread 54 wasn't executing a displaced step (in which case there's nothing to 55 finish). */ 56 DISPLACED_STEP_FINISH_STATUS_OK, 57 58 /* The thread started a displaced step, but didn't complete it. */ 59 DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED, 60 }; 61 62 /* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to 63 the matching displaced_step_fixup method. */ 64 65 struct displaced_step_copy_insn_closure 66 { 67 virtual ~displaced_step_copy_insn_closure () = 0; 68 }; 69 70 using displaced_step_copy_insn_closure_up 71 = std::unique_ptr<displaced_step_copy_insn_closure>; 72 73 /* A simple displaced step closure that contains only a byte buffer. */ 74 75 struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure 76 { buf_displaced_step_copy_insn_closurebuf_displaced_step_copy_insn_closure77 buf_displaced_step_copy_insn_closure (int buf_size) 78 : buf (buf_size) 79 {} 80 81 /* The content of this buffer is up to the user of the class, but typically 82 original instruction bytes, used during fixup to determine what needs to 83 be fixed up. */ 84 gdb::byte_vector buf; 85 }; 86 87 /* Per-inferior displaced stepping state. */ 88 89 struct displaced_step_inferior_state 90 { displaced_step_inferior_statedisplaced_step_inferior_state91 displaced_step_inferior_state () 92 { 93 reset (); 94 } 95 96 /* Put this object back in its original state. */ resetdisplaced_step_inferior_state97 void reset () 98 { 99 failed_before = false; 100 in_progress_count = 0; 101 unavailable = false; 102 } 103 104 /* True if preparing a displaced step ever failed. If so, we won't 105 try displaced stepping for this inferior again. */ 106 bool failed_before; 107 108 /* Number of displaced steps in progress for this inferior. */ 109 unsigned int in_progress_count; 110 111 /* If true, this tells GDB that it's not worth asking the gdbarch displaced 112 stepping implementation to prepare a displaced step, because it would 113 return UNAVAILABLE. This is set and reset by the gdbarch in the 114 displaced_step_prepare and displaced_step_finish methods. */ 115 bool unavailable; 116 }; 117 118 /* Per-thread displaced stepping state. */ 119 120 struct displaced_step_thread_state 121 { 122 /* Return true if this thread is currently executing a displaced step. */ in_progressdisplaced_step_thread_state123 bool in_progress () const 124 { 125 return m_original_gdbarch != nullptr; 126 } 127 128 /* Return the gdbarch of the thread prior to the step. */ get_original_gdbarchdisplaced_step_thread_state129 gdbarch *get_original_gdbarch () const 130 { 131 return m_original_gdbarch; 132 } 133 134 /* Mark this thread as currently executing a displaced step. 135 136 ORIGINAL_GDBARCH is the current gdbarch of the thread (before the step 137 is executed). */ setdisplaced_step_thread_state138 void set (gdbarch *original_gdbarch) 139 { 140 m_original_gdbarch = original_gdbarch; 141 } 142 143 /* Mark this thread as no longer executing a displaced step. */ resetdisplaced_step_thread_state144 void reset () 145 { 146 m_original_gdbarch = nullptr; 147 } 148 149 private: 150 gdbarch *m_original_gdbarch = nullptr; 151 }; 152 153 /* Control access to multiple displaced stepping buffers at fixed addresses. */ 154 155 struct displaced_step_buffers 156 { displaced_step_buffersdisplaced_step_buffers157 explicit displaced_step_buffers (gdb::array_view<CORE_ADDR> buffer_addrs) 158 { 159 gdb_assert (buffer_addrs.size () > 0); 160 161 m_buffers.reserve (buffer_addrs.size ()); 162 163 for (CORE_ADDR buffer_addr : buffer_addrs) 164 m_buffers.emplace_back (buffer_addr); 165 } 166 167 displaced_step_prepare_status prepare (thread_info *thread, 168 CORE_ADDR &displaced_pc); 169 170 displaced_step_finish_status finish (gdbarch *arch, thread_info *thread, 171 gdb_signal sig); 172 173 const displaced_step_copy_insn_closure * 174 copy_insn_closure_by_addr (CORE_ADDR addr); 175 176 void restore_in_ptid (ptid_t ptid); 177 178 private: 179 180 /* State of a single buffer. */ 181 182 struct displaced_step_buffer 183 { displaced_step_bufferdisplaced_step_buffers::displaced_step_buffer184 explicit displaced_step_buffer (CORE_ADDR addr) 185 : addr (addr) 186 {} 187 188 /* Address of the buffer. */ 189 const CORE_ADDR addr; 190 191 /* The original PC of the instruction currently being stepped. */ 192 CORE_ADDR original_pc = 0; 193 194 /* If set, the thread currently using the buffer. If unset, the buffer is not 195 used. */ 196 thread_info *current_thread = nullptr; 197 198 /* Saved copy of the bytes in the displaced buffer, to be restored once the 199 buffer is no longer used. */ 200 gdb::byte_vector saved_copy; 201 202 /* Closure obtained from gdbarch_displaced_step_copy_insn, to be passed to 203 gdbarch_displaced_step_fixup_insn. */ 204 displaced_step_copy_insn_closure_up copy_insn_closure; 205 }; 206 207 std::vector<displaced_step_buffer> m_buffers; 208 }; 209 210 #endif /* DISPLACED_STEPPING_H */ 211