brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.4 KiB · 721c4bc Raw
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