1 //===-- TestArm64InstEmulation.cpp ----------------------------------------===//
2 
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "gtest/gtest.h"
11 
12 #include <vector>
13 
14 #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"
15 
16 #include "lldb/Core/Address.h"
17 #include "lldb/Core/AddressRange.h"
18 #include "lldb/Symbol/UnwindPlan.h"
19 #include "lldb/Target/UnwindAssembly.h"
20 #include "lldb/Utility/ArchSpec.h"
21 
22 #include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h"
23 #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
24 #include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
25 #include "llvm/Support/TargetSelect.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 class TestArm64InstEmulation : public testing::Test {
31 public:
32   static void SetUpTestCase();
33   static void TearDownTestCase();
34 
35   //  virtual void SetUp() override { }
36   //  virtual void TearDown() override { }
37 
38 protected:
39 };
40 
SetUpTestCase()41 void TestArm64InstEmulation::SetUpTestCase() {
42   llvm::InitializeAllTargets();
43   llvm::InitializeAllAsmPrinters();
44   llvm::InitializeAllTargetMCs();
45   llvm::InitializeAllDisassemblers();
46   DisassemblerLLVMC::Initialize();
47   EmulateInstructionARM64::Initialize();
48 }
49 
TearDownTestCase()50 void TestArm64InstEmulation::TearDownTestCase() {
51   DisassemblerLLVMC::Terminate();
52   EmulateInstructionARM64::Terminate();
53 }
54 
TEST_F(TestArm64InstEmulation,TestSimpleDarwinFunction)55 TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) {
56   ArchSpec arch("arm64-apple-ios10");
57   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
58       static_cast<UnwindAssemblyInstEmulation *>(
59           UnwindAssemblyInstEmulation::CreateInstance(arch)));
60   ASSERT_NE(nullptr, engine);
61 
62   UnwindPlan::RowSP row_sp;
63   AddressRange sample_range;
64   UnwindPlan unwind_plan(eRegisterKindLLDB);
65   UnwindPlan::Row::RegisterLocation regloc;
66 
67   // 'int main() { }' compiled for arm64-apple-ios with clang
68   uint8_t data[] = {
69       0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd :  stp x29, x30, [sp, #-0x10]!
70       0xfd, 0x03, 0x00, 0x91, // 0x910003fd :  mov x29, sp
71       0xff, 0x43, 0x00, 0xd1, // 0xd10043ff :  sub sp, sp, #0x10
72 
73       0xbf, 0x03, 0x00, 0x91, // 0x910003bf :  mov sp, x29
74       0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd :  ldp x29, x30, [sp], #16
75       0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 :  ret
76   };
77 
78   // UnwindPlan we expect:
79 
80   // row[0]:    0: CFA=sp +0 =>
81   // row[1]:    4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
82   // row[2]:    8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
83   // row[2]:   16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
84   // row[3]:   20: CFA=sp +0 => fp= <same> lr= <same>
85 
86   sample_range = AddressRange(0x1000, sizeof(data));
87 
88   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
89       sample_range, data, sizeof(data), unwind_plan));
90 
91   // CFA=sp +0
92   row_sp = unwind_plan.GetRowForFunctionOffset(0);
93   EXPECT_EQ(0ull, row_sp->GetOffset());
94   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
95   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
96   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
97 
98   // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
99   row_sp = unwind_plan.GetRowForFunctionOffset(4);
100   EXPECT_EQ(4ull, row_sp->GetOffset());
101   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
102   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
103   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
104 
105   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
106   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
107   EXPECT_EQ(-16, regloc.GetOffset());
108 
109   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
110   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
111   EXPECT_EQ(-8, regloc.GetOffset());
112 
113   // CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
114   row_sp = unwind_plan.GetRowForFunctionOffset(8);
115   EXPECT_EQ(8ull, row_sp->GetOffset());
116   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
117   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
118   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
119 
120   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
121   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
122   EXPECT_EQ(-16, regloc.GetOffset());
123 
124   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
125   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
126   EXPECT_EQ(-8, regloc.GetOffset());
127 
128   // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
129   row_sp = unwind_plan.GetRowForFunctionOffset(16);
130   EXPECT_EQ(16ull, row_sp->GetOffset());
131   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
132   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
133   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
134 
135   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
136   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
137   EXPECT_EQ(-16, regloc.GetOffset());
138 
139   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
140   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
141   EXPECT_EQ(-8, regloc.GetOffset());
142 
143   // CFA=sp +0 => fp= <same> lr= <same>
144   row_sp = unwind_plan.GetRowForFunctionOffset(20);
145   EXPECT_EQ(20ull, row_sp->GetOffset());
146   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
147   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
148   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
149 }
150 
TEST_F(TestArm64InstEmulation,TestMediumDarwinFunction)151 TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) {
152   ArchSpec arch("arm64-apple-ios10");
153   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
154       static_cast<UnwindAssemblyInstEmulation *>(
155           UnwindAssemblyInstEmulation::CreateInstance(arch)));
156   ASSERT_NE(nullptr, engine);
157 
158   UnwindPlan::RowSP row_sp;
159   AddressRange sample_range;
160   UnwindPlan unwind_plan(eRegisterKindLLDB);
161   UnwindPlan::Row::RegisterLocation regloc;
162 
163   // disassembly of -[NSPlaceholderString initWithBytes:length:encoding:]
164   // from Foundation for iOS.
165   uint8_t data[] = {
166       0xf6, 0x57, 0xbd, 0xa9, // 0:  0xa9bd57f6 stp x22, x21, [sp, #-48]!
167       0xf4, 0x4f, 0x01, 0xa9, // 4:  0xa9014ff4 stp x20, x19, [sp, #16]
168       0xfd, 0x7b, 0x02, 0xa9, // 8:  0xa9027bfd stp x29, x30, [sp, #32]
169       0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32
170       0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16
171 
172       // [... function body ...]
173       0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop
174 
175       0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32
176       0xfd, 0x7b, 0x42, 0xa9, // 28: 0xa9427bfd ldp x29, x30, [sp, #32]
177       0xf4, 0x4f, 0x41, 0xa9, // 32: 0xa9414ff4 ldp x20, x19, [sp, #16]
178       0xf6, 0x57, 0xc3, 0xa8, // 36: 0xa8c357f6 ldp x22, x21, [sp], #48
179       0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b   0x18f640524 ; symbol stub
180                               // for: CFStringCreateWithBytes
181   };
182 
183   // UnwindPlan we expect:
184   //  0: CFA=sp +0 =>
185   //  4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]
186   //  8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
187   // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
188   // fp=[CFA-16] lr=[CFA-8]
189   // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
190   // fp=[CFA-16] lr=[CFA-8]
191 
192   // [... function body ...]
193 
194   // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
195   // fp=[CFA-16] lr=[CFA-8]
196   // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=
197   // <same> lr= <same>
198   // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=
199   // <same> lr= <same>
200   // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>
201   // lr= <same>
202 
203   sample_range = AddressRange(0x1000, sizeof(data));
204 
205   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
206       sample_range, data, sizeof(data), unwind_plan));
207 
208   // 0: CFA=sp +0 =>
209   row_sp = unwind_plan.GetRowForFunctionOffset(0);
210   EXPECT_EQ(0ull, row_sp->GetOffset());
211   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
212   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
213   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
214 
215   // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]
216   row_sp = unwind_plan.GetRowForFunctionOffset(4);
217   EXPECT_EQ(4ull, row_sp->GetOffset());
218   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
219   EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
220 
221   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
222   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
223   EXPECT_EQ(-40, regloc.GetOffset());
224 
225   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
226   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
227   EXPECT_EQ(-48, regloc.GetOffset());
228 
229   // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
230   row_sp = unwind_plan.GetRowForFunctionOffset(8);
231   EXPECT_EQ(8ull, row_sp->GetOffset());
232   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
233   EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
234 
235   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
236   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
237   EXPECT_EQ(-24, regloc.GetOffset());
238 
239   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
240   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
241   EXPECT_EQ(-32, regloc.GetOffset());
242 
243   // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
244   // fp=[CFA-16] lr=[CFA-8]
245   row_sp = unwind_plan.GetRowForFunctionOffset(12);
246   EXPECT_EQ(12ull, row_sp->GetOffset());
247   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
248   EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
249 
250   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
251   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
252   EXPECT_EQ(-16, regloc.GetOffset());
253 
254   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
255   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
256   EXPECT_EQ(-8, regloc.GetOffset());
257 
258   // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
259   // fp=[CFA-16] lr=[CFA-8]
260   row_sp = unwind_plan.GetRowForFunctionOffset(16);
261   EXPECT_EQ(16ull, row_sp->GetOffset());
262   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
263   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
264   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
265 
266   // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
267   // fp=[CFA-16] lr=[CFA-8]
268   row_sp = unwind_plan.GetRowForFunctionOffset(28);
269   EXPECT_EQ(28ull, row_sp->GetOffset());
270   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
271   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
272   EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset());
273 
274   // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=
275   // <same> lr= <same>
276   row_sp = unwind_plan.GetRowForFunctionOffset(32);
277   EXPECT_EQ(32ull, row_sp->GetOffset());
278 
279   // I'd prefer if these restored registers were cleared entirely instead of set
280   // to IsSame...
281   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
282   EXPECT_TRUE(regloc.IsSame());
283 
284   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
285   EXPECT_TRUE(regloc.IsSame());
286 
287   // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=
288   // <same> lr= <same>
289   row_sp = unwind_plan.GetRowForFunctionOffset(36);
290   EXPECT_EQ(36ull, row_sp->GetOffset());
291 
292   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
293   EXPECT_TRUE(regloc.IsSame());
294 
295   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
296   EXPECT_TRUE(regloc.IsSame());
297 
298   // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>
299   // lr= <same>
300   row_sp = unwind_plan.GetRowForFunctionOffset(40);
301   EXPECT_EQ(40ull, row_sp->GetOffset());
302   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
303   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
304   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
305 
306   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
307   EXPECT_TRUE(regloc.IsSame());
308 
309   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
310   EXPECT_TRUE(regloc.IsSame());
311 }
312 
TEST_F(TestArm64InstEmulation,TestFramelessThreeEpilogueFunction)313 TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) {
314   ArchSpec arch("arm64-apple-ios10");
315   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
316       static_cast<UnwindAssemblyInstEmulation *>(
317           UnwindAssemblyInstEmulation::CreateInstance(arch)));
318   ASSERT_NE(nullptr, engine);
319 
320   UnwindPlan::RowSP row_sp;
321   AddressRange sample_range;
322   UnwindPlan unwind_plan(eRegisterKindLLDB);
323   UnwindPlan::Row::RegisterLocation regloc;
324 
325   // disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u>
326   // from JavaScriptcore for iOS.
327   uint8_t data[] = {
328       0x08, 0x3c, 0x0f, 0x53, //  0: 0x530f3c08 ubfx   w8, w0, #15, #1
329       0x68, 0x00, 0x00, 0x39, //  4: 0x39000068 strb   w8, [x3]
330       0x08, 0x3c, 0x40, 0xd2, //  8: 0xd2403c08 eor    x8, x0, #0xffff
331       0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst    x0, #0x8000
332 
333       // [...]
334 
335       0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp    x9, x12
336       0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34
337       0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str    wzr, [x2]
338       0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr    w0, wzr, #0x1
339       0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret
340       0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor    x9, x12, x9
341 
342       // [...]
343 
344       0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add    w8, w8, #0x1
345       0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str    w8, [x2]
346       0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr    w0, wzr, #0x1
347       0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret
348       0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov    w0, #0x0
349       0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret
350 
351   };
352 
353   // UnwindPlan we expect:
354   //  0: CFA=sp +0 =>
355   // (possibly with additional rows at offsets 36 and 56 saying the same thing)
356 
357   sample_range = AddressRange(0x1000, sizeof(data));
358 
359   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
360       sample_range, data, sizeof(data), unwind_plan));
361 
362   // 0: CFA=sp +0 =>
363   row_sp = unwind_plan.GetRowForFunctionOffset(0);
364   EXPECT_EQ(0ull, row_sp->GetOffset());
365   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
366   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
367   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
368 
369   row_sp = unwind_plan.GetRowForFunctionOffset(32);
370   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
371   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
372   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
373 
374   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc));
375   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
376   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc));
377   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc));
378   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x23_arm64, regloc));
379   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x24_arm64, regloc));
380   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x25_arm64, regloc));
381   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x26_arm64, regloc));
382   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x27_arm64, regloc));
383   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x28_arm64, regloc));
384   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc));
385   EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc));
386 
387   row_sp = unwind_plan.GetRowForFunctionOffset(36);
388   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
389   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
390   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
391 
392   row_sp = unwind_plan.GetRowForFunctionOffset(52);
393   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
394   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
395   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
396 
397   row_sp = unwind_plan.GetRowForFunctionOffset(56);
398   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
399   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
400   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
401 
402   row_sp = unwind_plan.GetRowForFunctionOffset(60);
403   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
404   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
405   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
406 }
407 
TEST_F(TestArm64InstEmulation,TestRegisterSavedTwice)408 TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) {
409   ArchSpec arch("arm64-apple-ios10");
410   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
411       static_cast<UnwindAssemblyInstEmulation *>(
412           UnwindAssemblyInstEmulation::CreateInstance(arch)));
413   ASSERT_NE(nullptr, engine);
414 
415   UnwindPlan::RowSP row_sp;
416   AddressRange sample_range;
417   UnwindPlan unwind_plan(eRegisterKindLLDB);
418   UnwindPlan::Row::RegisterLocation regloc;
419 
420   // disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS.
421   uint8_t data[] = {
422 
423       0xfc, 0x6f, 0xba, 0xa9, //  0: 0xa9ba6ffc stp  x28, x27, [sp, #-0x60]!
424       0xfa, 0x67, 0x01, 0xa9, //  4: 0xa90167fa stp  x26, x25, [sp, #0x10]
425       0xf8, 0x5f, 0x02, 0xa9, //  8: 0xa9025ff8 stp  x24, x23, [sp, #0x20]
426       0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp  x22, x21, [sp, #0x30]
427       0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp  x20, x19, [sp, #0x40]
428       0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp  x29, x30, [sp, #0x50]
429       0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add  x29, sp, #0x50
430       0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub  sp, sp, #0x30
431 
432       // mid-function, store x20 & x24 on the stack at a different location.
433       // this should not show up in the unwind plan; caller's values are not
434       // being saved to stack.
435       0xf8, 0x53, 0x01, 0xa9, // 32: 0xa90153f8 stp    x24, x20, [sp, #0x10]
436 
437       // mid-function, copy x20 and x19 off of the stack -- but not from
438       // their original locations.  unwind plan should ignore this.
439       0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp  x20, x19, [sp, #0x10]
440 
441       // epilogue
442       0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub  sp, x29, #0x50
443       0xfd, 0x7b, 0x45, 0xa9, // 44: 0xa9457bfd ldp  x29, x30, [sp, #0x50]
444       0xf4, 0x4f, 0x44, 0xa9, // 48: 0xa9444ff4 ldp  x20, x19, [sp, #0x40]
445       0xf6, 0x57, 0x43, 0xa9, // 52: 0xa94357f6 ldp  x22, x21, [sp, #0x30]
446       0xf8, 0x5f, 0x42, 0xa9, // 56: 0xa9425ff8 ldp  x24, x23, [sp, #0x20]
447       0xfa, 0x67, 0x41, 0xa9, // 60: 0xa94167fa ldp  x26, x25, [sp, #0x10]
448       0xfc, 0x6f, 0xc6, 0xa8, // 64: 0xa8c66ffc ldp  x28, x27, [sp], #0x60
449       0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret
450   };
451 
452   // UnwindPlan we expect:
453   //   0: CFA=sp +0 =>
454   //   4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96]
455   //   8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
456   //  12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]
457   //  x27=[CFA-88] x28=[CFA-96]
458   //  16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]
459   //  x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
460   //  20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
461   //  x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
462   //  x28=[CFA-96]
463   //  24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
464   //  x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
465   //  x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
466   //  28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
467   //  x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
468   //  x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
469 
470   //  44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
471   //  x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
472   //  x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]
473   //  48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]
474   //  x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]
475   //  x28=[CFA-96]
476   //  52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]
477   //  x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
478   //  56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]
479   //  x27=[CFA-88] x28=[CFA-96]
480   //  60: CFA=sp+96 =>  x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]
481   //  64: CFA=sp+96 =>  x27=[CFA-88] x28=[CFA-96]
482   //  68: CFA=sp +0 =>
483 
484   sample_range = AddressRange(0x1000, sizeof(data));
485 
486   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
487       sample_range, data, sizeof(data), unwind_plan));
488 
489   row_sp = unwind_plan.GetRowForFunctionOffset(36);
490   EXPECT_EQ(28ull, row_sp->GetOffset());
491   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
492   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
493   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
494 
495   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
496   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
497   EXPECT_EQ(-32, regloc.GetOffset());
498 
499   row_sp = unwind_plan.GetRowForFunctionOffset(40);
500   EXPECT_EQ(28ull, row_sp->GetOffset());
501   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
502   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
503   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
504 
505   EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc));
506   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
507   EXPECT_EQ(-32, regloc.GetOffset());
508 }
509 
TEST_F(TestArm64InstEmulation,TestRegisterDoubleSpills)510 TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) {
511   ArchSpec arch("arm64-apple-ios10");
512   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
513       static_cast<UnwindAssemblyInstEmulation *>(
514           UnwindAssemblyInstEmulation::CreateInstance(arch)));
515   ASSERT_NE(nullptr, engine);
516 
517   UnwindPlan::RowSP row_sp;
518   AddressRange sample_range;
519   UnwindPlan unwind_plan(eRegisterKindLLDB);
520   UnwindPlan::Row::RegisterLocation regloc;
521 
522   // this file built with clang for iOS arch arm64 optimization -Os
523   // #include <stdio.h>
524   // double foo(double in) {
525   // double arr[32];
526   // for (int i = 0; i < 32; i++)
527   //   arr[i] = in + i;
528   // for (int i = 2; i < 30; i++)
529   //   arr[i] = ((((arr[i - 1] * arr[i - 2] * 0.2) + (0.7 * arr[i])) /
530   //   ((((arr[i] * 0.73) + 0.65) * (arr[i - 1] + 0.2)) - ((arr[i + 1] + (arr[i]
531   //   * 0.32) + 0.52) / 0.3) + (0.531 * arr[i - 2]))) + ((arr[i - 1] + 5) /
532   //   ((arr[i + 2] + 0.4) / arr[i])) + (arr[5] * (0.17 + arr[7] * arr[i])) +
533   //   ((i > 5 ? (arr[i - 3]) : arr[i - 1]) * 0.263) + (((arr[i - 2] + arr[i -
534   //   1]) * 0.3252) + 3.56) - (arr[i + 1] * 0.852311)) * ((arr[i] * 85234.1345)
535   //   + (77342.451324 / (arr[i - 2] + arr[i - 1] - 73425341.33455))) + (arr[i]
536   //   * 875712013.55) - (arr[i - 1] * 0.5555) - ((arr[i] * (arr[i + 1] +
537   //   17342834.44) / 8688200123.555)) + (arr[i - 2] + 8888.888);
538   // return arr[16];
539   //}
540   // int main(int argc, char **argv) { printf("%g\n", foo(argc)); }
541 
542   // so function foo() uses enough registers that it spills the callee-saved
543   // floating point registers.
544   uint8_t data[] = {
545       // prologue
546       0xef, 0x3b, 0xba, 0x6d, //  0: 0x6dba3bef   stp    d15, d14, [sp, #-0x60]!
547       0xed, 0x33, 0x01, 0x6d, //  4: 0x6d0133ed   stp    d13, d12, [sp, #0x10]
548       0xeb, 0x2b, 0x02, 0x6d, //  8: 0x6d022beb   stp    d11, d10, [sp, #0x20]
549       0xe9, 0x23, 0x03, 0x6d, // 12: 0x6d0323e9   stp    d9, d8, [sp, #0x30]
550       0xfc, 0x6f, 0x04, 0xa9, // 16: 0xa9046ffc   stp    x28, x27, [sp, #0x40]
551       0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd   stp    x29, x30, [sp, #0x50]
552       0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd   add    x29, sp, #0x50
553       0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff   sub    sp, sp, #0x110
554 
555       // epilogue
556       0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf   sub    sp, x29, #0x50
557       0xfd, 0x7b, 0x45, 0xa9, // 36: 0xa9457bfd   ldp    x29, x30, [sp, #0x50]
558       0xfc, 0x6f, 0x44, 0xa9, // 40: 0xa9446ffc   ldp    x28, x27, [sp, #0x40]
559       0xe9, 0x23, 0x43, 0x6d, // 44: 0x6d4323e9   ldp    d9, d8, [sp, #0x30]
560       0xeb, 0x2b, 0x42, 0x6d, // 48: 0x6d422beb   ldp    d11, d10, [sp, #0x20]
561       0xed, 0x33, 0x41, 0x6d, // 52: 0x6d4133ed   ldp    d13, d12, [sp, #0x10]
562       0xef, 0x3b, 0xc6, 0x6c, // 56: 0x6cc63bef   ldp    d15, d14, [sp], #0x60
563       0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0   ret
564   };
565 
566   // UnwindPlan we expect:
567   //   0: CFA=sp +0 =>
568   //   4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]
569   //   8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
570   //  12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]
571   //  d14=[CFA-88] d15=[CFA-96]
572   //  16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]
573   //  d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
574   //  20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]
575   //  d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]
576   //  d15=[CFA-96]
577   //  24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
578   //  d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
579   //  d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
580   //  28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
581   //  d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
582   //  d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
583   //  36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
584   //  d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
585   //  d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
586   //  40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]
587   //  d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]
588   //  d15=[CFA-96]
589   //  44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]
590   //  d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
591   //  48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]
592   //  d14=[CFA-88] d15=[CFA-96]
593   //  52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
594   //  56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]
595   //  60: CFA=sp +0 =>
596 
597   sample_range = AddressRange(0x1000, sizeof(data));
598 
599   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
600       sample_range, data, sizeof(data), unwind_plan));
601 
602   //  28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]
603   //  d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]
604   //  d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]
605   row_sp = unwind_plan.GetRowForFunctionOffset(28);
606   EXPECT_EQ(28ull, row_sp->GetOffset());
607   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
608   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
609   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
610 
611   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d15_arm64, regloc));
612   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
613   EXPECT_EQ(-96, regloc.GetOffset());
614 
615   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d14_arm64, regloc));
616   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
617   EXPECT_EQ(-88, regloc.GetOffset());
618 
619   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d13_arm64, regloc));
620   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
621   EXPECT_EQ(-80, regloc.GetOffset());
622 
623   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d12_arm64, regloc));
624   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
625   EXPECT_EQ(-72, regloc.GetOffset());
626 
627   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d11_arm64, regloc));
628   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
629   EXPECT_EQ(-64, regloc.GetOffset());
630 
631   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d10_arm64, regloc));
632   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
633   EXPECT_EQ(-56, regloc.GetOffset());
634 
635   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d9_arm64, regloc));
636   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
637   EXPECT_EQ(-48, regloc.GetOffset());
638 
639   EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d8_arm64, regloc));
640   EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
641   EXPECT_EQ(-40, regloc.GetOffset());
642 
643   //  60: CFA=sp +0 =>
644   row_sp = unwind_plan.GetRowForFunctionOffset(60);
645   EXPECT_EQ(60ull, row_sp->GetOffset());
646   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
647   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
648   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
649 
650   if (row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)) {
651     EXPECT_TRUE(regloc.IsSame());
652   }
653   if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) {
654     EXPECT_TRUE(regloc.IsSame());
655   }
656   if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) {
657     EXPECT_TRUE(regloc.IsSame());
658   }
659   if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) {
660     EXPECT_TRUE(regloc.IsSame());
661   }
662   if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) {
663     EXPECT_TRUE(regloc.IsSame());
664   }
665   if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) {
666     EXPECT_TRUE(regloc.IsSame());
667   }
668   if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) {
669     EXPECT_TRUE(regloc.IsSame());
670   }
671   if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) {
672     EXPECT_TRUE(regloc.IsSame());
673   }
674   if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) {
675     EXPECT_TRUE(regloc.IsSame());
676   }
677   if (row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)) {
678     EXPECT_TRUE(regloc.IsSame());
679   }
680 }
681 
TEST_F(TestArm64InstEmulation,TestCFARegisterTrackedAcrossJumps)682 TEST_F(TestArm64InstEmulation, TestCFARegisterTrackedAcrossJumps) {
683   ArchSpec arch("arm64-apple-ios10");
684   std::unique_ptr<UnwindAssemblyInstEmulation> engine(
685       static_cast<UnwindAssemblyInstEmulation *>(
686           UnwindAssemblyInstEmulation::CreateInstance(arch)));
687   ASSERT_NE(nullptr, engine);
688 
689   UnwindPlan::RowSP row_sp;
690   AddressRange sample_range;
691   UnwindPlan unwind_plan(eRegisterKindLLDB);
692   UnwindPlan::Row::RegisterLocation regloc;
693 
694   uint8_t data[] = {
695       // prologue
696       0xf4, 0x4f, 0xbe, 0xa9, //  0: 0xa9be4ff4 stp x20, x19, [sp, #-0x20]!
697       0xfd, 0x7b, 0x01, 0xa9, //  4: 0xa9017bfd stp x29, x30, [sp, #0x10]
698       0xfd, 0x43, 0x00, 0x91, //  8: 0x910043fd add x29, sp, #0x10
699       0xff, 0x43, 0x00, 0xd1, // 12: 0xd10043ff sub sp, sp, #0x10
700       // conditional branch over a mid-function epilogue
701       0xeb, 0x00, 0x00, 0x54, // 16: 0x540000eb b.lt <+44>
702       // mid-function epilogue
703       0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f   nop
704       0xe0, 0x03, 0x13, 0xaa, // 24: 0xaa1303e0   mov    x0, x19
705       0xbf, 0x43, 0x00, 0xd1, // 28: 0xd10043bf   sub    sp, x29, #0x10
706       0xfd, 0x7b, 0x41, 0xa9, // 32: 0xa9417bfd   ldp    x29, x30, [sp, #0x10]
707       0xf4, 0x4f, 0xc2, 0xa8, // 36: 0xa8c24ff4   ldp    x20, x19, [sp], #0x20
708       0xc0, 0x03, 0x5f, 0xd6, // 40: 0xd65f03c0   ret
709       // unwind state restored, we're using a frame pointer, let's change the
710       // stack pointer and see no change in how the CFA is computed
711       0x1f, 0x20, 0x03, 0xd5, // 44: 0xd503201f   nop
712       0xff, 0x43, 0x00, 0xd1, // 48: 0xd10043ff   sub    sp, sp, #0x10
713       0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f   nop
714       // final epilogue
715       0xe0, 0x03, 0x13, 0xaa, // 56: 0xaa1303e0   mov    x0, x19
716       0xbf, 0x43, 0x00, 0xd1, // 60: 0xd10043bf   sub    sp, x29, #0x10
717       0xfd, 0x7b, 0x41, 0xa9, // 64: 0xa9417bfd   ldp    x29, x30, [sp, #0x10]
718       0xf4, 0x4f, 0xc2, 0xa8, // 68: 0xa8c24ff4   ldp    x20, x19, [sp], #0x20
719       0xc0, 0x03, 0x5f, 0xd6, // 72: 0xd65f03c0   ret
720 
721       0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f   nop
722   };
723 
724   // UnwindPlan we expect:
725   // row[0]:    0: CFA=sp +0 =>
726   // row[1]:    4: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32]
727   // row[2]:    8: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
728   // row[3]:   12: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
729   // row[4]:   32: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
730   // row[5]:   36: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
731   // row[6]:   40: CFA=sp +0 => x19= <same> x20= <same> fp= <same> lr= <same>
732   // row[7]:   44: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
733   // row[8]:   64: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
734   // row[9]:   68: CFA=sp+32 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
735   // row[10]:  72: CFA=sp +0 => x19= <same> x20= <same> fp= <same> lr= <same>
736 
737   // The specific bug we're looking for is this incorrect CFA definition,
738   // where the InstEmulation is using the $sp value mixed in with $fp,
739   // it looks like this:
740   //
741   // row[7]:   44: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
742   // row[8]:   52: CFA=fp+64 => x19=[CFA-24] x20=[CFA-32] fp=[CFA-16] lr=[CFA-8]
743   // row[9]:   68: CFA=fp+64 => x19=[CFA-24] x20=[CFA-32] fp= <same> lr= <same>
744 
745   sample_range = AddressRange(0x1000, sizeof(data));
746 
747   EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
748       sample_range, data, sizeof(data), unwind_plan));
749 
750   // Confirm CFA at mid-func epilogue 'ret' is $sp+0
751   row_sp = unwind_plan.GetRowForFunctionOffset(40);
752   EXPECT_EQ(40ull, row_sp->GetOffset());
753   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
754   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
755   EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset());
756 
757   // After the 'ret', confirm we're back to the correct CFA of $fp+16
758   row_sp = unwind_plan.GetRowForFunctionOffset(44);
759   EXPECT_EQ(44ull, row_sp->GetOffset());
760   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
761   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
762   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
763 
764   // Confirm that we have no additional UnwindPlan rows before the
765   // real epilogue -- we still get the Row at offset 44.
766   row_sp = unwind_plan.GetRowForFunctionOffset(60);
767   EXPECT_EQ(44ull, row_sp->GetOffset());
768   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
769   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
770   EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset());
771 
772   // And in the epilogue, confirm that we start by switching back to
773   // defining the CFA in terms of $sp.
774   row_sp = unwind_plan.GetRowForFunctionOffset(64);
775   EXPECT_EQ(64ull, row_sp->GetOffset());
776   EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
777   EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
778   EXPECT_EQ(32, row_sp->GetCFAValue().GetOffset());
779 }
780 
781