|
9 | 9 | #include "Plugins/Architecture/Arm/ArchitectureArm.h" |
10 | 10 | #include "Plugins/Process/Utility/ARMDefines.h" |
11 | 11 | #include "Plugins/Process/Utility/InstructionUtils.h" |
| 12 | +#include "Utility/ARM_DWARF_Registers.h" |
12 | 13 | #include "lldb/Core/PluginManager.h" |
| 14 | +#include "lldb/Symbol/UnwindPlan.h" |
| 15 | +#include "lldb/Target/Process.h" |
13 | 16 | #include "lldb/Target/RegisterContext.h" |
| 17 | +#include "lldb/Target/RegisterNumber.h" |
14 | 18 | #include "lldb/Target/Thread.h" |
| 19 | +#include "lldb/Target/UnwindLLDB.h" |
15 | 20 | #include "lldb/Utility/ArchSpec.h" |
| 21 | +#include "lldb/Utility/LLDBLog.h" |
| 22 | +#include "lldb/Utility/Log.h" |
| 23 | +#include "lldb/Utility/RegisterValue.h" |
16 | 24 |
|
17 | 25 | using namespace lldb_private; |
18 | 26 | using namespace lldb; |
@@ -150,3 +158,181 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr, |
150 | 158 | } |
151 | 159 | return opcode_addr & ~(1ull); |
152 | 160 | } |
| 161 | + |
| 162 | +// The ARM M-Profile Armv7-M Architecture Reference Manual, |
| 163 | +// subsection "B1.5 Armv7-M exception model", see the parts |
| 164 | +// describing "Exception entry behavior" and "Exception |
| 165 | +// return behavior". |
| 166 | +// When an exception happens on this processor, certain registers are |
| 167 | +// saved below the stack pointer, the stack pointer is decremented, |
| 168 | +// a special value is put in the link register to indicate the |
| 169 | +// exception has been taken, and an exception handler function |
| 170 | +// is invoked. |
| 171 | +// |
| 172 | +// Detect that special value in $lr, and if present, add |
| 173 | +// unwind rules for the registers that were saved above this |
| 174 | +// stack frame's CFA. Overwrite any register locations that |
| 175 | +// the current_unwindplan has for these registers; they are |
| 176 | +// not correct when we're invoked this way. |
| 177 | +UnwindPlanSP 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 value |
| 190 | + // 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 to |
| 229 | + // one of these values when an exception has taken |
| 230 | + // place: |
| 231 | + // if HaveFPExt() then |
| 232 | + // if CurrentMode==Mode_Handler then |
| 233 | + // LR = Ones(27):NOT(CONTROL.FPCA):'0001'; |
| 234 | + // else |
| 235 | + // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01'; |
| 236 | + // else |
| 237 | + // if CurrentMode==Mode_Handler then |
| 238 | + // LR = Ones(28):'0001'; |
| 239 | + // else |
| 240 | + // 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 (to |
| 298 | + // an 8-byte alignment) when the exception happened, and we must |
| 299 | + // 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%" PRIx64 |
| 312 | + ", 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 with |
| 327 | + // SetRegisterLocationToIsConstant(regno, cfa+offset) |
| 328 | + // expressing it in terms of CFA addr+offset - this UnwindPlan |
| 329 | + // is only used once, with this specific CFA. I'm not sure |
| 330 | + // 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 | +} |
0 commit comments