|
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" |
14 | 17 | #include "lldb/Target/Thread.h" |
15 | 18 | #include "lldb/Utility/ArchSpec.h" |
| 19 | +#include "lldb/Utility/LLDBLog.h" |
| 20 | +#include "lldb/Utility/Log.h" |
16 | 21 |
|
17 | 22 | using namespace lldb_private; |
18 | 23 | using namespace lldb; |
@@ -150,3 +155,118 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr, |
150 | 155 | } |
151 | 156 | return opcode_addr & ~(1ull); |
152 | 157 | } |
| 158 | + |
| 159 | +// The ARM M-Profile Armv7-M Architecture Reference Manual |
| 160 | +// "Exception return behavior" describes how the processor |
| 161 | +// saves registers to the stack, decrements the stack pointer, |
| 162 | +// puts a special value in $lr, and then calls a registered |
| 163 | +// exception handler routine. |
| 164 | +// |
| 165 | +// Detect that special value in $lr, and if present, add |
| 166 | +// unwind rules for the registers that were saved above this |
| 167 | +// stack frame's CFA. Overwrite any register locations that |
| 168 | +// the current_unwindplan has for these registers; they are |
| 169 | +// not correct when we're invoked this way. |
| 170 | +UnwindPlanSP ArchitectureArm::GetArchitectureUnwindPlan( |
| 171 | + Thread &thread, addr_t callers_return_address, addr_t cfa, |
| 172 | + std::shared_ptr<const UnwindPlan> current_unwindplan) { |
| 173 | + |
| 174 | + ProcessSP process_sp = thread.GetProcess(); |
| 175 | + if (!process_sp) |
| 176 | + return {}; |
| 177 | + |
| 178 | + const ArchSpec arch = process_sp->GetTarget().GetArchitecture(); |
| 179 | + if (!arch.GetTriple().isArmMClass() || arch.GetAddressByteSize() != 4) |
| 180 | + return {}; |
| 181 | + |
| 182 | + if (callers_return_address != 0xFFFFFFF1 && |
| 183 | + callers_return_address != 0xFFFFFFF9 && |
| 184 | + callers_return_address != 0xFFFFFFFD && |
| 185 | + callers_return_address != 0xFFFFFFE1 && |
| 186 | + callers_return_address != 0xFFFFFFE9 && |
| 187 | + callers_return_address != 0xFFFFFFED) |
| 188 | + return {}; |
| 189 | + |
| 190 | + const RegisterKind plan_regkind = current_unwindplan->GetRegisterKind(); |
| 191 | + UnwindPlanSP new_plan = std::make_shared<UnwindPlan>(plan_regkind); |
| 192 | + new_plan->SetSourceName("Arm Cortex-M exception return UnwindPlan"); |
| 193 | + new_plan->SetSourcedFromCompiler(eLazyBoolNo); |
| 194 | + new_plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); |
| 195 | + new_plan->SetUnwindPlanForSignalTrap(eLazyBoolYes); |
| 196 | + |
| 197 | + // bit 4 will be 1 if only the general purpose registers were saved. |
| 198 | + // bit 4 will be 0 if the GPRs + floating point registers were saved. |
| 199 | + const bool fp_regs_saved = (callers_return_address & 0x10) == 0; |
| 200 | + |
| 201 | + int stored_regs_size = 0x20; |
| 202 | + if (fp_regs_saved) |
| 203 | + stored_regs_size = 0x68; |
| 204 | + |
| 205 | + uint32_t gpr_regs[] = {dwarf_r0, dwarf_r1, dwarf_r2, dwarf_r3, |
| 206 | + dwarf_r12, dwarf_lr, dwarf_pc, dwarf_cpsr}; |
| 207 | + const int gpr_reg_count = sizeof(gpr_regs) / sizeof(uint32_t); |
| 208 | + uint32_t fpr_regs[] = {dwarf_s0, dwarf_s1, dwarf_s2, dwarf_s3, |
| 209 | + dwarf_s4, dwarf_s5, dwarf_s6, dwarf_s7, |
| 210 | + dwarf_s8, dwarf_s9, dwarf_s10, dwarf_s11, |
| 211 | + dwarf_s12, dwarf_s13, dwarf_s14, dwarf_s15}; |
| 212 | + const int fpr_reg_count = sizeof(fpr_regs) / sizeof(uint32_t); |
| 213 | + |
| 214 | + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); |
| 215 | + std::vector<uint32_t> saved_regs; |
| 216 | + for (int i = 0; i < gpr_reg_count; i++) { |
| 217 | + uint32_t regno = gpr_regs[i]; |
| 218 | + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, gpr_regs[i], |
| 219 | + plan_regkind, regno); |
| 220 | + saved_regs.push_back(regno); |
| 221 | + } |
| 222 | + if (fp_regs_saved) { |
| 223 | + for (int i = 0; i < fpr_reg_count; i++) { |
| 224 | + uint32_t regno = fpr_regs[i]; |
| 225 | + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, fpr_regs[i], |
| 226 | + plan_regkind, regno); |
| 227 | + saved_regs.push_back(regno); |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + // PSR bit 9 indicates that the stack pointer was unaligned (to |
| 232 | + // an 8-byte alignment) when the exception happened, and we must |
| 233 | + // account for that when restoring the excepted stack pointer value. |
| 234 | + Status error; |
| 235 | + uint32_t callers_xPSR = |
| 236 | + process_sp->ReadUnsignedIntegerFromMemory(cfa + 0x28, 4, 0, error); |
| 237 | + const bool align_stack = callers_xPSR & (1 << 9U); |
| 238 | + uint32_t callers_sp = cfa + stored_regs_size; |
| 239 | + if (align_stack) |
| 240 | + callers_sp |= 4; |
| 241 | + |
| 242 | + Log *log = GetLog(LLDBLog::Unwind); |
| 243 | + LLDB_LOGF(log, |
| 244 | + "ArchitectureArm::GetArchitectureUnwindPlan found caller return " |
| 245 | + "addr of 0x%" PRIx64 ", for frame with CFA 0x%" PRIx64 |
| 246 | + ", fp_regs_saved %d, stored_regs_size 0x%x, align stack %d", |
| 247 | + callers_return_address, cfa, fp_regs_saved, stored_regs_size, |
| 248 | + align_stack); |
| 249 | + |
| 250 | + uint32_t sp_regnum = dwarf_sp; |
| 251 | + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, dwarf_sp, |
| 252 | + plan_regkind, sp_regnum); |
| 253 | + |
| 254 | + const int row_count = current_unwindplan->GetRowCount(); |
| 255 | + for (int i = 0; i < row_count; i++) { |
| 256 | + UnwindPlan::Row row = *current_unwindplan->GetRowAtIndex(i); |
| 257 | + uint32_t offset = 0; |
| 258 | + const size_t saved_reg_count = saved_regs.size(); |
| 259 | + for (size_t j = 0; j < saved_reg_count; j++) { |
| 260 | + // The locations could be set with |
| 261 | + // SetRegisterLocationToIsConstant(regno, cfa+offset) |
| 262 | + // expressing it in terms of CFA addr+offset - this UnwindPlan |
| 263 | + // is only used once, with this specific CFA. I'm not sure |
| 264 | + // which will be clearer for someone reading the unwind log. |
| 265 | + row.SetRegisterLocationToAtCFAPlusOffset(saved_regs[j], offset, true); |
| 266 | + offset += 4; |
| 267 | + } |
| 268 | + row.SetRegisterLocationToIsCFAPlusOffset(sp_regnum, callers_sp - cfa, true); |
| 269 | + new_plan->AppendRow(row); |
| 270 | + } |
| 271 | + return new_plan; |
| 272 | +} |
0 commit comments