]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/displaced-stepping.c
New Romanian translation for ld
[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);
187b041e 56 gdbarch *arch = regcache->arch ();
deb65a3c 57 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
187b041e 58
480af54c
SM
59 /* Search for an unused buffer. */
60 displaced_step_buffer *buffer = nullptr;
61 displaced_step_prepare_status fail_status
62 = DISPLACED_STEP_PREPARE_STATUS_CANT;
187b041e 63
480af54c
SM
64 for (displaced_step_buffer &candidate : m_buffers)
65 {
f9582a22
TV
66 bool bp_in_range = breakpoint_in_range_p (thread->inf->aspace.get (),
67 candidate.addr, len);
480af54c
SM
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),
a6e5abae 125 bytes_to_string (buffer->saved_copy).c_str ());
187b041e
SM
126
127 /* Save this in a local variable first, so it's released if code below
128 throws. */
129 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c
SM
130 = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc,
131 buffer->addr, regcache);
187b041e
SM
132
133 if (copy_insn_closure == nullptr)
134 {
135 /* The architecture doesn't know how or want to displaced step
480af54c
SM
136 this instruction or instruction sequence. Fallback to
137 stepping over the breakpoint in-line. */
187b041e
SM
138 return DISPLACED_STEP_PREPARE_STATUS_CANT;
139 }
140
187b041e 141 /* This marks the buffer as being in use. */
480af54c 142 buffer->current_thread = thread;
187b041e
SM
143
144 /* Save this, now that we know everything went fine. */
480af54c 145 buffer->copy_insn_closure = std::move (copy_insn_closure);
187b041e 146
1e5ccb9c
LM
147 /* Reset the displaced step buffer state if we failed to write PC.
148 Otherwise we will prevent this buffer from being used, as it will
149 always have a thread in buffer->current_thread. */
150 auto reset_buffer = make_scope_exit
151 ([buffer] ()
152 {
153 buffer->current_thread = nullptr;
154 buffer->copy_insn_closure.reset ();
155 });
156
157 /* Adjust the PC so it points to the displaced step buffer address that will
158 be used. This needs to be done after we save the copy_insn_closure, as
159 some architectures (Arm, for one) need that information so they can adjust
160 other data as needed. In particular, Arm needs to know if the instruction
161 being executed in the displaced step buffer is thumb or not. Without that
162 information, things will be very wrong in a random way. */
163 regcache_write_pc (regcache, buffer->addr);
164
165 /* PC update successful. Discard the displaced step state rollback. */
166 reset_buffer.release ();
167
480af54c
SM
168 /* Tell infrun not to try preparing a displaced step again for this inferior if
169 all buffers are taken. */
187b041e 170 thread->inf->displaced_step_state.unavailable = true;
480af54c
SM
171 for (const displaced_step_buffer &buf : m_buffers)
172 {
173 if (buf.current_thread == nullptr)
174 {
175 thread->inf->displaced_step_state.unavailable = false;
176 break;
177 }
178 }
187b041e
SM
179
180 return DISPLACED_STEP_PREPARE_STATUS_OK;
181}
182
183static void
184write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
185 const gdb_byte *myaddr, int len)
186{
187 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
188
189 inferior_ptid = ptid;
190 write_memory (memaddr, myaddr, len);
191}
192
193static bool
58c01087
PA
194displaced_step_instruction_executed_successfully
195 (gdbarch *arch, const target_waitstatus &status)
187b041e 196{
58c01087
PA
197 if (status.kind () == TARGET_WAITKIND_STOPPED
198 && status.sig () != GDB_SIGNAL_TRAP)
187b041e
SM
199 return false;
200
58c01087
PA
201 /* All other (thread event) waitkinds can only happen if the
202 instruction fully executed. For example, a fork, or a syscall
203 entry can only happen if the syscall instruction actually
204 executed. */
205
187b041e
SM
206 if (target_stopped_by_watchpoint ())
207 {
208 if (gdbarch_have_nonsteppable_watchpoint (arch)
209 || target_have_steppable_watchpoint ())
210 return false;
211 }
212
213 return true;
214}
215
216displaced_step_finish_status
480af54c 217displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
58c01087 218 const target_waitstatus &status)
187b041e
SM
219{
220 gdb_assert (thread->displaced_step_state.in_progress ());
480af54c
SM
221
222 /* Find the buffer this thread was using. */
223 displaced_step_buffer *buffer = nullptr;
224
225 for (displaced_step_buffer &candidate : m_buffers)
226 {
227 if (candidate.current_thread == thread)
228 {
229 buffer = &candidate;
230 break;
231 }
232 }
233
234 gdb_assert (buffer != nullptr);
187b041e
SM
235
236 /* Move this to a local variable so it's released in case something goes
237 wrong. */
238 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c 239 = std::move (buffer->copy_insn_closure);
187b041e
SM
240 gdb_assert (copy_insn_closure != nullptr);
241
480af54c
SM
242 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
243 in case something goes wrong below. */
244 buffer->current_thread = nullptr;
187b041e
SM
245
246 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
247 step again for this inferior. Do that here in case something goes wrong
248 below. */
249 thread->inf->displaced_step_state.unavailable = false;
250
deb65a3c 251 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
187b041e 252
480af54c
SM
253 /* Restore memory of the buffer. */
254 write_memory_ptid (thread->ptid, buffer->addr,
255 buffer->saved_copy.data (), len);
187b041e
SM
256
257 displaced_debug_printf ("restored %s %s",
e53c95d4 258 thread->ptid.to_string ().c_str (),
480af54c 259 paddress (arch, buffer->addr));
187b041e 260
21d48304
PA
261 /* If the thread exited while stepping, we are done. The code above
262 made the buffer available again, and we restored the bytes in the
263 buffer. We don't want to run the fixup: since the thread is now
264 dead there's nothing to adjust. */
265 if (status.kind () == TARGET_WAITKIND_THREAD_EXITED)
266 return DISPLACED_STEP_FINISH_STATUS_OK;
267
187b041e
SM
268 regcache *rc = get_thread_regcache (thread);
269
270 bool instruction_executed_successfully
58c01087 271 = displaced_step_instruction_executed_successfully (arch, status);
187b041e 272
cf141dd8
AB
273 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
274 buffer->original_pc, buffer->addr,
275 rc, instruction_executed_successfully);
276
277 return (instruction_executed_successfully
278 ? DISPLACED_STEP_FINISH_STATUS_OK
279 : DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED);
187b041e
SM
280}
281
282const displaced_step_copy_insn_closure *
480af54c 283displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
187b041e 284{
480af54c
SM
285 for (const displaced_step_buffer &buffer : m_buffers)
286 {
5d4a870e
LM
287 /* Make sure we have active buffers to compare to. */
288 if (buffer.current_thread != nullptr && addr == buffer.addr)
1e5ccb9c
LM
289 {
290 /* The closure information should always be available. */
291 gdb_assert (buffer.copy_insn_closure.get () != nullptr);
480af54c 292 return buffer.copy_insn_closure.get ();
1e5ccb9c 293 }
480af54c
SM
294 }
295
296 return nullptr;
187b041e
SM
297}
298
299void
480af54c 300displaced_step_buffers::restore_in_ptid (ptid_t ptid)
187b041e 301{
480af54c 302 for (const displaced_step_buffer &buffer : m_buffers)
187b041e 303 {
480af54c
SM
304 if (buffer.current_thread == nullptr)
305 continue;
306
307 regcache *regcache = get_thread_regcache (buffer.current_thread);
187b041e 308 gdbarch *arch = regcache->arch ();
deb65a3c 309 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
187b041e 310
480af54c 311 write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len);
187b041e
SM
312
313 displaced_debug_printf ("restored in ptid %s %s",
e53c95d4 314 ptid.to_string ().c_str (),
480af54c 315 paddress (arch, buffer.addr));
187b041e
SM
316 }
317}
318
c7acb87b
SM
319void _initialize_displaced_stepping ();
320void
321_initialize_displaced_stepping ()
322{
323 add_setshow_boolean_cmd ("displaced", class_maintenance,
324 &debug_displaced, _("\
325Set displaced stepping debugging."), _("\
326Show displaced stepping debugging."), _("\
327When non-zero, displaced stepping specific debugging is enabled."),
328 NULL,
329 show_debug_displaced,
330 &setdebuglist, &showdebuglist);
331}