gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced...
[binutils-gdb.git] / gdb / displaced-stepping.c
1 /* Displaced stepping related things.
2
3 Copyright (C) 2020 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 #include "defs.h"
21 #include "displaced-stepping.h"
22
23 #include "cli/cli-cmds.h"
24 #include "command.h"
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"
31
32 /* Default destructor for displaced_step_copy_insn_closure. */
33
34 displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
35 = default;
36
37 bool debug_displaced = false;
38
39 static void
40 show_debug_displaced (struct ui_file *file, int from_tty,
41 struct cmd_list_element *c, const char *value)
42 {
43 fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
44 }
45
46 displaced_step_prepare_status
47 displaced_step_buffer::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
48 {
49 gdb_assert (!thread->displaced_step_state.in_progress ());
50
51 /* Is a thread currently using the buffer? */
52 if (m_current_thread != nullptr)
53 {
54 /* If so, it better not be this thread. */
55 gdb_assert (thread != m_current_thread);
56 return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
57 }
58
59 regcache *regcache = get_thread_regcache (thread);
60 const address_space *aspace = regcache->aspace ();
61 gdbarch *arch = regcache->arch ();
62 ULONGEST len = gdbarch_max_insn_length (arch);
63
64 if (breakpoint_in_range_p (aspace, m_addr, len))
65 {
66 /* There's a breakpoint set in the scratch pad location range
67 (which is usually around the entry point). We'd either
68 install it before resuming, which would overwrite/corrupt the
69 scratch pad, or if it was already inserted, this displaced
70 step would overwrite it. The latter is OK in the sense that
71 we already assume that no thread is going to execute the code
72 in the scratch pad range (after initial startup) anyway, but
73 the former is unacceptable. Simply punt and fallback to
74 stepping over this breakpoint in-line. */
75 displaced_debug_printf ("breakpoint set in scratch pad. "
76 "Stepping over breakpoint in-line instead.");
77
78 return DISPLACED_STEP_PREPARE_STATUS_CANT;
79 }
80
81 m_original_pc = regcache_read_pc (regcache);
82 displaced_pc = m_addr;
83
84 /* Save the original contents of the displaced stepping buffer. */
85 m_saved_copy.resize (len);
86
87 int status = target_read_memory (m_addr, m_saved_copy.data (), len);
88 if (status != 0)
89 throw_error (MEMORY_ERROR,
90 _("Error accessing memory address %s (%s) for "
91 "displaced-stepping scratch space."),
92 paddress (arch, m_addr), safe_strerror (status));
93
94 displaced_debug_printf ("saved %s: %s",
95 paddress (arch, m_addr),
96 displaced_step_dump_bytes
97 (m_saved_copy.data (), len).c_str ());
98
99 /* Save this in a local variable first, so it's released if code below
100 throws. */
101 displaced_step_copy_insn_closure_up copy_insn_closure
102 = gdbarch_displaced_step_copy_insn (arch, m_original_pc, m_addr, regcache);
103
104 if (copy_insn_closure == nullptr)
105 {
106 /* The architecture doesn't know how or want to displaced step
107 this instruction or instruction sequence. Fallback to
108 stepping over the breakpoint in-line. */
109 return DISPLACED_STEP_PREPARE_STATUS_CANT;
110 }
111
112 /* Resume execution at the copy. */
113 regcache_write_pc (regcache, m_addr);
114
115 /* This marks the buffer as being in use. */
116 m_current_thread = thread;
117
118 /* Save this, now that we know everything went fine. */
119 m_copy_insn_closure = std::move (copy_insn_closure);
120
121 /* Tell infrun not to try preparing a displaced step again for this inferior. */
122 thread->inf->displaced_step_state.unavailable = true;
123
124 return DISPLACED_STEP_PREPARE_STATUS_OK;
125 }
126
127 static void
128 write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
129 const gdb_byte *myaddr, int len)
130 {
131 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
132
133 inferior_ptid = ptid;
134 write_memory (memaddr, myaddr, len);
135 }
136
137 static bool
138 displaced_step_instruction_executed_successfully (gdbarch *arch,
139 gdb_signal signal)
140 {
141 if (signal != GDB_SIGNAL_TRAP)
142 return false;
143
144 if (target_stopped_by_watchpoint ())
145 {
146 if (gdbarch_have_nonsteppable_watchpoint (arch)
147 || target_have_steppable_watchpoint ())
148 return false;
149 }
150
151 return true;
152 }
153
154 displaced_step_finish_status
155 displaced_step_buffer::finish (gdbarch *arch, thread_info *thread,
156 gdb_signal sig)
157 {
158 gdb_assert (thread->displaced_step_state.in_progress ());
159 gdb_assert (thread == m_current_thread);
160
161 /* Move this to a local variable so it's released in case something goes
162 wrong. */
163 displaced_step_copy_insn_closure_up copy_insn_closure
164 = std::move (m_copy_insn_closure);
165 gdb_assert (copy_insn_closure != nullptr);
166
167 /* Reset M_CURRENT_THREAD immediately to mark the buffer as available, in case
168 something goes wrong below. */
169 m_current_thread = nullptr;
170
171 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
172 step again for this inferior. Do that here in case something goes wrong
173 below. */
174 thread->inf->displaced_step_state.unavailable = false;
175
176 ULONGEST len = gdbarch_max_insn_length (arch);
177
178 write_memory_ptid (thread->ptid, m_addr,
179 m_saved_copy.data (), len);
180
181 displaced_debug_printf ("restored %s %s",
182 target_pid_to_str (thread->ptid).c_str (),
183 paddress (arch, m_addr));
184
185 regcache *rc = get_thread_regcache (thread);
186
187 bool instruction_executed_successfully
188 = displaced_step_instruction_executed_successfully (arch, sig);
189
190 if (instruction_executed_successfully)
191 {
192 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (), m_original_pc,
193 m_addr, rc);
194 return DISPLACED_STEP_FINISH_STATUS_OK;
195 }
196 else
197 {
198 /* Since the instruction didn't complete, all we can do is relocate the
199 PC. */
200 CORE_ADDR pc = regcache_read_pc (rc);
201 pc = m_original_pc + (pc - m_addr);
202 regcache_write_pc (rc, pc);
203 return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
204 }
205 }
206
207 const displaced_step_copy_insn_closure *
208 displaced_step_buffer::copy_insn_closure_by_addr (CORE_ADDR addr)
209 {
210 if (addr == m_addr)
211 return m_copy_insn_closure.get ();
212 else
213 return nullptr;
214 }
215
216 void
217 displaced_step_buffer::restore_in_ptid (ptid_t ptid)
218 {
219 if (m_current_thread != nullptr)
220 {
221 regcache *regcache = get_thread_regcache (m_current_thread);
222 gdbarch *arch = regcache->arch ();
223 ULONGEST len = gdbarch_max_insn_length (arch);
224
225 write_memory_ptid (ptid, m_addr, m_saved_copy.data (), len);
226
227 displaced_debug_printf ("restored in ptid %s %s",
228 target_pid_to_str (ptid).c_str (),
229 paddress (arch, m_addr));
230 }
231 }
232
233 void _initialize_displaced_stepping ();
234 void
235 _initialize_displaced_stepping ()
236 {
237 add_setshow_boolean_cmd ("displaced", class_maintenance,
238 &debug_displaced, _("\
239 Set displaced stepping debugging."), _("\
240 Show displaced stepping debugging."), _("\
241 When non-zero, displaced stepping specific debugging is enabled."),
242 NULL,
243 show_debug_displaced,
244 &setdebuglist, &showdebuglist);
245 }