1 | //===-- EmulateInstructionPPC64.cpp ---------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "EmulateInstructionPPC64.h" |
10 | |
11 | #include <cstdlib> |
12 | #include <optional> |
13 | |
14 | #include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" |
15 | #include "lldb/Core/PluginManager.h" |
16 | #include "lldb/Symbol/UnwindPlan.h" |
17 | #include "lldb/Utility/ArchSpec.h" |
18 | #include "lldb/Utility/ConstString.h" |
19 | #include "lldb/Utility/LLDBLog.h" |
20 | |
21 | #define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT |
22 | #include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" |
23 | |
24 | #include "Plugins/Process/Utility/InstructionUtils.h" |
25 | |
26 | using namespace lldb; |
27 | using namespace lldb_private; |
28 | |
29 | LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionPPC64, InstructionPPC64) |
30 | |
31 | EmulateInstructionPPC64::EmulateInstructionPPC64(const ArchSpec &arch) |
32 | : EmulateInstruction(arch) {} |
33 | |
34 | void EmulateInstructionPPC64::Initialize() { |
35 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
36 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
37 | } |
38 | |
39 | void EmulateInstructionPPC64::Terminate() { |
40 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
41 | } |
42 | |
43 | llvm::StringRef EmulateInstructionPPC64::GetPluginDescriptionStatic() { |
44 | return "Emulate instructions for the PPC64 architecture." ; |
45 | } |
46 | |
47 | EmulateInstruction * |
48 | EmulateInstructionPPC64::CreateInstance(const ArchSpec &arch, |
49 | InstructionType inst_type) { |
50 | if (EmulateInstructionPPC64::SupportsEmulatingInstructionsOfTypeStatic( |
51 | inst_type)) |
52 | if (arch.GetTriple().isPPC64()) |
53 | return new EmulateInstructionPPC64(arch); |
54 | |
55 | return nullptr; |
56 | } |
57 | |
58 | bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) { |
59 | return arch.GetTriple().isPPC64(); |
60 | } |
61 | |
62 | static std::optional<RegisterInfo> LLDBTableGetRegisterInfo(uint32_t reg_num) { |
63 | if (reg_num >= std::size(g_register_infos_ppc64le)) |
64 | return {}; |
65 | return g_register_infos_ppc64le[reg_num]; |
66 | } |
67 | |
68 | std::optional<RegisterInfo> |
69 | EmulateInstructionPPC64::GetRegisterInfo(RegisterKind reg_kind, |
70 | uint32_t reg_num) { |
71 | if (reg_kind == eRegisterKindGeneric) { |
72 | switch (reg_num) { |
73 | case LLDB_REGNUM_GENERIC_PC: |
74 | reg_kind = eRegisterKindLLDB; |
75 | reg_num = gpr_pc_ppc64le; |
76 | break; |
77 | case LLDB_REGNUM_GENERIC_SP: |
78 | reg_kind = eRegisterKindLLDB; |
79 | reg_num = gpr_r1_ppc64le; |
80 | break; |
81 | case LLDB_REGNUM_GENERIC_RA: |
82 | reg_kind = eRegisterKindLLDB; |
83 | reg_num = gpr_lr_ppc64le; |
84 | break; |
85 | case LLDB_REGNUM_GENERIC_FLAGS: |
86 | reg_kind = eRegisterKindLLDB; |
87 | reg_num = gpr_cr_ppc64le; |
88 | break; |
89 | |
90 | default: |
91 | return {}; |
92 | } |
93 | } |
94 | |
95 | if (reg_kind == eRegisterKindLLDB) |
96 | return LLDBTableGetRegisterInfo(reg_num); |
97 | return {}; |
98 | } |
99 | |
100 | bool EmulateInstructionPPC64::ReadInstruction() { |
101 | bool success = false; |
102 | m_addr = ReadRegisterUnsigned(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, |
103 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
104 | if (success) { |
105 | Context ctx; |
106 | ctx.type = eContextReadOpcode; |
107 | ctx.SetNoArgs(); |
108 | m_opcode.SetOpcode32(inst: ReadMemoryUnsigned(context: ctx, addr: m_addr, byte_size: 4, fail_value: 0, success_ptr: &success), |
109 | order: GetByteOrder()); |
110 | } |
111 | if (!success) |
112 | m_addr = LLDB_INVALID_ADDRESS; |
113 | return success; |
114 | } |
115 | |
116 | bool EmulateInstructionPPC64::CreateFunctionEntryUnwind( |
117 | UnwindPlan &unwind_plan) { |
118 | unwind_plan.Clear(); |
119 | unwind_plan.SetRegisterKind(eRegisterKindLLDB); |
120 | |
121 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
122 | |
123 | // Our previous Call Frame Address is the stack pointer |
124 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: gpr_r1_ppc64le, offset: 0); |
125 | |
126 | unwind_plan.AppendRow(row_sp: row); |
127 | unwind_plan.SetSourceName("EmulateInstructionPPC64" ); |
128 | unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); |
129 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
130 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
131 | unwind_plan.SetReturnAddressRegister(gpr_lr_ppc64le); |
132 | return true; |
133 | } |
134 | |
135 | EmulateInstructionPPC64::Opcode * |
136 | EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) { |
137 | static EmulateInstructionPPC64::Opcode g_opcodes[] = { |
138 | {.mask: 0xfc0007ff, .value: 0x7c0002a6, .callback: &EmulateInstructionPPC64::EmulateMFSPR, |
139 | .name: "mfspr RT, SPR" }, |
140 | {.mask: 0xfc000003, .value: 0xf8000000, .callback: &EmulateInstructionPPC64::EmulateSTD, |
141 | .name: "std RS, DS(RA)" }, |
142 | {.mask: 0xfc000003, .value: 0xf8000001, .callback: &EmulateInstructionPPC64::EmulateSTD, |
143 | .name: "stdu RS, DS(RA)" }, |
144 | {.mask: 0xfc0007fe, .value: 0x7c000378, .callback: &EmulateInstructionPPC64::EmulateOR, |
145 | .name: "or RA, RS, RB" }, |
146 | {.mask: 0xfc000000, .value: 0x38000000, .callback: &EmulateInstructionPPC64::EmulateADDI, |
147 | .name: "addi RT, RA, SI" }, |
148 | {.mask: 0xfc000003, .value: 0xe8000000, .callback: &EmulateInstructionPPC64::EmulateLD, |
149 | .name: "ld RT, DS(RA)" }}; |
150 | static const size_t k_num_ppc_opcodes = std::size(g_opcodes); |
151 | |
152 | for (size_t i = 0; i < k_num_ppc_opcodes; ++i) { |
153 | if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value) |
154 | return &g_opcodes[i]; |
155 | } |
156 | return nullptr; |
157 | } |
158 | |
159 | bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) { |
160 | const uint32_t opcode = m_opcode.GetOpcode32(); |
161 | // LLDB_LOG(log, "PPC64::EvaluateInstruction: opcode={0:X+8}", opcode); |
162 | Opcode *opcode_data = GetOpcodeForInstruction(opcode); |
163 | if (!opcode_data) |
164 | return false; |
165 | |
166 | // LLDB_LOG(log, "PPC64::EvaluateInstruction: {0}", opcode_data->name); |
167 | const bool auto_advance_pc = |
168 | evaluate_options & eEmulateInstructionOptionAutoAdvancePC; |
169 | |
170 | bool success = false; |
171 | |
172 | uint32_t orig_pc_value = 0; |
173 | if (auto_advance_pc) { |
174 | orig_pc_value = |
175 | ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: gpr_pc_ppc64le, fail_value: 0, success_ptr: &success); |
176 | if (!success) |
177 | return false; |
178 | } |
179 | |
180 | // Call the Emulate... function. |
181 | success = (this->*opcode_data->callback)(opcode); |
182 | if (!success) |
183 | return false; |
184 | |
185 | if (auto_advance_pc) { |
186 | uint32_t new_pc_value = |
187 | ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: gpr_pc_ppc64le, fail_value: 0, success_ptr: &success); |
188 | if (!success) |
189 | return false; |
190 | |
191 | if (new_pc_value == orig_pc_value) { |
192 | EmulateInstruction::Context context; |
193 | context.type = eContextAdvancePC; |
194 | context.SetNoArgs(); |
195 | if (!WriteRegisterUnsigned(context, reg_kind: eRegisterKindLLDB, reg_num: gpr_pc_ppc64le, |
196 | reg_value: orig_pc_value + 4)) |
197 | return false; |
198 | } |
199 | } |
200 | return true; |
201 | } |
202 | |
203 | bool EmulateInstructionPPC64::EmulateMFSPR(uint32_t opcode) { |
204 | uint32_t rt = Bits32(bits: opcode, msbit: 25, lsbit: 21); |
205 | uint32_t spr = Bits32(bits: opcode, msbit: 20, lsbit: 11); |
206 | |
207 | enum { SPR_LR = 0x100 }; |
208 | |
209 | // For now, we're only insterested in 'mfspr r0, lr' |
210 | if (rt != gpr_r0_ppc64le || spr != SPR_LR) |
211 | return false; |
212 | |
213 | Log *log = GetLog(mask: LLDBLog::Unwind); |
214 | LLDB_LOG(log, "EmulateMFSPR: {0:X+8}: mfspr r0, lr" , m_addr); |
215 | |
216 | bool success; |
217 | uint64_t lr = |
218 | ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: gpr_lr_ppc64le, fail_value: 0, success_ptr: &success); |
219 | if (!success) |
220 | return false; |
221 | Context context; |
222 | context.type = eContextWriteRegisterRandomBits; |
223 | WriteRegisterUnsigned(context, reg_kind: eRegisterKindLLDB, reg_num: gpr_r0_ppc64le, reg_value: lr); |
224 | LLDB_LOG(log, "EmulateMFSPR: success!" ); |
225 | return true; |
226 | } |
227 | |
228 | bool EmulateInstructionPPC64::EmulateLD(uint32_t opcode) { |
229 | uint32_t rt = Bits32(bits: opcode, msbit: 25, lsbit: 21); |
230 | uint32_t ra = Bits32(bits: opcode, msbit: 20, lsbit: 16); |
231 | uint32_t ds = Bits32(bits: opcode, msbit: 15, lsbit: 2); |
232 | |
233 | int32_t ids = llvm::SignExtend32<16>(X: ds << 2); |
234 | |
235 | // For now, tracking only loads from 0(r1) to r1 (0(r1) is the ABI defined |
236 | // location to save previous SP) |
237 | if (ra != gpr_r1_ppc64le || rt != gpr_r1_ppc64le || ids != 0) |
238 | return false; |
239 | |
240 | Log *log = GetLog(mask: LLDBLog::Unwind); |
241 | LLDB_LOG(log, "EmulateLD: {0:X+8}: ld r{1}, {2}(r{3})" , m_addr, rt, ids, ra); |
242 | |
243 | std::optional<RegisterInfo> r1_info = |
244 | GetRegisterInfo(reg_kind: eRegisterKindLLDB, reg_num: gpr_r1_ppc64le); |
245 | if (!r1_info) |
246 | return false; |
247 | |
248 | // restore SP |
249 | Context ctx; |
250 | ctx.type = eContextRestoreStackPointer; |
251 | ctx.SetRegisterToRegisterPlusOffset(data_reg: *r1_info, base_reg: *r1_info, offset: 0); |
252 | |
253 | WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: gpr_r1_ppc64le, reg_value: 0); |
254 | LLDB_LOG(log, "EmulateLD: success!" ); |
255 | return true; |
256 | } |
257 | |
258 | bool EmulateInstructionPPC64::EmulateSTD(uint32_t opcode) { |
259 | uint32_t rs = Bits32(bits: opcode, msbit: 25, lsbit: 21); |
260 | uint32_t ra = Bits32(bits: opcode, msbit: 20, lsbit: 16); |
261 | uint32_t ds = Bits32(bits: opcode, msbit: 15, lsbit: 2); |
262 | uint32_t u = Bits32(bits: opcode, msbit: 1, lsbit: 0); |
263 | |
264 | // For now, tracking only stores to r1 |
265 | if (ra != gpr_r1_ppc64le) |
266 | return false; |
267 | // ... and only stores of SP, FP and LR (moved into r0 by a previous mfspr) |
268 | if (rs != gpr_r1_ppc64le && rs != gpr_r31_ppc64le && rs != gpr_r30_ppc64le && |
269 | rs != gpr_r0_ppc64le) |
270 | return false; |
271 | |
272 | bool success; |
273 | uint64_t rs_val = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: rs, fail_value: 0, success_ptr: &success); |
274 | if (!success) |
275 | return false; |
276 | |
277 | int32_t ids = llvm::SignExtend32<16>(X: ds << 2); |
278 | Log *log = GetLog(mask: LLDBLog::Unwind); |
279 | LLDB_LOG(log, "EmulateSTD: {0:X+8}: std{1} r{2}, {3}(r{4})" , m_addr, |
280 | u ? "u" : "" , rs, ids, ra); |
281 | |
282 | // Make sure that r0 is really holding LR value (this won't catch unlikely |
283 | // cases, such as r0 being overwritten after mfspr) |
284 | uint32_t rs_num = rs; |
285 | if (rs == gpr_r0_ppc64le) { |
286 | uint64_t lr = |
287 | ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: gpr_lr_ppc64le, fail_value: 0, success_ptr: &success); |
288 | if (!success || lr != rs_val) |
289 | return false; |
290 | rs_num = gpr_lr_ppc64le; |
291 | } |
292 | |
293 | // set context |
294 | std::optional<RegisterInfo> rs_info = |
295 | GetRegisterInfo(reg_kind: eRegisterKindLLDB, reg_num: rs_num); |
296 | if (!rs_info) |
297 | return false; |
298 | std::optional<RegisterInfo> ra_info = GetRegisterInfo(reg_kind: eRegisterKindLLDB, reg_num: ra); |
299 | if (!ra_info) |
300 | return false; |
301 | |
302 | Context ctx; |
303 | ctx.type = eContextPushRegisterOnStack; |
304 | ctx.SetRegisterToRegisterPlusOffset(data_reg: *rs_info, base_reg: *ra_info, offset: ids); |
305 | |
306 | // store |
307 | uint64_t ra_val = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: ra, fail_value: 0, success_ptr: &success); |
308 | if (!success) |
309 | return false; |
310 | |
311 | lldb::addr_t addr = ra_val + ids; |
312 | WriteMemory(context: ctx, addr, src: &rs_val, src_len: sizeof(rs_val)); |
313 | |
314 | // update RA? |
315 | if (u) { |
316 | Context ctx; |
317 | // NOTE Currently, RA will always be equal to SP(r1) |
318 | ctx.type = eContextAdjustStackPointer; |
319 | WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: ra, reg_value: addr); |
320 | } |
321 | |
322 | LLDB_LOG(log, "EmulateSTD: success!" ); |
323 | return true; |
324 | } |
325 | |
326 | bool EmulateInstructionPPC64::EmulateOR(uint32_t opcode) { |
327 | uint32_t rs = Bits32(bits: opcode, msbit: 25, lsbit: 21); |
328 | uint32_t ra = Bits32(bits: opcode, msbit: 20, lsbit: 16); |
329 | uint32_t rb = Bits32(bits: opcode, msbit: 15, lsbit: 11); |
330 | |
331 | // to be safe, process only the known 'mr r31/r30, r1' prologue instructions |
332 | if (m_fp != LLDB_INVALID_REGNUM || rs != rb || |
333 | (ra != gpr_r30_ppc64le && ra != gpr_r31_ppc64le) || rb != gpr_r1_ppc64le) |
334 | return false; |
335 | |
336 | Log *log = GetLog(mask: LLDBLog::Unwind); |
337 | LLDB_LOG(log, "EmulateOR: {0:X+8}: mr r{1}, r{2}" , m_addr, ra, rb); |
338 | |
339 | // set context |
340 | std::optional<RegisterInfo> ra_info = GetRegisterInfo(reg_kind: eRegisterKindLLDB, reg_num: ra); |
341 | if (!ra_info) |
342 | return false; |
343 | |
344 | Context ctx; |
345 | ctx.type = eContextSetFramePointer; |
346 | ctx.SetRegister(*ra_info); |
347 | |
348 | // move |
349 | bool success; |
350 | uint64_t rb_val = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: rb, fail_value: 0, success_ptr: &success); |
351 | if (!success) |
352 | return false; |
353 | WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: ra, reg_value: rb_val); |
354 | m_fp = ra; |
355 | LLDB_LOG(log, "EmulateOR: success!" ); |
356 | return true; |
357 | } |
358 | |
359 | bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) { |
360 | uint32_t rt = Bits32(bits: opcode, msbit: 25, lsbit: 21); |
361 | uint32_t ra = Bits32(bits: opcode, msbit: 20, lsbit: 16); |
362 | uint32_t si = Bits32(bits: opcode, msbit: 15, lsbit: 0); |
363 | |
364 | // handle stack adjustments only |
365 | // (this is a typical epilogue operation, with ra == r1. If it's |
366 | // something else, then we won't know the correct value of ra) |
367 | if (rt != gpr_r1_ppc64le || ra != gpr_r1_ppc64le) |
368 | return false; |
369 | |
370 | int32_t si_val = llvm::SignExtend32<16>(X: si); |
371 | Log *log = GetLog(mask: LLDBLog::Unwind); |
372 | LLDB_LOG(log, "EmulateADDI: {0:X+8}: addi r1, r1, {1}" , m_addr, si_val); |
373 | |
374 | // set context |
375 | std::optional<RegisterInfo> r1_info = |
376 | GetRegisterInfo(reg_kind: eRegisterKindLLDB, reg_num: gpr_r1_ppc64le); |
377 | if (!r1_info) |
378 | return false; |
379 | |
380 | Context ctx; |
381 | ctx.type = eContextRestoreStackPointer; |
382 | ctx.SetRegisterToRegisterPlusOffset(data_reg: *r1_info, base_reg: *r1_info, offset: 0); |
383 | |
384 | // adjust SP |
385 | bool success; |
386 | uint64_t r1 = |
387 | ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: gpr_r1_ppc64le, fail_value: 0, success_ptr: &success); |
388 | if (!success) |
389 | return false; |
390 | WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: gpr_r1_ppc64le, reg_value: r1 + si_val); |
391 | LLDB_LOG(log, "EmulateADDI: success!" ); |
392 | return true; |
393 | } |
394 | |