| 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::Row 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: std::move(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 | |