1 /* Displaced stepping related things.
3 Copyright (C) 2020-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
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.
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.
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/>. */
20 #include "displaced-stepping.h"
22 #include "cli/cli-cmds.h"
26 #include "gdbthread.h"
29 #include "target/target.h"
31 /* Default destructor for displaced_step_copy_insn_closure. */
33 displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
36 bool debug_displaced
= false;
39 show_debug_displaced (struct ui_file
*file
, int from_tty
,
40 struct cmd_list_element
*c
, const char *value
)
42 gdb_printf (file
, _("Displace stepping debugging is %s.\n"), value
);
45 displaced_step_prepare_status
46 displaced_step_buffers::prepare (thread_info
*thread
, CORE_ADDR
&displaced_pc
)
48 gdb_assert (!thread
->displaced_step_state
.in_progress ());
50 /* Sanity check: the thread should not be using a buffer at this point. */
51 for (displaced_step_buffer
&buf
: m_buffers
)
52 gdb_assert (buf
.current_thread
!= thread
);
54 regcache
*regcache
= get_thread_regcache (thread
);
55 gdbarch
*arch
= regcache
->arch ();
56 ULONGEST len
= gdbarch_displaced_step_buffer_length (arch
);
58 /* Search for an unused buffer. */
59 displaced_step_buffer
*buffer
= nullptr;
60 displaced_step_prepare_status fail_status
61 = DISPLACED_STEP_PREPARE_STATUS_CANT
;
63 for (displaced_step_buffer
&candidate
: m_buffers
)
65 bool bp_in_range
= breakpoint_in_range_p (thread
->inf
->aspace
.get (),
67 bool is_free
= candidate
.current_thread
== nullptr;
78 /* This buffer would be suitable, but it's used right now. */
79 fail_status
= DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE
;
84 /* There's a breakpoint set in the scratch pad location range
85 (which is usually around the entry point). We'd either
86 install it before resuming, which would overwrite/corrupt the
87 scratch pad, or if it was already inserted, this displaced
88 step would overwrite it. The latter is OK in the sense that
89 we already assume that no thread is going to execute the code
90 in the scratch pad range (after initial startup) anyway, but
91 the former is unacceptable. Simply punt and fallback to
92 stepping over this breakpoint in-line. */
93 displaced_debug_printf ("breakpoint set in displaced stepping "
94 "buffer at %s, can't use.",
95 paddress (arch
, candidate
.addr
));
99 if (buffer
== nullptr)
102 displaced_debug_printf ("selected buffer at %s",
103 paddress (arch
, buffer
->addr
));
105 /* Save the original PC of the thread. */
106 buffer
->original_pc
= regcache_read_pc (regcache
);
108 /* Return displaced step buffer address to caller. */
109 displaced_pc
= buffer
->addr
;
111 /* Save the original contents of the displaced stepping buffer. */
112 buffer
->saved_copy
.resize (len
);
114 int status
= target_read_memory (buffer
->addr
,
115 buffer
->saved_copy
.data (), len
);
117 throw_error (MEMORY_ERROR
,
118 _("Error accessing memory address %s (%s) for "
119 "displaced-stepping scratch space."),
120 paddress (arch
, buffer
->addr
), safe_strerror (status
));
122 displaced_debug_printf ("saved %s: %s",
123 paddress (arch
, buffer
->addr
),
124 bytes_to_string (buffer
->saved_copy
).c_str ());
126 /* Save this in a local variable first, so it's released if code below
128 displaced_step_copy_insn_closure_up copy_insn_closure
129 = gdbarch_displaced_step_copy_insn (arch
, buffer
->original_pc
,
130 buffer
->addr
, regcache
);
132 if (copy_insn_closure
== nullptr)
134 /* The architecture doesn't know how or want to displaced step
135 this instruction or instruction sequence. Fallback to
136 stepping over the breakpoint in-line. */
137 return DISPLACED_STEP_PREPARE_STATUS_CANT
;
140 /* This marks the buffer as being in use. */
141 buffer
->current_thread
= thread
;
143 /* Save this, now that we know everything went fine. */
144 buffer
->copy_insn_closure
= std::move (copy_insn_closure
);
146 /* Reset the displaced step buffer state if we failed to write PC.
147 Otherwise we will prevent this buffer from being used, as it will
148 always have a thread in buffer->current_thread. */
149 auto reset_buffer
= make_scope_exit
152 buffer
->current_thread
= nullptr;
153 buffer
->copy_insn_closure
.reset ();
156 /* Adjust the PC so it points to the displaced step buffer address that will
157 be used. This needs to be done after we save the copy_insn_closure, as
158 some architectures (Arm, for one) need that information so they can adjust
159 other data as needed. In particular, Arm needs to know if the instruction
160 being executed in the displaced step buffer is thumb or not. Without that
161 information, things will be very wrong in a random way. */
162 regcache_write_pc (regcache
, buffer
->addr
);
164 /* PC update successful. Discard the displaced step state rollback. */
165 reset_buffer
.release ();
167 /* Tell infrun not to try preparing a displaced step again for this inferior if
168 all buffers are taken. */
169 thread
->inf
->displaced_step_state
.unavailable
= true;
170 for (const displaced_step_buffer
&buf
: m_buffers
)
172 if (buf
.current_thread
== nullptr)
174 thread
->inf
->displaced_step_state
.unavailable
= false;
179 return DISPLACED_STEP_PREPARE_STATUS_OK
;
183 write_memory_ptid (ptid_t ptid
, CORE_ADDR memaddr
,
184 const gdb_byte
*myaddr
, int len
)
186 scoped_restore save_inferior_ptid
= make_scoped_restore (&inferior_ptid
);
188 inferior_ptid
= ptid
;
189 write_memory (memaddr
, myaddr
, len
);
193 displaced_step_instruction_executed_successfully
194 (gdbarch
*arch
, const target_waitstatus
&status
)
196 if (status
.kind () == TARGET_WAITKIND_STOPPED
197 && status
.sig () != GDB_SIGNAL_TRAP
)
200 /* All other (thread event) waitkinds can only happen if the
201 instruction fully executed. For example, a fork, or a syscall
202 entry can only happen if the syscall instruction actually
205 if (target_stopped_by_watchpoint ())
207 if (gdbarch_have_nonsteppable_watchpoint (arch
)
208 || target_have_steppable_watchpoint ())
215 displaced_step_finish_status
216 displaced_step_buffers::finish (gdbarch
*arch
, thread_info
*thread
,
217 const target_waitstatus
&status
)
219 gdb_assert (thread
->displaced_step_state
.in_progress ());
221 /* Find the buffer this thread was using. */
222 displaced_step_buffer
*buffer
= nullptr;
224 for (displaced_step_buffer
&candidate
: m_buffers
)
226 if (candidate
.current_thread
== thread
)
233 gdb_assert (buffer
!= nullptr);
235 /* Move this to a local variable so it's released in case something goes
237 displaced_step_copy_insn_closure_up copy_insn_closure
238 = std::move (buffer
->copy_insn_closure
);
239 gdb_assert (copy_insn_closure
!= nullptr);
241 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
242 in case something goes wrong below. */
243 buffer
->current_thread
= nullptr;
245 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
246 step again for this inferior. Do that here in case something goes wrong
248 thread
->inf
->displaced_step_state
.unavailable
= false;
250 ULONGEST len
= gdbarch_displaced_step_buffer_length (arch
);
252 /* Restore memory of the buffer. */
253 write_memory_ptid (thread
->ptid
, buffer
->addr
,
254 buffer
->saved_copy
.data (), len
);
256 displaced_debug_printf ("restored %s %s",
257 thread
->ptid
.to_string ().c_str (),
258 paddress (arch
, buffer
->addr
));
260 /* If the thread exited while stepping, we are done. The code above
261 made the buffer available again, and we restored the bytes in the
262 buffer. We don't want to run the fixup: since the thread is now
263 dead there's nothing to adjust. */
264 if (status
.kind () == TARGET_WAITKIND_THREAD_EXITED
)
265 return DISPLACED_STEP_FINISH_STATUS_OK
;
267 regcache
*rc
= get_thread_regcache (thread
);
269 bool instruction_executed_successfully
270 = displaced_step_instruction_executed_successfully (arch
, status
);
272 gdbarch_displaced_step_fixup (arch
, copy_insn_closure
.get (),
273 buffer
->original_pc
, buffer
->addr
,
274 rc
, instruction_executed_successfully
);
276 return (instruction_executed_successfully
277 ? DISPLACED_STEP_FINISH_STATUS_OK
278 : DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED
);
281 const displaced_step_copy_insn_closure
*
282 displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr
)
284 for (const displaced_step_buffer
&buffer
: m_buffers
)
286 /* Make sure we have active buffers to compare to. */
287 if (buffer
.current_thread
!= nullptr && addr
== buffer
.addr
)
289 /* The closure information should always be available. */
290 gdb_assert (buffer
.copy_insn_closure
.get () != nullptr);
291 return buffer
.copy_insn_closure
.get ();
299 displaced_step_buffers::restore_in_ptid (ptid_t ptid
)
301 for (const displaced_step_buffer
&buffer
: m_buffers
)
303 if (buffer
.current_thread
== nullptr)
306 regcache
*regcache
= get_thread_regcache (buffer
.current_thread
);
307 gdbarch
*arch
= regcache
->arch ();
308 ULONGEST len
= gdbarch_displaced_step_buffer_length (arch
);
310 write_memory_ptid (ptid
, buffer
.addr
, buffer
.saved_copy
.data (), len
);
312 displaced_debug_printf ("restored in ptid %s %s",
313 ptid
.to_string ().c_str (),
314 paddress (arch
, buffer
.addr
));
318 void _initialize_displaced_stepping ();
320 _initialize_displaced_stepping ()
322 add_setshow_boolean_cmd ("displaced", class_maintenance
,
323 &debug_displaced
, _("\
324 Set displaced stepping debugging."), _("\
325 Show displaced stepping debugging."), _("\
326 When non-zero, displaced stepping specific debugging is enabled."),
328 show_debug_displaced
,
329 &setdebuglist
, &showdebuglist
);