]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/displaced-stepping.c
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / displaced-stepping.c
CommitLineData
c7acb87b
SM
1/* Displaced stepping related things.
2
213516ef 3 Copyright (C) 2020-2023 Free Software Foundation, Inc.
c7acb87b
SM
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#include "defs.h"
21#include "displaced-stepping.h"
c7acb87b 22
187b041e 23#include "cli/cli-cmds.h"
c7acb87b 24#include "command.h"
187b041e
SM
25#include "gdbarch.h"
26#include "gdbcore.h"
27#include "gdbthread.h"
28#include "inferior.h"
29#include "regcache.h"
30#include "target/target.h"
c7acb87b
SM
31
32/* Default destructor for displaced_step_copy_insn_closure. */
33
34displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
35 = default;
36
37bool debug_displaced = false;
38
39static void
40show_debug_displaced (struct ui_file *file, int from_tty,
41 struct cmd_list_element *c, const char *value)
42{
6cb06a8c 43 gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
c7acb87b
SM
44}
45
187b041e 46displaced_step_prepare_status
480af54c 47displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
187b041e
SM
48{
49 gdb_assert (!thread->displaced_step_state.in_progress ());
50
480af54c
SM
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);
187b041e
SM
54
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);
59
480af54c
SM
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;
187b041e 64
480af54c
SM
65 for (displaced_step_buffer &candidate : m_buffers)
66 {
67 bool bp_in_range = breakpoint_in_range_p (aspace, candidate.addr, len);
68 bool is_free = candidate.current_thread == nullptr;
69
70 if (!bp_in_range)
71 {
72 if (is_free)
73 {
74 buffer = &candidate;
75 break;
76 }
77 else
78 {
79 /* This buffer would be suitable, but it's used right now. */
80 fail_status = DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
81 }
82 }
83 else
84 {
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));
97 }
187b041e
SM
98 }
99
480af54c
SM
100 if (buffer == nullptr)
101 return fail_status;
102
103 displaced_debug_printf ("selected buffer at %s",
104 paddress (arch, buffer->addr));
105
106 /* Save the original PC of the thread. */
107 buffer->original_pc = regcache_read_pc (regcache);
108
109 /* Return displaced step buffer address to caller. */
110 displaced_pc = buffer->addr;
187b041e
SM
111
112 /* Save the original contents of the displaced stepping buffer. */
480af54c 113 buffer->saved_copy.resize (len);
187b041e 114
480af54c
SM
115 int status = target_read_memory (buffer->addr,
116 buffer->saved_copy.data (), len);
187b041e
SM
117 if (status != 0)
118 throw_error (MEMORY_ERROR,
119 _("Error accessing memory address %s (%s) for "
120 "displaced-stepping scratch space."),
480af54c 121 paddress (arch, buffer->addr), safe_strerror (status));
187b041e
SM
122
123 displaced_debug_printf ("saved %s: %s",
480af54c 124 paddress (arch, buffer->addr),
187b041e 125 displaced_step_dump_bytes
480af54c 126 (buffer->saved_copy.data (), len).c_str ());
187b041e
SM
127
128 /* Save this in a local variable first, so it's released if code below
129 throws. */
130 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c
SM
131 = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc,
132 buffer->addr, regcache);
187b041e
SM
133
134 if (copy_insn_closure == nullptr)
135 {
136 /* The architecture doesn't know how or want to displaced step
480af54c
SM
137 this instruction or instruction sequence. Fallback to
138 stepping over the breakpoint in-line. */
187b041e
SM
139 return DISPLACED_STEP_PREPARE_STATUS_CANT;
140 }
141
187b041e 142 /* This marks the buffer as being in use. */
480af54c 143 buffer->current_thread = thread;
187b041e
SM
144
145 /* Save this, now that we know everything went fine. */
480af54c 146 buffer->copy_insn_closure = std::move (copy_insn_closure);
187b041e 147
1e5ccb9c
LM
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
152 ([buffer] ()
153 {
154 buffer->current_thread = nullptr;
155 buffer->copy_insn_closure.reset ();
156 });
157
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);
165
166 /* PC update successful. Discard the displaced step state rollback. */
167 reset_buffer.release ();
168
480af54c
SM
169 /* Tell infrun not to try preparing a displaced step again for this inferior if
170 all buffers are taken. */
187b041e 171 thread->inf->displaced_step_state.unavailable = true;
480af54c
SM
172 for (const displaced_step_buffer &buf : m_buffers)
173 {
174 if (buf.current_thread == nullptr)
175 {
176 thread->inf->displaced_step_state.unavailable = false;
177 break;
178 }
179 }
187b041e
SM
180
181 return DISPLACED_STEP_PREPARE_STATUS_OK;
182}
183
184static void
185write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
186 const gdb_byte *myaddr, int len)
187{
188 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
189
190 inferior_ptid = ptid;
191 write_memory (memaddr, myaddr, len);
192}
193
194static bool
195displaced_step_instruction_executed_successfully (gdbarch *arch,
196 gdb_signal signal)
197{
198 if (signal != GDB_SIGNAL_TRAP)
199 return false;
200
201 if (target_stopped_by_watchpoint ())
202 {
203 if (gdbarch_have_nonsteppable_watchpoint (arch)
204 || target_have_steppable_watchpoint ())
205 return false;
206 }
207
208 return true;
209}
210
211displaced_step_finish_status
480af54c
SM
212displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
213 gdb_signal sig)
187b041e
SM
214{
215 gdb_assert (thread->displaced_step_state.in_progress ());
480af54c
SM
216
217 /* Find the buffer this thread was using. */
218 displaced_step_buffer *buffer = nullptr;
219
220 for (displaced_step_buffer &candidate : m_buffers)
221 {
222 if (candidate.current_thread == thread)
223 {
224 buffer = &candidate;
225 break;
226 }
227 }
228
229 gdb_assert (buffer != nullptr);
187b041e
SM
230
231 /* Move this to a local variable so it's released in case something goes
232 wrong. */
233 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c 234 = std::move (buffer->copy_insn_closure);
187b041e
SM
235 gdb_assert (copy_insn_closure != nullptr);
236
480af54c
SM
237 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
238 in case something goes wrong below. */
239 buffer->current_thread = nullptr;
187b041e
SM
240
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
243 below. */
244 thread->inf->displaced_step_state.unavailable = false;
245
246 ULONGEST len = gdbarch_max_insn_length (arch);
247
480af54c
SM
248 /* Restore memory of the buffer. */
249 write_memory_ptid (thread->ptid, buffer->addr,
250 buffer->saved_copy.data (), len);
187b041e
SM
251
252 displaced_debug_printf ("restored %s %s",
e53c95d4 253 thread->ptid.to_string ().c_str (),
480af54c 254 paddress (arch, buffer->addr));
187b041e
SM
255
256 regcache *rc = get_thread_regcache (thread);
257
258 bool instruction_executed_successfully
259 = displaced_step_instruction_executed_successfully (arch, sig);
260
261 if (instruction_executed_successfully)
262 {
480af54c
SM
263 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
264 buffer->original_pc,
265 buffer->addr, rc);
187b041e
SM
266 return DISPLACED_STEP_FINISH_STATUS_OK;
267 }
268 else
269 {
270 /* Since the instruction didn't complete, all we can do is relocate the
271 PC. */
272 CORE_ADDR pc = regcache_read_pc (rc);
480af54c 273 pc = buffer->original_pc + (pc - buffer->addr);
187b041e
SM
274 regcache_write_pc (rc, pc);
275 return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
276 }
277}
278
279const displaced_step_copy_insn_closure *
480af54c 280displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
187b041e 281{
480af54c
SM
282 for (const displaced_step_buffer &buffer : m_buffers)
283 {
284 if (addr == buffer.addr)
1e5ccb9c
LM
285 {
286 /* The closure information should always be available. */
287 gdb_assert (buffer.copy_insn_closure.get () != nullptr);
480af54c 288 return buffer.copy_insn_closure.get ();
1e5ccb9c 289 }
480af54c
SM
290 }
291
292 return nullptr;
187b041e
SM
293}
294
295void
480af54c 296displaced_step_buffers::restore_in_ptid (ptid_t ptid)
187b041e 297{
480af54c 298 for (const displaced_step_buffer &buffer : m_buffers)
187b041e 299 {
480af54c
SM
300 if (buffer.current_thread == nullptr)
301 continue;
302
303 regcache *regcache = get_thread_regcache (buffer.current_thread);
187b041e
SM
304 gdbarch *arch = regcache->arch ();
305 ULONGEST len = gdbarch_max_insn_length (arch);
306
480af54c 307 write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len);
187b041e
SM
308
309 displaced_debug_printf ("restored in ptid %s %s",
e53c95d4 310 ptid.to_string ().c_str (),
480af54c 311 paddress (arch, buffer.addr));
187b041e
SM
312 }
313}
314
c7acb87b
SM
315void _initialize_displaced_stepping ();
316void
317_initialize_displaced_stepping ()
318{
319 add_setshow_boolean_cmd ("displaced", class_maintenance,
320 &debug_displaced, _("\
321Set displaced stepping debugging."), _("\
322Show displaced stepping debugging."), _("\
323When non-zero, displaced stepping specific debugging is enabled."),
324 NULL,
325 show_debug_displaced,
326 &setdebuglist, &showdebuglist);
327}