Commit | Line | Data |
---|---|---|
e088209c SM |
1 | #include "defs.h" |
2 | ||
3 | #include "displaced-stepping.h" | |
4 | ||
5 | #include "gdbarch.h" | |
6 | #include "gdbthread.h" | |
7 | #include "target.h" | |
8 | #include "inferior.h" | |
9 | #include "gdbcore.h" | |
10 | ||
11 | displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure() = default; | |
12 | ||
13 | displaced_step_prepare_status | |
9635ae5d | 14 | multiple_displaced_buffer_manager::prepare (thread_info *thread) |
e088209c | 15 | { |
5514706f SM |
16 | gdb_assert (thread != nullptr); |
17 | ||
18 | gdbarch *arch = thread->arch (); | |
19 | ||
20 | /* This class requires that the arch implements both copy_insn and fixup. */ | |
21 | gdb_assert (gdbarch_displaced_step_copy_insn_p (arch)); | |
22 | gdb_assert (gdbarch_displaced_step_fixup_p (arch)); | |
23 | ||
24 | /* It's invalid to `prepare` a thread that already has a displaced step in | |
25 | progress. */ | |
9635ae5d | 26 | gdb_assert (!thread->displaced_step_state.in_progress ()); |
9635ae5d | 27 | |
5514706f | 28 | /* Sanity check: no buffer is currently assigned to this thread. */ |
9635ae5d SM |
29 | for (displaced_step_buffer_state &buf : m_buffers) |
30 | gdb_assert (buf.m_current_thread != thread); | |
31 | ||
32 | /* Search for an unused buffer. */ | |
5514706f SM |
33 | displaced_step_buffer_state *buffer = nullptr; |
34 | ||
9635ae5d | 35 | for (displaced_step_buffer_state &candidate : m_buffers) |
e088209c | 36 | { |
9635ae5d SM |
37 | if (candidate.m_current_thread == nullptr) |
38 | { | |
39 | buffer = &candidate; | |
40 | break; | |
41 | } | |
e088209c SM |
42 | } |
43 | ||
9635ae5d SM |
44 | if (buffer == nullptr) |
45 | return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; | |
46 | ||
9635ae5d SM |
47 | |
48 | if (debug_displaced) | |
49 | fprintf_unfiltered (gdb_stdlog, "displaced: selected buffer at %s\n", | |
50 | paddress (arch, buffer->m_buffer_addr)); | |
51 | ||
e088209c SM |
52 | struct regcache *regcache = thread->regcache (); |
53 | ULONGEST len = gdbarch_max_insn_length (arch); | |
9635ae5d | 54 | buffer->m_original_pc = regcache_read_pc (regcache); |
e088209c SM |
55 | |
56 | /* Save the original contents of the displaced stepping buffer. */ | |
9635ae5d | 57 | buffer->m_saved_copy.resize (len); |
e088209c | 58 | |
9635ae5d | 59 | int status = target_read_memory (buffer->m_buffer_addr, buffer->m_saved_copy.data (), len); |
e088209c SM |
60 | if (status != 0) |
61 | throw_error (MEMORY_ERROR, | |
62 | _("Error accessing memory address %s (%s) for " | |
63 | "displaced-stepping scratch space."), | |
9635ae5d | 64 | paddress (arch, buffer->m_buffer_addr), safe_strerror (status)); |
e088209c SM |
65 | |
66 | if (debug_displaced) | |
67 | { | |
68 | fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ", | |
9635ae5d SM |
69 | paddress (arch, buffer->m_buffer_addr)); |
70 | displaced_step_dump_bytes (gdb_stdlog, buffer->m_saved_copy.data (), len); | |
e088209c SM |
71 | }; |
72 | ||
9635ae5d SM |
73 | buffer->m_copy_insn_closure |
74 | = gdbarch_displaced_step_copy_insn (arch, buffer->m_original_pc, | |
75 | buffer->m_buffer_addr, regcache); | |
76 | if (buffer->m_copy_insn_closure == nullptr) | |
e088209c SM |
77 | { |
78 | /* The architecture doesn't know how or want to displaced step | |
79 | this instruction or instruction sequence. Fallback to | |
80 | stepping over the breakpoint in-line. */ | |
81 | return DISPLACED_STEP_PREPARE_STATUS_ERROR; | |
82 | } | |
83 | ||
84 | try | |
85 | { | |
86 | /* Resume execution at the copy. */ | |
9635ae5d | 87 | regcache_write_pc (regcache, buffer->m_buffer_addr); |
e088209c SM |
88 | } |
89 | catch (...) | |
90 | { | |
91 | /* Failed to write the PC. Release the architecture's displaced | |
92 | stepping resources and the thread's displaced stepping state. */ | |
9635ae5d | 93 | buffer->m_copy_insn_closure.reset (); |
e088209c SM |
94 | |
95 | return DISPLACED_STEP_PREPARE_STATUS_ERROR; | |
96 | } | |
97 | ||
98 | /* This marks the buffer as being in use. */ | |
9635ae5d | 99 | buffer->m_current_thread = thread; |
e088209c SM |
100 | |
101 | return DISPLACED_STEP_PREPARE_STATUS_OK; | |
102 | } | |
103 | ||
104 | static void | |
105 | write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, | |
106 | const gdb_byte *myaddr, int len) | |
107 | { | |
108 | scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); | |
109 | ||
110 | inferior_ptid = ptid; | |
111 | write_memory (memaddr, myaddr, len); | |
112 | } | |
113 | ||
114 | static bool | |
115 | displaced_step_instruction_executed_successfully (gdbarch *arch, gdb_signal signal) | |
116 | { | |
117 | if (signal != GDB_SIGNAL_TRAP) | |
118 | return false; | |
119 | ||
120 | if (target_stopped_by_watchpoint ()) | |
121 | { | |
122 | // FIXME: Not sure about this condition. | |
123 | if (gdbarch_have_nonsteppable_watchpoint (arch) | |
124 | || target_have_steppable_watchpoint) | |
125 | return false; | |
126 | } | |
127 | ||
128 | return true; | |
129 | } | |
130 | ||
131 | displaced_step_finish_status | |
9635ae5d SM |
132 | multiple_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread, |
133 | gdb_signal sig) | |
e088209c SM |
134 | { |
135 | displaced_step_finish_status status; | |
9635ae5d | 136 | displaced_step_buffer_state *buffer = nullptr; |
e088209c | 137 | |
9635ae5d SM |
138 | gdb_assert (thread->displaced_step_state.in_progress ()); |
139 | ||
140 | /* Find the buffer this thread was using. */ | |
141 | for (displaced_step_buffer_state &candidate : m_buffers) | |
142 | { | |
143 | if (thread == candidate.m_current_thread) | |
144 | { | |
145 | buffer = &candidate; | |
146 | break; | |
147 | } | |
148 | } | |
149 | ||
150 | gdb_assert (buffer != nullptr); | |
e088209c SM |
151 | |
152 | ULONGEST len = gdbarch_max_insn_length (arch); | |
153 | ||
9635ae5d SM |
154 | /* Restore memory of the buffer. */ |
155 | write_memory_ptid (thread->ptid, buffer->m_buffer_addr, | |
156 | buffer->m_saved_copy.data (), len); | |
e088209c SM |
157 | if (debug_displaced) |
158 | fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n", | |
159 | target_pid_to_str (thread->ptid).c_str (), | |
9635ae5d | 160 | paddress (arch, buffer->m_buffer_addr)); |
e088209c SM |
161 | |
162 | regcache *rc = get_thread_regcache (thread); | |
163 | ||
164 | bool instruction_executed_successfully | |
165 | = displaced_step_instruction_executed_successfully (arch, sig); | |
166 | ||
e088209c SM |
167 | if (instruction_executed_successfully) |
168 | { | |
9635ae5d SM |
169 | gdbarch_displaced_step_fixup (arch, buffer->m_copy_insn_closure.get (), |
170 | buffer->m_original_pc, | |
171 | buffer->m_buffer_addr, rc); | |
e088209c SM |
172 | status = DISPLACED_STEP_FINISH_STATUS_OK; |
173 | } | |
174 | else | |
175 | { | |
176 | /* Since the instruction didn't complete, all we can do is relocate the | |
177 | PC. */ | |
178 | CORE_ADDR pc = regcache_read_pc (rc); | |
9635ae5d | 179 | pc = buffer->m_original_pc + (pc - buffer->m_buffer_addr); |
e088209c SM |
180 | regcache_write_pc (rc, pc); |
181 | status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED; | |
182 | } | |
183 | ||
9635ae5d SM |
184 | buffer->m_copy_insn_closure.reset (); |
185 | buffer->m_current_thread = nullptr; | |
e088209c SM |
186 | |
187 | return status; | |
188 | } | |
348a832d | 189 | |
65a7e4d6 SM |
190 | bool |
191 | default_supports_displaced_step (target_ops *target, thread_info *thread) | |
192 | { | |
193 | /* Only check for the presence of `prepare`. `finish` is required by the | |
194 | gdbarch verification to be provided if `prepare` is. */ | |
195 | gdbarch *arch = thread->arch (); | |
196 | return gdbarch_displaced_step_prepare_p (arch); | |
197 | } | |
198 | ||
348a832d | 199 | displaced_step_prepare_status |
65a7e4d6 | 200 | default_displaced_step_prepare (target_ops *target, thread_info *thread) |
348a832d SM |
201 | { |
202 | gdbarch *arch = thread->arch (); | |
203 | return gdbarch_displaced_step_prepare (arch, thread); | |
204 | } | |
205 | ||
206 | displaced_step_finish_status | |
207 | default_displaced_step_finish (target_ops *target, | |
208 | thread_info *thread, | |
209 | gdb_signal sig) | |
210 | { | |
211 | gdbarch *arch = thread->arch (); | |
212 | return gdbarch_displaced_step_finish (arch, thread, sig); | |
213 | } |