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
27using namespace lldb;
28using namespace lldb_private;
29
30class TestArm64InstEmulation : public testing::Test {
31public:
32 static void SetUpTestCase();
33 static void TearDownTestCase();
34
35 // virtual void SetUp() override { }
36 // virtual void TearDown() override { }
37
38protected:
39};
40
41void TestArm64InstEmulation::SetUpTestCase() {
42 llvm::InitializeAllTargets();
43 llvm::InitializeAllAsmPrinters();
44 llvm::InitializeAllTargetMCs();
45 llvm::InitializeAllDisassemblers();
46 DisassemblerLLVMC::Initialize();
47 EmulateInstructionARM64::Initialize();
48}
49
50void TestArm64InstEmulation::TearDownTestCase() {
51 DisassemblerLLVMC::Terminate();
52 EmulateInstructionARM64::Terminate();
53}
54
55TEST_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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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
151TEST_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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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
313TEST_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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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(offset: 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
408TEST_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(offset: 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(offset: 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
510TEST_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(offset: 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(offset: 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(reg_num: fpu_d8_arm64, register_location&: regloc)) {
651 EXPECT_TRUE(regloc.IsSame());
652 }
653 if (row_sp->GetRegisterInfo(reg_num: fpu_d9_arm64, register_location&: regloc)) {
654 EXPECT_TRUE(regloc.IsSame());
655 }
656 if (row_sp->GetRegisterInfo(reg_num: fpu_d10_arm64, register_location&: regloc)) {
657 EXPECT_TRUE(regloc.IsSame());
658 }
659 if (row_sp->GetRegisterInfo(reg_num: fpu_d11_arm64, register_location&: regloc)) {
660 EXPECT_TRUE(regloc.IsSame());
661 }
662 if (row_sp->GetRegisterInfo(reg_num: fpu_d12_arm64, register_location&: regloc)) {
663 EXPECT_TRUE(regloc.IsSame());
664 }
665 if (row_sp->GetRegisterInfo(reg_num: fpu_d13_arm64, register_location&: regloc)) {
666 EXPECT_TRUE(regloc.IsSame());
667 }
668 if (row_sp->GetRegisterInfo(reg_num: fpu_d14_arm64, register_location&: regloc)) {
669 EXPECT_TRUE(regloc.IsSame());
670 }
671 if (row_sp->GetRegisterInfo(reg_num: fpu_d15_arm64, register_location&: regloc)) {
672 EXPECT_TRUE(regloc.IsSame());
673 }
674 if (row_sp->GetRegisterInfo(reg_num: gpr_x27_arm64, register_location&: regloc)) {
675 EXPECT_TRUE(regloc.IsSame());
676 }
677 if (row_sp->GetRegisterInfo(reg_num: gpr_x28_arm64, register_location&: regloc)) {
678 EXPECT_TRUE(regloc.IsSame());
679 }
680}
681
682TEST_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(offset: 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(offset: 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(offset: 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(offset: 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
781TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {
782 ArchSpec arch("arm64-apple-ios15");
783 std::unique_ptr<UnwindAssemblyInstEmulation> engine(
784 static_cast<UnwindAssemblyInstEmulation *>(
785 UnwindAssemblyInstEmulation::CreateInstance(arch)));
786 ASSERT_NE(nullptr, engine);
787
788 UnwindPlan::RowSP row_sp;
789 AddressRange sample_range;
790 UnwindPlan unwind_plan(eRegisterKindLLDB);
791 UnwindPlan::Row::RegisterLocation regloc;
792
793 // The called_from_nodebug() from TestStepNoDebug.py
794 // Most of the previous unit tests have $sp being set as
795 // $fp plus an offset, and the unwinder recognizes that
796 // as a CFA change. This codegen overwrites $fp and we
797 // need to know that CFA is now in terms of $sp.
798 uint8_t data[] = {
799 // prologue
800 0xff, 0x83, 0x00, 0xd1, // 0: 0xd10083ff sub sp, sp, #0x20
801 0xfd, 0x7b, 0x01, 0xa9, // 4: 0xa9017bfd stp x29, x30, [sp, #0x10]
802 0xfd, 0x43, 0x00, 0x91, // 8: 0x910043fd add x29, sp, #0x10
803
804 // epilogue
805 0xfd, 0x7b, 0x41, 0xa9, // 12: 0xa9417bfd ldp x29, x30, [sp, #0x10]
806 0xff, 0x83, 0x00, 0x91, // 16: 0x910083ff add sp, sp, #0x20
807 0xc0, 0x03, 0x5f, 0xd6, // 20: 0xd65f03c0 ret
808 };
809
810 // UnwindPlan we expect:
811 // row[0]: 0: CFA=sp +0 =>
812 // row[1]: 4: CFA=sp+32 =>
813 // row[2]: 8: CFA=sp+32 => fp=[CFA-16] lr=[CFA-8]
814 // row[3]: 12: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]
815 // row[4]: 16: CFA=sp+32 => x0= <same> fp= <same> lr= <same>
816 // row[5]: 20: CFA=sp +0 => x0= <same> fp= <same> lr= <same>
817
818 // The specific issue we're testing for is after the
819 // ldp x29, x30, [sp, #0x10]
820 // when $fp and $lr have been restored to the original values,
821 // the CFA is now set in terms of the stack pointer. If it is
822 // left as being in terms of the frame pointer, $fp now has the
823 // caller function's $fp value and our StackID will be wrong etc.
824
825 sample_range = AddressRange(0x1000, sizeof(data));
826
827 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(
828 sample_range, data, sizeof(data), unwind_plan));
829
830 // Confirm CFA before epilogue instructions is in terms of $fp
831 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 12);
832 EXPECT_EQ(12ull, row_sp->GetOffset());
833 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
834 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
835
836 // Confirm that after restoring $fp to caller's value, CFA is now in
837 // terms of $sp
838 row_sp = unwind_plan.GetRowForFunctionOffset(offset: 16);
839 EXPECT_EQ(16ull, row_sp->GetOffset());
840 EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
841 EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true);
842}
843

source code of lldb/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp