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