339 lines · cpp
1//===-- ArchitectureArm.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Plugins/Architecture/Arm/ArchitectureArm.h"10#include "Plugins/Process/Utility/ARMDefines.h"11#include "Plugins/Process/Utility/InstructionUtils.h"12#include "Utility/ARM_DWARF_Registers.h"13#include "lldb/Core/PluginManager.h"14#include "lldb/Symbol/UnwindPlan.h"15#include "lldb/Target/Process.h"16#include "lldb/Target/RegisterContext.h"17#include "lldb/Target/RegisterNumber.h"18#include "lldb/Target/Thread.h"19#include "lldb/Target/UnwindLLDB.h"20#include "lldb/Utility/ArchSpec.h"21#include "lldb/Utility/LLDBLog.h"22#include "lldb/Utility/Log.h"23#include "lldb/Utility/RegisterValue.h"24 25using namespace lldb_private;26using namespace lldb;27 28LLDB_PLUGIN_DEFINE(ArchitectureArm)29 30void ArchitectureArm::Initialize() {31 PluginManager::RegisterPlugin(GetPluginNameStatic(),32 "Arm-specific algorithms",33 &ArchitectureArm::Create);34}35 36void ArchitectureArm::Terminate() {37 PluginManager::UnregisterPlugin(&ArchitectureArm::Create);38}39 40std::unique_ptr<Architecture> ArchitectureArm::Create(const ArchSpec &arch) {41 if (arch.GetMachine() != llvm::Triple::arm)42 return nullptr;43 return std::unique_ptr<Architecture>(new ArchitectureArm());44}45 46void ArchitectureArm::OverrideStopInfo(Thread &thread) const {47 // We need to check if we are stopped in Thumb mode in a IT instruction and48 // detect if the condition doesn't pass. If this is the case it means we49 // won't actually execute this instruction. If this happens we need to clear50 // the stop reason to no thread plans think we are stopped for a reason and51 // the plans should keep going.52 //53 // We do this because when single stepping many ARM processes, debuggers54 // often use the BVR/BCR registers that says "stop when the PC is not equal55 // to its current value". This method of stepping means we can end up56 // stopping on instructions inside an if/then block that wouldn't get57 // executed. By fixing this we can stop the debugger from seeming like you58 // stepped through both the "if" _and_ the "else" clause when source level59 // stepping because the debugger stops regardless due to the BVR/BCR60 // triggering a stop.61 //62 // It also means we can set breakpoints on instructions inside an if/then63 // block and correctly skip them if we use the BKPT instruction. The ARM and64 // Thumb BKPT instructions are unconditional even when executed in a Thumb IT65 // block.66 //67 // If your debugger inserts software traps in ARM/Thumb code, it will need to68 // use 16 and 32 bit instruction for 16 and 32 bit thumb instructions69 // respectively. If your debugger inserts a 16 bit thumb trap on top of a 3270 // bit thumb instruction for an opcode that is inside an if/then, it will71 // change the it/then to conditionally execute your72 // 16 bit trap and then cause your program to crash if it executes the73 // trailing 16 bits (the second half of the 32 bit thumb instruction you74 // partially overwrote).75 76 RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());77 if (!reg_ctx_sp)78 return;79 80 const uint32_t cpsr = reg_ctx_sp->GetFlags(0);81 if (cpsr == 0)82 return;83 84 // Read the J and T bits to get the ISETSTATE85 const uint32_t J = Bit32(cpsr, 24);86 const uint32_t T = Bit32(cpsr, 5);87 const uint32_t ISETSTATE = J << 1 | T;88 if (ISETSTATE == 0) {89// NOTE: I am pretty sure we want to enable the code below90// that detects when we stop on an instruction in ARM mode that is conditional91// and the condition doesn't pass. This can happen if you set a breakpoint on92// an instruction that is conditional. We currently will _always_ stop on the93// instruction which is bad. You can also run into this while single stepping94// and you could appear to run code in the "if" and in the "else" clause95// because it would stop at all of the conditional instructions in both. In96// such cases, we really don't want to stop at this location.97// I will check with the lldb-dev list first before I enable this.98#if 099 // ARM mode: check for condition on instruction100 const addr_t pc = reg_ctx_sp->GetPC();101 Status error;102 // If we fail to read the opcode we will get UINT64_MAX as the result in103 // "opcode" which we can use to detect if we read a valid opcode.104 const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error);105 if (opcode <= UINT32_MAX)106 {107 const uint32_t condition = Bits32((uint32_t)opcode, 31, 28);108 if (!ARMConditionPassed(condition, cpsr))109 {110 // We ARE stopped on an ARM instruction whose condition doesn't111 // pass so this instruction won't get executed. Regardless of why112 // it stopped, we need to clear the stop info113 thread.SetStopInfo (StopInfoSP());114 }115 }116#endif117 } else if (ISETSTATE == 1) {118 // Thumb mode119 const uint32_t ITSTATE = Bits32(cpsr, 15, 10) << 2 | Bits32(cpsr, 26, 25);120 if (ITSTATE != 0) {121 const uint32_t condition = Bits32(ITSTATE, 7, 4);122 if (!ARMConditionPassed(condition, cpsr)) {123 // We ARE stopped in a Thumb IT instruction on an instruction whose124 // condition doesn't pass so this instruction won't get executed.125 // Regardless of why it stopped, we need to clear the stop info126 thread.SetStopInfo(StopInfoSP());127 }128 }129 }130}131 132addr_t ArchitectureArm::GetCallableLoadAddress(addr_t code_addr,133 AddressClass addr_class) const {134 bool is_alternate_isa = false;135 136 switch (addr_class) {137 case AddressClass::eData:138 case AddressClass::eDebug:139 return LLDB_INVALID_ADDRESS;140 case AddressClass::eCodeAlternateISA:141 is_alternate_isa = true;142 break;143 default: break;144 }145 146 if ((code_addr & 2u) || is_alternate_isa)147 return code_addr | 1u;148 return code_addr;149}150 151addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr,152 AddressClass addr_class) const {153 switch (addr_class) {154 case AddressClass::eData:155 case AddressClass::eDebug:156 return LLDB_INVALID_ADDRESS;157 default: break;158 }159 return opcode_addr & ~(1ull);160}161 162// The ARM M-Profile Armv7-M Architecture Reference Manual,163// subsection "B1.5 Armv7-M exception model", see the parts164// describing "Exception entry behavior" and "Exception165// return behavior".166// When an exception happens on this processor, certain registers are167// saved below the stack pointer, the stack pointer is decremented,168// a special value is put in the link register to indicate the169// exception has been taken, and an exception handler function170// is invoked.171//172// Detect that special value in $lr, and if present, add173// unwind rules for the registers that were saved above this174// stack frame's CFA. Overwrite any register locations that175// the current_unwindplan has for these registers; they are176// not correct when we're invoked this way.177UnwindPlanSP ArchitectureArm::GetArchitectureUnwindPlan(178 Thread &thread, RegisterContextUnwind *regctx,179 std::shared_ptr<const UnwindPlan> current_unwindplan) {180 181 ProcessSP process_sp = thread.GetProcess();182 if (!process_sp)183 return {};184 185 const ArchSpec arch = process_sp->GetTarget().GetArchitecture();186 if (!arch.GetTriple().isArmMClass() || arch.GetAddressByteSize() != 4)187 return {};188 189 // Get the caller's LR value from regctx (the LR value190 // at function entry to this function).191 RegisterNumber ra_regnum(thread, eRegisterKindGeneric,192 LLDB_REGNUM_GENERIC_RA);193 uint32_t ra_regnum_lldb = ra_regnum.GetAsKind(eRegisterKindLLDB);194 195 if (ra_regnum_lldb == LLDB_INVALID_REGNUM)196 return {};197 198 UnwindLLDB::ConcreteRegisterLocation regloc = {};199 bool got_concrete_location = false;200 if (regctx->SavedLocationForRegister(ra_regnum_lldb, regloc) ==201 UnwindLLDB::RegisterSearchResult::eRegisterFound) {202 got_concrete_location = true;203 } else {204 RegisterNumber pc_regnum(thread, eRegisterKindGeneric,205 LLDB_REGNUM_GENERIC_PC);206 uint32_t pc_regnum_lldb = pc_regnum.GetAsKind(eRegisterKindLLDB);207 if (regctx->SavedLocationForRegister(pc_regnum_lldb, regloc) ==208 UnwindLLDB::RegisterSearchResult::eRegisterFound)209 got_concrete_location = true;210 }211 212 if (!got_concrete_location)213 return {};214 215 addr_t callers_return_address = LLDB_INVALID_ADDRESS;216 const RegisterInfo *reg_info = regctx->GetRegisterInfoAtIndex(ra_regnum_lldb);217 if (reg_info) {218 RegisterValue reg_value;219 if (regctx->ReadRegisterValueFromRegisterLocation(regloc, reg_info,220 reg_value)) {221 callers_return_address = reg_value.GetAsUInt32();222 }223 }224 225 if (callers_return_address == LLDB_INVALID_ADDRESS)226 return {};227 228 // ARMv7-M ARM says that the LR will be set to229 // one of these values when an exception has taken230 // place:231 // if HaveFPExt() then232 // if CurrentMode==Mode_Handler then233 // LR = Ones(27):NOT(CONTROL.FPCA):'0001';234 // else235 // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01';236 // else237 // if CurrentMode==Mode_Handler then238 // LR = Ones(28):'0001';239 // else240 // LR = Ones(29):CONTROL.SPSEL:'01';241 242 // Top 27 bits are set for an exception return.243 const uint32_t exception_return = -1U & ~0b11111U;244 // Bit4 is 1 if only GPRs were saved.245 const uint32_t gprs_only = 0b10000;246 // Bit<1:0> are '01'.247 const uint32_t lowbits = 0b01;248 249 if ((callers_return_address & exception_return) != exception_return)250 return {};251 if ((callers_return_address & lowbits) != lowbits)252 return {};253 254 const bool fp_regs_saved = !(callers_return_address & gprs_only);255 256 const RegisterKind plan_regkind = current_unwindplan->GetRegisterKind();257 UnwindPlanSP new_plan = std::make_shared<UnwindPlan>(plan_regkind);258 new_plan->SetSourceName("Arm Cortex-M exception return UnwindPlan");259 new_plan->SetSourcedFromCompiler(eLazyBoolNo);260 new_plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);261 new_plan->SetUnwindPlanForSignalTrap(eLazyBoolYes);262 263 int stored_regs_size = fp_regs_saved ? 0x68 : 0x20;264 265 uint32_t gpr_regs[] = {dwarf_r0, dwarf_r1, dwarf_r2, dwarf_r3,266 dwarf_r12, dwarf_lr, dwarf_pc, dwarf_cpsr};267 const int gpr_reg_count = std::size(gpr_regs);268 uint32_t fpr_regs[] = {dwarf_s0, dwarf_s1, dwarf_s2, dwarf_s3,269 dwarf_s4, dwarf_s5, dwarf_s6, dwarf_s7,270 dwarf_s8, dwarf_s9, dwarf_s10, dwarf_s11,271 dwarf_s12, dwarf_s13, dwarf_s14, dwarf_s15};272 const int fpr_reg_count = std::size(fpr_regs);273 274 RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();275 std::vector<uint32_t> saved_regs;276 for (int i = 0; i < gpr_reg_count; i++) {277 uint32_t regno = gpr_regs[i];278 reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, gpr_regs[i],279 plan_regkind, regno);280 saved_regs.push_back(regno);281 }282 if (fp_regs_saved) {283 for (int i = 0; i < fpr_reg_count; i++) {284 uint32_t regno = fpr_regs[i];285 reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, fpr_regs[i],286 plan_regkind, regno);287 saved_regs.push_back(regno);288 }289 }290 291 addr_t cfa;292 if (!regctx->GetCFA(cfa))293 return {};294 295 // The CPSR value saved to stack is actually (from Armv7-M ARM)296 // "XPSR<31:10>:frameptralign:XPSR<8:0>"297 // Bit 9 indicates that the stack pointer was aligned (to298 // an 8-byte alignment) when the exception happened, and we must299 // account for that when restoring the original stack pointer value.300 Status error;301 uint32_t callers_xPSR =302 process_sp->ReadUnsignedIntegerFromMemory(cfa + 0x1c, 4, 0, error);303 const bool align_stack = callers_xPSR & (1U << 9);304 uint32_t callers_sp = cfa + stored_regs_size;305 if (align_stack)306 callers_sp |= 4;307 308 Log *log = GetLog(LLDBLog::Unwind);309 LLDB_LOGF(log,310 "ArchitectureArm::GetArchitectureUnwindPlan found caller return "311 "addr of 0x%" PRIx64 ", for frame with CFA 0x%" PRIx64312 ", fp_regs_saved %d, stored_regs_size 0x%x, align stack %d",313 callers_return_address, cfa, fp_regs_saved, stored_regs_size,314 align_stack);315 316 uint32_t sp_regnum = dwarf_sp;317 reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, dwarf_sp,318 plan_regkind, sp_regnum);319 320 const int row_count = current_unwindplan->GetRowCount();321 for (int i = 0; i < row_count; i++) {322 UnwindPlan::Row row = *current_unwindplan->GetRowAtIndex(i);323 uint32_t offset = 0;324 const size_t saved_reg_count = saved_regs.size();325 for (size_t j = 0; j < saved_reg_count; j++) {326 // The locations could be set with327 // SetRegisterLocationToIsConstant(regno, cfa+offset)328 // expressing it in terms of CFA addr+offset - this UnwindPlan329 // is only used once, with this specific CFA. I'm not sure330 // which will be clearer for someone reading the unwind log.331 row.SetRegisterLocationToAtCFAPlusOffset(saved_regs[j], offset, true);332 offset += 4;333 }334 row.SetRegisterLocationToIsCFAPlusOffset(sp_regnum, callers_sp - cfa, true);335 new_plan->AppendRow(row);336 }337 return new_plan;338}339