Skip to content

Commit 42f75b8

Browse files
Merge pull request llvm#11940 from felipepiovezan/felipe/cherrypick_unwind_backwards_fixes
🍒 [lldb] Handle backwards branches in UnwindAssemblyInstEmulation (PR llvm#11920)
2 parents b7a1892 + 80a6823 commit 42f75b8

File tree

6 files changed

+205
-34
lines changed

6 files changed

+205
-34
lines changed

lldb/include/lldb/Core/Disassembler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class Instruction {
167167

168168
virtual bool IsLoad() = 0;
169169

170+
virtual bool IsBarrier() = 0;
171+
170172
virtual bool IsAuthenticated() = 0;
171173

172174
bool CanSetBreakpoint ();
@@ -368,6 +370,8 @@ class PseudoInstruction : public Instruction {
368370

369371
bool IsLoad() override;
370372

373+
bool IsBarrier() override;
374+
371375
bool IsAuthenticated() override;
372376

373377
void CalculateMnemonicOperandsAndComment(

lldb/source/Core/Disassembler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,11 @@ bool PseudoInstruction::DoesBranch() {
12011201
return false;
12021202
}
12031203

1204+
bool PseudoInstruction::IsBarrier() {
1205+
// This is NOT a valid question for a pseudo instruction.
1206+
return false;
1207+
}
1208+
12041209
bool PseudoInstruction::HasDelaySlot() {
12051210
// This is NOT a valid question for a pseudo instruction.
12061211
return false;

lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class DisassemblerLLVMC::MCDisasmInstance {
7070
bool HasDelaySlot(llvm::MCInst &mc_inst) const;
7171
bool IsCall(llvm::MCInst &mc_inst) const;
7272
bool IsLoad(llvm::MCInst &mc_inst) const;
73+
bool IsBarrier(llvm::MCInst &mc_inst) const;
7374
bool IsAuthenticated(llvm::MCInst &mc_inst) const;
7475

7576
private:
@@ -436,6 +437,11 @@ class InstructionLLVMC : public lldb_private::Instruction {
436437
return m_is_load;
437438
}
438439

440+
bool IsBarrier() override {
441+
VisitInstruction();
442+
return m_is_barrier;
443+
}
444+
439445
bool IsAuthenticated() override {
440446
VisitInstruction();
441447
return m_is_authenticated;
@@ -1195,6 +1201,7 @@ class InstructionLLVMC : public lldb_private::Instruction {
11951201
bool m_is_call = false;
11961202
bool m_is_load = false;
11971203
bool m_is_authenticated = false;
1204+
bool m_is_barrier = false;
11981205

11991206
void VisitInstruction() {
12001207
if (m_has_visited_instruction)
@@ -1227,6 +1234,7 @@ class InstructionLLVMC : public lldb_private::Instruction {
12271234
m_is_call = mc_disasm_ptr->IsCall(inst);
12281235
m_is_load = mc_disasm_ptr->IsLoad(inst);
12291236
m_is_authenticated = mc_disasm_ptr->IsAuthenticated(inst);
1237+
m_is_barrier = mc_disasm_ptr->IsBarrier(inst);
12301238
}
12311239

12321240
private:
@@ -1429,6 +1437,11 @@ bool DisassemblerLLVMC::MCDisasmInstance::IsLoad(llvm::MCInst &mc_inst) const {
14291437
return m_instr_info_up->get(mc_inst.getOpcode()).mayLoad();
14301438
}
14311439

1440+
bool DisassemblerLLVMC::MCDisasmInstance::IsBarrier(
1441+
llvm::MCInst &mc_inst) const {
1442+
return m_instr_info_up->get(mc_inst.getOpcode()).isBarrier();
1443+
}
1444+
14321445
bool DisassemblerLLVMC::MCDisasmInstance::IsAuthenticated(
14331446
llvm::MCInst &mc_inst) const {
14341447
const auto &InstrDesc = m_instr_info_up->get(mc_inst.getOpcode());

lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "lldb/Utility/Log.h"
2626
#include "lldb/Utility/Status.h"
2727
#include "lldb/Utility/StreamString.h"
28+
#include "llvm/ADT/SmallSet.h"
29+
#include <deque>
2830

2931
using namespace lldb;
3032
using namespace lldb_private;
@@ -63,7 +65,7 @@ static void DumpUnwindRowsToLog(Log *log, AddressRange range,
6365
}
6466

6567
static void DumpInstToLog(Log *log, Instruction &inst,
66-
InstructionList inst_list) {
68+
const InstructionList &inst_list) {
6769
if (!log || !log->GetVerbose())
6870
return;
6971
const bool show_address = true;
@@ -150,29 +152,38 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
150152
EmulateInstruction::InstructionCondition last_condition =
151153
EmulateInstruction::UnconditionalCondition;
152154

153-
for (const InstructionSP &inst : inst_list.Instructions()) {
154-
if (!inst)
155-
continue;
156-
DumpInstToLog(log, *inst, inst_list);
155+
std::deque<std::size_t> to_visit = {0};
156+
llvm::SmallSet<std::size_t, 0> enqueued = {0};
157+
158+
// Instructions reachable through jumps are inserted on the front.
159+
// The next instruction is inserted on the back.
160+
// Pop from the back to ensure non-branching instructions are visited
161+
// sequentially.
162+
while (!to_visit.empty()) {
163+
const std::size_t current_index = to_visit.back();
164+
Instruction &inst = *inst_list.GetInstructionAtIndex(current_index);
165+
to_visit.pop_back();
166+
DumpInstToLog(log, inst, inst_list);
157167

158168
m_curr_row_modified = false;
159-
m_forward_branch_offset = 0;
169+
m_branch_offset = 0;
160170

161171
lldb::addr_t current_offset =
162-
inst->GetAddress().GetFileAddress() - base_addr;
172+
inst.GetAddress().GetFileAddress() - base_addr;
163173
auto it = saved_unwind_states.upper_bound(current_offset);
164174
assert(it != saved_unwind_states.begin() &&
165175
"Unwind row for the function entry missing");
166176
--it; // Move it to the row corresponding to the current offset
167177

168-
// If the offset of m_state.row doesn't match with the offset we see in
169-
// saved_unwind_states then we have to update current unwind state to
170-
// the saved values. It is happening after we processed an epilogue and a
171-
// return to caller instruction.
178+
// When state is forwarded through a branch, the offset of m_state.row is
179+
// different from the offset available in saved_unwind_states. Use the
180+
// forwarded state in this case, as the previous instruction may have been
181+
// an unconditional jump.
182+
// FIXME: this assignment can always be done unconditionally.
172183
if (it->second.row.GetOffset() != m_state.row.GetOffset())
173184
m_state = it->second;
174185

175-
m_inst_emulator_up->SetInstruction(inst->GetOpcode(), inst->GetAddress(),
186+
m_inst_emulator_up->SetInstruction(inst.GetOpcode(), inst.GetAddress(),
176187
nullptr);
177188
const EmulateInstruction::InstructionCondition new_condition =
178189
m_inst_emulator_up->GetInstructionCondition();
@@ -199,26 +210,43 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
199210

200211
// If the current instruction is a branch forward then save the current
201212
// CFI information for the offset where we are branching.
202-
if (m_forward_branch_offset != 0 &&
203-
range.ContainsFileAddress(inst->GetAddress().GetFileAddress() +
204-
m_forward_branch_offset)) {
213+
Address branch_address = inst.GetAddress();
214+
branch_address.Slide(m_branch_offset);
215+
if (m_branch_offset != 0 &&
216+
range.ContainsFileAddress(branch_address.GetFileAddress())) {
205217
if (auto [it, inserted] = saved_unwind_states.emplace(
206-
current_offset + m_forward_branch_offset, m_state);
207-
inserted)
208-
it->second.row.SetOffset(current_offset + m_forward_branch_offset);
218+
current_offset + m_branch_offset, m_state);
219+
inserted) {
220+
it->second.row.SetOffset(current_offset + m_branch_offset);
221+
if (std::size_t dest_instr_index =
222+
inst_list.GetIndexOfInstructionAtAddress(branch_address);
223+
dest_instr_index < inst_list.GetSize()) {
224+
to_visit.push_front(dest_instr_index);
225+
enqueued.insert(dest_instr_index);
226+
}
227+
}
209228
}
210229

230+
// If inst is a barrier, do not propagate state to the next instruction.
231+
if (inst.IsBarrier())
232+
continue;
233+
211234
// Were there any changes to the CFI while evaluating this instruction?
212235
if (m_curr_row_modified) {
213236
// Save the modified row if we don't already have a CFI row in the
214237
// current address
215-
if (saved_unwind_states.count(current_offset +
216-
inst->GetOpcode().GetByteSize()) == 0) {
217-
m_state.row.SetOffset(current_offset + inst->GetOpcode().GetByteSize());
218-
saved_unwind_states.emplace(
219-
current_offset + inst->GetOpcode().GetByteSize(), m_state);
238+
const lldb::addr_t next_inst_offset =
239+
current_offset + inst.GetOpcode().GetByteSize();
240+
if (saved_unwind_states.count(next_inst_offset) == 0) {
241+
m_state.row.SetOffset(next_inst_offset);
242+
saved_unwind_states.emplace(next_inst_offset, m_state);
220243
}
221244
}
245+
246+
const size_t next_idx = current_index + 1;
247+
const bool never_enqueued = enqueued.insert(next_idx).second;
248+
if (never_enqueued && next_idx < inst_list.GetSize())
249+
to_visit.push_back(next_idx);
222250
}
223251

224252
for (auto &[_, state] : saved_unwind_states)
@@ -506,21 +534,20 @@ bool UnwindAssemblyInstEmulation::WriteRegister(
506534
case EmulateInstruction::eContextAbsoluteBranchRegister:
507535
case EmulateInstruction::eContextRelativeBranchImmediate: {
508536
if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediate &&
509-
context.info.ISAAndImmediate.unsigned_data32 > 0) {
510-
m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
537+
context.info.ISAAndImmediate.unsigned_data32 != 0) {
538+
m_branch_offset = context.info.ISAAndImmediate.unsigned_data32;
511539
} else if (context.GetInfoType() ==
512540
EmulateInstruction::eInfoTypeISAAndImmediateSigned &&
513-
context.info.ISAAndImmediateSigned.signed_data32 > 0) {
514-
m_forward_branch_offset =
515-
context.info.ISAAndImmediateSigned.signed_data32;
541+
context.info.ISAAndImmediateSigned.signed_data32 != 0) {
542+
m_branch_offset = context.info.ISAAndImmediateSigned.signed_data32;
516543
} else if (context.GetInfoType() ==
517544
EmulateInstruction::eInfoTypeImmediate &&
518-
context.info.unsigned_immediate > 0) {
519-
m_forward_branch_offset = context.info.unsigned_immediate;
545+
context.info.unsigned_immediate != 0) {
546+
m_branch_offset = context.info.unsigned_immediate;
520547
} else if (context.GetInfoType() ==
521548
EmulateInstruction::eInfoTypeImmediateSigned &&
522-
context.info.signed_immediate > 0) {
523-
m_forward_branch_offset = context.info.signed_immediate;
549+
context.info.signed_immediate != 0) {
550+
m_branch_offset = context.info.signed_immediate;
524551
}
525552
} break;
526553

lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
6464
lldb_private::EmulateInstruction *inst_emulator)
6565
: UnwindAssembly(arch), m_inst_emulator_up(inst_emulator),
6666
m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr),
67-
m_curr_row_modified(false), m_forward_branch_offset(0) {
67+
m_curr_row_modified(false) {
6868
if (m_inst_emulator_up) {
6969
m_inst_emulator_up->SetBaton(this);
7070
m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister,
@@ -152,7 +152,7 @@ class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly {
152152
bool m_curr_row_modified;
153153
// The instruction is branching forward with the given offset. 0 value means
154154
// no branching.
155-
uint32_t m_forward_branch_offset;
155+
int64_t m_branch_offset = 0;
156156
};
157157

158158
#endif // LLDB_SOURCE_PLUGINS_UNWINDASSEMBLY_INSTEMULATION_UNWINDASSEMBLYINSTEMULATION_H

lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,125 @@ TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
856856
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
857857
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
858858
}
859+
860+
TEST_F(TestArm64InstEmulation, TestMidFunctionEpilogueAndBackwardsJump) {
861+
ArchSpec arch("arm64-apple-ios15");
862+
std::unique_ptr<UnwindAssemblyInstEmulation> engine(
863+
static_cast<UnwindAssemblyInstEmulation *>(
864+
UnwindAssemblyInstEmulation::CreateInstance(arch)));
865+
ASSERT_NE(nullptr, engine);
866+
867+
const UnwindPlan::Row *row;
868+
AddressRange sample_range;
869+
UnwindPlan unwind_plan(eRegisterKindLLDB);
870+
UnwindPlan::Row::AbstractRegisterLocation regloc;
871+
872+
// clang-format off
873+
uint8_t data[] = {
874+
0xff, 0xc3, 0x00, 0xd1, // <+0>: sub sp, sp, #0x30
875+
0xfd, 0x7b, 0x02, 0xa9, // <+4>: stp x29, x30, [sp, #0x20]
876+
0xfd, 0x83, 0x00, 0x91, // <+8>: add x29, sp, #0x20
877+
0x1f, 0x04, 0x00, 0xf1, // <+12>: cmp x0, #0x1
878+
0x21, 0x01, 0x00, 0x54, // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
879+
0xfd, 0x7b, 0x42, 0xa9, // <+20>: ldp x29, x30, [sp, #0x20]
880+
0xff, 0xc3, 0x00, 0x91, // <+24>: add sp, sp, #0x30
881+
0xc0, 0x03, 0x5f, 0xd6, // <+28>: ret
882+
// AFTER_EPILOGUE
883+
0x37, 0x00, 0x80, 0xd2, // <+32>: mov x23, #0x1
884+
0xf6, 0x5f, 0x41, 0xa9, // <+36>: ldp x22, x23, [sp, #0x10]
885+
0xfd, 0x7b, 0x42, 0xa9, // <+40>: ldp x29, x30, [sp, #0x20]
886+
0xff, 0xc3, 0x00, 0x91, // <+44>: add sp, sp, #0x30
887+
0xc0, 0x03, 0x5f, 0xd6, // <+48>: ret
888+
// DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
889+
0xf6, 0x5f, 0x01, 0xa9, // <+52>: stp x22, x23, [sp, #0x10]
890+
0x36, 0x00, 0x80, 0xd2, // <+56>: mov x22, #0x1
891+
0x37, 0x00, 0x80, 0xd2, // <+60>: mov x23, #0x1
892+
0xf8, 0xff, 0xff, 0x17, // <+64>: b ; <+32> AFTER_EPILOGUE
893+
};
894+
895+
// UnwindPlan we expect:
896+
// row[0]: 0: CFA=sp +0 =>
897+
// row[1]: 4: CFA=sp+48 =>
898+
// row[2]: 8: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
899+
// row[3]: 12: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
900+
// row[4]: 24: CFA=sp+48 => fp=<same> lr=<same>
901+
//
902+
// This must come from +56
903+
// row[5]: 32: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]
904+
// row[6]: 40: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=same, x23 = same
905+
// row[6]: 44: CFA=sp+48 => fp=same lr=same x22=same, x23 = same
906+
// row[6]: 48: CFA=sp0 => fp=same lr=same x22=same, x23 = same
907+
//
908+
// row[x]: 52: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
909+
// row[x]: 56: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]
910+
// clang-format on
911+
912+
sample_range = AddressRange(0x1000, sizeof(data));
913+
914+
EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
915+
sample_range, data, sizeof(data), unwind_plan));
916+
917+
// At the end of prologue (+12), CFA = fp + 16.
918+
// <+0>: sub sp, sp, #0x30
919+
// <+4>: stp x29, x30, [sp, #0x20]
920+
// <+8>: add x29, sp, #0x20
921+
row = unwind_plan.GetRowForFunctionOffset(12);
922+
EXPECT_EQ(12, row->GetOffset());
923+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
924+
EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);
925+
EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);
926+
927+
// +16 and +20 are the same as +12.
928+
// <+12>: cmp x0, #0x1
929+
// <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
930+
EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(16)->GetOffset());
931+
EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(20)->GetOffset());
932+
933+
// After restoring $fp to caller's value, CFA = $sp + 48
934+
// <+20>: ldp x29, x30, [sp, #0x20]
935+
row = unwind_plan.GetRowForFunctionOffset(24);
936+
EXPECT_EQ(24, row->GetOffset());
937+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
938+
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
939+
EXPECT_EQ(row->GetCFAValue().GetOffset(), 48);
940+
941+
// $sp has been restored
942+
// <+24>: add sp, sp, #0x30
943+
row = unwind_plan.GetRowForFunctionOffset(28);
944+
EXPECT_EQ(28, row->GetOffset());
945+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
946+
EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
947+
EXPECT_EQ(row->GetCFAValue().GetOffset(), 0);
948+
949+
// Row for offset +32 should not inherit the state of the `ret` instruction
950+
// in +28. Instead, it should inherit the state of the branch in +64.
951+
// Check for register x22, which is available in row +64.
952+
// <+28>: ret
953+
// <+32>: mov x23, #0x1
954+
row = unwind_plan.GetRowForFunctionOffset(32);
955+
EXPECT_EQ(32, row->GetOffset());
956+
{
957+
UnwindPlan::Row::AbstractRegisterLocation loc;
958+
EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, loc));
959+
EXPECT_TRUE(loc.IsAtCFAPlusOffset());
960+
EXPECT_EQ(loc.GetOffset(), -32);
961+
}
962+
963+
// Check that the state of this branch
964+
// <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE
965+
// was forwarded to the branch target:
966+
// <+52>: stp x22, x23, [sp, #0x10]
967+
row = unwind_plan.GetRowForFunctionOffset(52);
968+
EXPECT_EQ(52, row->GetOffset());
969+
EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());
970+
EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);
971+
EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);
972+
973+
row = unwind_plan.GetRowForFunctionOffset(64);
974+
{
975+
UnwindPlan::Row::AbstractRegisterLocation loc;
976+
EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, loc));
977+
EXPECT_TRUE(loc.IsAtCFAPlusOffset());
978+
EXPECT_EQ(loc.GetOffset(), -32);
979+
}
980+
}

0 commit comments

Comments
 (0)