Skip to content

Commit 1960927

Browse files
authored
Merge pull request swiftlang#11363 from jasonmolenda/cp/r110663219-cortex-m-exception-unwind-21.x
[lldb] Unwind through ARM Cortex-M exceptions automatically (llvm#153922)
2 parents cacf7ed + f9d8cb8 commit 1960927

File tree

14 files changed

+488
-21
lines changed

14 files changed

+488
-21
lines changed

lldb/include/lldb/Core/Architecture.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "lldb/Core/PluginInterface.h"
1313
#include "lldb/Target/DynamicRegisterInfo.h"
1414
#include "lldb/Target/MemoryTagManager.h"
15+
#include "lldb/Target/RegisterContextUnwind.h"
1516

1617
namespace lldb_private {
1718

@@ -129,6 +130,14 @@ class Architecture : public PluginInterface {
129130
RegisterContext &reg_context) const {
130131
return false;
131132
}
133+
134+
/// Return an UnwindPlan that allows architecture-defined rules for finding
135+
/// saved registers, given a particular set of register values.
136+
virtual lldb::UnwindPlanSP GetArchitectureUnwindPlan(
137+
lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx,
138+
std::shared_ptr<const UnwindPlan> current_unwindplan) {
139+
return lldb::UnwindPlanSP();
140+
}
132141
};
133142

134143
} // namespace lldb_private

