1 /* Displaced stepping related things.
3 Copyright (C) 2020-2022 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/>. */
21 #include "displaced-stepping.h"
23 #include "cli/cli-cmds.h"
27 #include "gdbthread.h"
30 #include "target/target.h"
32 /* Default destructor for displaced_step_copy_insn_closure. */
34 displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
37 bool debug_displaced = false;
40 show_debug_displaced (struct ui_file *file, int from_tty,
41 struct cmd_list_element *c, const char *value)
43 gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
46 displaced_step_prepare_status
47 displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
49 gdb_assert (!thread->displaced_step_state.in_progress ());
51 /* Sanity check: the thread should not be using a buffer at this point. */
52 for (displaced_step_buffer &buf : m_buffers)
53 gdb_assert (buf.current_thread != thread);
55 regcache *regcache = get_thread_regcache (thread);
56 const address_space *aspace = regcache->aspace ();
57 gdbarch *arch = regcache->arch ();
58 ULONGEST len = gdbarch_max_insn_length (arch);
60 /* Search for an unused buffer. */
61 displaced_step_buffer *buffer = nullptr;
62 displaced_step_prepare_status fail_status
63 = DISPLACED_STEP_PREPARE_STATUS_CANT;
65 for (displaced_step_buffer &candidate : m_buffers)
67 bool bp_in_range = breakpoint_in_range_p (aspace, candidate.addr, len);
68 bool is_free = candidate.current_thread == nullptr;
79 /* This buffer would be suitable, but it's used right now. */
80 fail_status = DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
85 /* There's a breakpoint set in the scratch pad location range
86 (which is usually around the entry point). We'd either
87 install it before resuming, which would overwrite/corrupt the
88 scratch pad, or if it was already inserted, this displaced
89 step would overwrite it. The latter is OK in the sense that
90 we already assume that no thread is going to execute the code
91 in the scratch pad range (after initial startup) anyway, but
92 the former is unacceptable. Simply punt and fallback to
93 stepping over this breakpoint in-line. */
94 displaced_debug_printf ("breakpoint set in displaced stepping "
95 "buffer at %s, can't use.",
96 paddress (arch, candidate.addr));
100 if (buffer == nullptr)
103 displaced_debug_printf ("selected buffer at %s",
104 paddress (arch, buffer->addr));
106 /* Save the original PC of the thread. */
107 buffer->original_pc = regcache_read_pc (regcache);
109 /* Return displaced step buffer address to caller. */
110 displaced_pc = buffer->addr;
112 /* Save the original contents of the displaced stepping buffer. */
113 buffer->saved_copy.resize (len);
115 int status = target_read_memory (buffer->addr,
116 buffer->saved_copy.data (), len);
118 throw_error (MEMORY_ERROR,
119 _("Error accessing memory address %s (%s) for "
120 "displaced-stepping scratch space."),
121 paddress (arch, buffer->addr), safe_strerror (status));
123 displaced_debug_printf ("saved %s: %s",
124 paddress (arch, buffer->addr),
125 displaced_step_dump_bytes
126 (buffer->saved_copy.data (), len).c_str ());
128 /* Save this in a local variable first, so it's released if code below
130 displaced_step_copy_insn_closure_up copy_insn_closure
131 = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc,
132 buffer->addr, regcache);
134 if (copy_insn_closure == nullptr)
136 /* The architecture doesn't know how or want to displaced step
137 this instruction or instruction sequence. Fallback to
138 stepping over the breakpoint in-line. */
139 return DISPLACED_STEP_PREPARE_STATUS_CANT;
142 /* This marks the buffer as being in use. */
143 buffer->current_thread = thread;
145 /* Save this, now that we know everything went fine. */
146 buffer->copy_insn_closure = std::move (copy_insn_closure);
148 /* Reset the displaced step buffer state if we failed to write PC.
149 Otherwise we will prevent this buffer from being used, as it will
150 always have a thread in buffer->current_thread. */
151 auto reset_buffer = make_scope_exit
154 buffer->current_thread = nullptr;
155 buffer->copy_insn_closure.reset ();
158 /* Adjust the PC so it points to the displaced step buffer address that will
159 be used. This needs to be done after we save the copy_insn_closure, as
160 some architectures (Arm, for one) need that information so they can adjust
161 other data as needed. In particular, Arm needs to know if the instruction
162 being executed in the displaced step buffer is thumb or not. Without that
163 information, things will be very wrong in a random way. */
164 regcache_write_pc (regcache, buffer->addr);
166 /* PC update successful. Discard the displaced step state rollback. */
167 reset_buffer.release ();
169 /* Tell infrun not to try preparing a displaced step again for this inferior if
170 all buffers are taken. */
171 thread->inf->displaced_step_state.unavailable = true;
172 for (const displaced_step_buffer &buf : m_buffers)
174 if (buf.current_thread == nullptr)
176 thread->inf->displaced_step_state.unavailable = false;
181 return DISPLACED_STEP_PREPARE_STATUS_OK;
185 write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
186 const gdb_byte *myaddr, int len)
188 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
190 inferior_ptid = ptid;
191 write_memory (memaddr, myaddr, len);
195 displaced_step_instruction_executed_successfully (gdbarch *arch,
198 if (signal != GDB_SIGNAL_TRAP)
201 if (target_stopped_by_watchpoint ())
203 if (gdbarch_have_nonsteppable_watchpoint (arch)
204 || target_have_steppable_watchpoint ())
211 displaced_step_finish_status
212 displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
215 gdb_assert (thread->displaced_step_state.in_progress ());
217 /* Find the buffer this thread was using. */
218 displaced_step_buffer *buffer = nullptr;
220 for (displaced_step_buffer &candidate : m_buffers)
222 if (candidate.current_thread == thread)
229 gdb_assert (buffer != nullptr);
231 /* Move this to a local variable so it's released in case something goes
233 displaced_step_copy_insn_closure_up copy_insn_closure
234 = std::move (buffer->copy_insn_closure);
235 gdb_assert (copy_insn_closure != nullptr);
237 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
238 in case something goes wrong below. */
239 buffer->current_thread = nullptr;
241 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
242 step again for this inferior. Do that here in case something goes wrong
244 thread->inf->displaced_step_state.unavailable = false;
246 ULONGEST len = gdbarch_max_insn_length (arch);
248 /* Restore memory of the buffer. */
249 write_memory_ptid (thread->ptid, buffer->addr,
250 buffer->saved_copy.data (), len);
252 displaced_debug_printf ("restored %s %s",
253 thread->ptid.to_string ().c_str (),
254 paddress (arch, buffer->addr));
256 regcache *rc = get_thread_regcache (thread);
258 bool instruction_executed_successfully
259 = displaced_step_instruction_executed_successfully (arch, sig);
261 if (instruction_executed_successfully)
263 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
266 return DISPLACED_STEP_FINISH_STATUS_OK;
270 /* Since the instruction didn't complete, all we can do is relocate the
272 CORE_ADDR pc = regcache_read_pc (rc);
273 pc = buffer->original_pc + (pc - buffer->addr);
274 regcache_write_pc (rc, pc);
275 return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
279 const displaced_step_copy_insn_closure *
280 displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
282 for (const displaced_step_buffer &buffer : m_buffers)
284 if (addr == buffer.addr)
286 /* The closure information should always be available. */
287 gdb_assert (buffer.copy_insn_closure.get () != nullptr);
288 return buffer.copy_insn_closure.get ();
296 displaced_step_buffers::restore_in_ptid (ptid_t ptid)
298 for (const displaced_step_buffer &buffer : m_buffers)
300 if (buffer.current_thread == nullptr)
303 regcache *regcache = get_thread_regcache (buffer.current_thread);
304 gdbarch *arch = regcache->arch ();
305 ULONGEST len = gdbarch_max_insn_length (arch);
307 write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len);
309 displaced_debug_printf ("restored in ptid %s %s",
310 ptid.to_string ().c_str (),
311 paddress (arch, buffer.addr));
315 void _initialize_displaced_stepping ();
317 _initialize_displaced_stepping ()
319 add_setshow_boolean_cmd ("displaced", class_maintenance,
320 &debug_displaced, _("\
321 Set displaced stepping debugging."), _("\
322 Show displaced stepping debugging."), _("\
323 When non-zero, displaced stepping specific debugging is enabled."),
325 show_debug_displaced,
326 &setdebuglist, &showdebuglist);