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