lldb/include/lldb/Target/RegisterContextUnwind.h

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace lldb_private {
2222

2323
class UnwindLLDB;
24+
class ArchitectureArm;
2425

2526
class RegisterContextUnwind : public lldb_private::RegisterContext {
2627
public:
@@ -72,6 +73,25 @@ class RegisterContextUnwind : public lldb_private::RegisterContext {
7273
// above asynchronous trap handlers (sigtramp) for instance.
7374
bool BehavesLikeZerothFrame() const override;
7475

76+
protected:
77+
// Provide a location for where THIS function saved the CALLER's register
78+
// value, or a frame "below" this one saved it. That is, this function doesn't
79+
// modify the register, it may call a function that does & saved it to stack.
80+
//
81+
// The ConcreteRegisterLocation type may be set to eRegisterNotAvailable --
82+
// this will happen for a volatile register being queried mid-stack. Instead
83+
// of floating frame 0's contents of that register up the stack (which may or
84+
// may not be the value of that reg when the function was executing), we won't
85+
// return any value.
86+
//
87+
// If a non-volatile register (a "preserved" register, a callee-preserved
88+
// register) is requested mid-stack, and no frames "below" the requested stack
89+
// have saved the register anywhere, it is safe to assume that frame 0's
90+
// register value is the same.
91+
lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister(
92+
uint32_t lldb_regnum,
93+
lldb_private::UnwindLLDB::ConcreteRegisterLocation &regloc);
94+
7595
private:
7696
enum FrameType {
7797
eNormalFrame,
@@ -86,6 +106,8 @@ class RegisterContextUnwind : public lldb_private::RegisterContext {
86106

87107
// UnwindLLDB needs to pass around references to ConcreteRegisterLocations
88108
friend class UnwindLLDB;
109+
// Architecture may need to retrieve caller register values from this frame
110+
friend class ArchitectureArm;
89111

90112
// Returns true if we have an unwind loop -- the same stack frame unwinding
91113
// multiple times.
@@ -130,27 +152,6 @@ class RegisterContextUnwind : public lldb_private::RegisterContext {
130152
void PropagateTrapHandlerFlagFromUnwindPlan(
131153
std::shared_ptr<const UnwindPlan> unwind_plan);
132154

133-
// Provide a location for where THIS function saved the CALLER's register
134-
// value
135-
// Or a frame "below" this one saved it, i.e. a function called by this one,
136-
// preserved a register that this
137-
// function didn't modify/use.
138-
//
139-
// The ConcreteRegisterLocation type may be set to eRegisterNotAvailable --
140-
// this will happen for a volatile register being queried mid-stack. Instead
141-
// of floating frame 0's contents of that register up the stack (which may or
142-
// may not be the value of that reg when the function was executing), we won't
143-
// return any value.
144-
//
145-
// If a non-volatile register (a "preserved" register) is requested mid-stack
146-
// and no frames "below" the requested
147-
// stack have saved the register anywhere, it is safe to assume that frame 0's
148-
// register values are still the same
149-
// as the requesting frame's.
150-
lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister(
151-
uint32_t lldb_regnum,
152-
lldb_private::UnwindLLDB::ConcreteRegisterLocation &regloc);
153-
154155
std::optional<UnwindPlan::Row::AbstractRegisterLocation>
155156
GetAbstractRegisterLocation(uint32_t lldb_regnum, lldb::RegisterKind &kind);
156157

@@ -202,6 +203,8 @@ class RegisterContextUnwind : public lldb_private::RegisterContext {
202203

203204
std::shared_ptr<const UnwindPlan> GetFullUnwindPlanForFrame();
204205

206+
lldb::UnwindPlanSP TryAdoptArchitectureUnwindPlan();
207+
205208
void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
206209

207210
void UnwindLogMsgVerbose(const char *fmt, ...)

lldb/include/lldb/Target/UnwindLLDB.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
namespace lldb_private {
2323

2424
class RegisterContextUnwind;
25+
class ArchitectureArm;
2526

2627
class UnwindLLDB : public lldb_private::Unwind {
2728
public:
@@ -37,6 +38,7 @@ class UnwindLLDB : public lldb_private::Unwind {
3738

3839
protected:
3940
friend class lldb_private::RegisterContextUnwind;
41+
friend class lldb_private::ArchitectureArm;
4042

4143
/// An UnwindPlan::Row::AbstractRegisterLocation, combined with the register
4244
/// context and memory for a specific stop point, is used to create a

lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,13 @@ UnwindPlanSP ABISysV_arm::CreateFunctionEntryUnwindPlan() {
19211921

19221922
UnwindPlanSP ABISysV_arm::CreateDefaultUnwindPlan() {
19231923
// TODO: Handle thumb
1924+
// If we had a Target argument, could at least check
1925+
// target.GetArchitecture().GetTriple().isArmMClass()
1926+
// which is always thumb.
1927+
// To handle thumb properly, we'd need to fetch the current
1928+
// CPSR state at unwind time to tell if the processor is
1929+
// in thumb mode in this stack frame. There's no way to
1930+
// express something like that in an UnwindPlan today.
19241931
uint32_t fp_reg_num = dwarf_r11;
19251932
uint32_t pc_reg_num = dwarf_pc;
19261933

lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@
99
#include "Plugins/Architecture/Arm/ArchitectureArm.h"
1010
#include "Plugins/Process/Utility/ARMDefines.h"
1111
#include "Plugins/Process/Utility/InstructionUtils.h"
12+
#include "Utility/ARM_DWARF_Registers.h"
1213
#include "lldb/Core/PluginManager.h"
14+
#include "lldb/Symbol/UnwindPlan.h"
15+
#include "lldb/Target/Process.h"
1316
#include "lldb/Target/RegisterContext.h"
17+
#include "lldb/Target/RegisterNumber.h"
1418
#include "lldb/Target/Thread.h"
19+
#include "lldb/Target/UnwindLLDB.h"
1520
#include "lldb/Utility/ArchSpec.h"
21+
#include "lldb/Utility/LLDBLog.h"
22+
#include "lldb/Utility/Log.h"
23+
#include "lldb/Utility/RegisterValue.h"
1624

1725
using namespace lldb_private;
1826
using namespace lldb;
@@ -150,3 +158,181 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr,
150158
}
151159
return opcode_addr & ~(1ull);
152160
}
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+
}

lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_SOURCE_PLUGINS_ARCHITECTURE_ARM_ARCHITECTUREARM_H
1111

1212
#include "lldb/Core/Architecture.h"
13+
#include "lldb/Target/Thread.h"
1314

1415
namespace lldb_private {
1516

@@ -29,6 +30,10 @@ class ArchitectureArm : public Architecture {
2930
lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
3031
AddressClass addr_class) const override;
3132

33+
lldb::UnwindPlanSP GetArchitectureUnwindPlan(
34+
lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx,
35+
std::shared_ptr<const UnwindPlan> current_unwindplan) override;
36+
3237
private:
3338
static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
3439
ArchitectureArm() = default;

0 commit comments

Comments
 (0)