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 | |
14 | single_displaced_buffer_manager::prepare (thread_info *thread) | |
15 | { | |
16 | /* Is a thread currently using the buffer? */ | |
17 | if (m_current_thread != nullptr) | |
18 | { | |
19 | /* If so, it better not be this thread. */ | |
20 | gdb_assert (thread != m_current_thread); | |
21 | return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; | |
22 | } | |
23 | ||
24 | gdbarch *arch = thread->arch (); | |
25 | struct regcache *regcache = thread->regcache (); | |
26 | ULONGEST len = gdbarch_max_insn_length (arch); | |
27 | m_original_pc = regcache_read_pc (regcache); | |
28 | ||
29 | /* Save the original contents of the displaced stepping buffer. */ | |
30 | m_saved_copy.resize (len); | |
31 | ||
32 | int status = target_read_memory (m_buffer_addr, m_saved_copy.data (), len); | |
33 | if (status != 0) | |
34 | throw_error (MEMORY_ERROR, | |
35 | _("Error accessing memory address %s (%s) for " | |
36 | "displaced-stepping scratch space."), | |
37 | paddress (arch, m_buffer_addr), safe_strerror (status)); | |
38 | ||
39 | if (debug_displaced) | |
40 | { | |
41 | fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ", | |
42 | paddress (arch, m_buffer_addr)); | |
43 | displaced_step_dump_bytes (gdb_stdlog, m_saved_copy.data (), len); | |
44 | }; | |
45 | ||
46 | m_copy_insn_closure = gdbarch_displaced_step_copy_insn (arch, | |
47 | m_original_pc, | |
48 | m_buffer_addr, | |
49 | regcache); | |
50 | if (m_copy_insn_closure == nullptr) | |
51 | { | |
52 | /* The architecture doesn't know how or want to displaced step | |
53 | this instruction or instruction sequence. Fallback to | |
54 | stepping over the breakpoint in-line. */ | |
55 | return DISPLACED_STEP_PREPARE_STATUS_ERROR; | |
56 | } | |
57 | ||
58 | try | |
59 | { | |
60 | /* Resume execution at the copy. */ | |
61 | regcache_write_pc (regcache, m_buffer_addr); | |
62 | } | |
63 | catch (...) | |
64 | { | |
65 | /* Failed to write the PC. Release the architecture's displaced | |
66 | stepping resources and the thread's displaced stepping state. */ | |
67 | m_copy_insn_closure.reset (); | |
68 | ||
69 | return DISPLACED_STEP_PREPARE_STATUS_ERROR; | |
70 | } | |
71 | ||
72 | /* This marks the buffer as being in use. */ | |
73 | m_current_thread = thread; | |
74 | ||
75 | return DISPLACED_STEP_PREPARE_STATUS_OK; | |
76 | } | |
77 | ||
78 | static void | |
79 | write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, | |
80 | const gdb_byte *myaddr, int len) | |
81 | { | |
82 | scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); | |
83 | ||
84 | inferior_ptid = ptid; | |
85 | write_memory (memaddr, myaddr, len); | |
86 | } | |
87 | ||
88 | static bool | |
89 | displaced_step_instruction_executed_successfully (gdbarch *arch, gdb_signal signal) | |
90 | { | |
91 | if (signal != GDB_SIGNAL_TRAP) | |
92 | return false; | |
93 | ||
94 | if (target_stopped_by_watchpoint ()) | |
95 | { | |
96 | // FIXME: Not sure about this condition. | |
97 | if (gdbarch_have_nonsteppable_watchpoint (arch) | |
98 | || target_have_steppable_watchpoint) | |
99 | return false; | |
100 | } | |
101 | ||
102 | return true; | |
103 | } | |
104 | ||
105 | displaced_step_finish_status | |
106 | single_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread, | |
107 | gdb_signal sig) | |
108 | { | |
109 | displaced_step_finish_status status; | |
110 | ||
111 | gdb_assert (thread == m_current_thread); | |
112 | ||
113 | ULONGEST len = gdbarch_max_insn_length (arch); | |
114 | ||
115 | write_memory_ptid (thread->ptid, m_buffer_addr, | |
116 | m_saved_copy.data (), len); | |
117 | if (debug_displaced) | |
118 | fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n", | |
119 | target_pid_to_str (thread->ptid).c_str (), | |
120 | paddress (arch, m_buffer_addr)); | |
121 | ||
122 | regcache *rc = get_thread_regcache (thread); | |
123 | ||
124 | bool instruction_executed_successfully | |
125 | = displaced_step_instruction_executed_successfully (arch, sig); | |
126 | ||
127 | ||
128 | if (instruction_executed_successfully) | |
129 | { | |
130 | gdbarch_displaced_step_fixup (arch, m_copy_insn_closure.get (), m_original_pc, | |
131 | m_buffer_addr, rc); | |
132 | status = DISPLACED_STEP_FINISH_STATUS_OK; | |
133 | } | |
134 | else | |
135 | { | |
136 | /* Since the instruction didn't complete, all we can do is relocate the | |
137 | PC. */ | |
138 | CORE_ADDR pc = regcache_read_pc (rc); | |
139 | pc = m_original_pc + (pc - m_buffer_addr); | |
140 | regcache_write_pc (rc, pc); | |
141 | status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED; | |
142 | } | |
143 | ||
144 | m_copy_insn_closure.reset (); | |
145 | m_current_thread = nullptr; | |
146 | ||
147 | return status; | |
148 | } |