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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 0);
93 EXPECT_EQ(0, row->GetOffset());
94 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
95 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
96 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
97
98 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
99 EXPECT_TRUE(regloc.IsSame());
100
101 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
102 EXPECT_TRUE(regloc.IsSame());
103
104 // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]
105 row = unwind_plan.GetRowForFunctionOffset(offset: 4);
106 EXPECT_EQ(4, row->GetOffset());
107 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
108 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
109 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
110
111 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
112 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
113 EXPECT_EQ(-16, regloc.GetOffset());
114
115 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 8);
121 EXPECT_EQ(8, row->GetOffset());
122 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
123 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
124 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
125
126 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
127 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
128 EXPECT_EQ(-16, regloc.GetOffset());
129
130 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 16);
136 EXPECT_EQ(16, row->GetOffset());
137 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
138 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
139 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
140
141 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
142 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
143 EXPECT_EQ(-16, regloc.GetOffset());
144
145 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 20);
151 EXPECT_EQ(20, row->GetOffset());
152 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
153 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
154 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
155
156 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
157 EXPECT_TRUE(regloc.IsSame());
158
159 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
160 EXPECT_TRUE(regloc.IsSame());
161}
162
163TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 0);
222 EXPECT_EQ(0, row->GetOffset());
223 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
224 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
225 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
226
227 // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]
228 row = unwind_plan.GetRowForFunctionOffset(offset: 4);
229 EXPECT_EQ(4, row->GetOffset());
230 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
231 EXPECT_EQ(48, row->GetCFAValue().GetOffset());
232
233 EXPECT_TRUE(row->GetRegisterInfo(gpr_x21_arm64, regloc));
234 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
235 EXPECT_EQ(-40, regloc.GetOffset());
236
237 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 8);
243 EXPECT_EQ(8, row->GetOffset());
244 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
245 EXPECT_EQ(48, row->GetCFAValue().GetOffset());
246
247 EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
248 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
249 EXPECT_EQ(-24, regloc.GetOffset());
250
251 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 12);
258 EXPECT_EQ(12, row->GetOffset());
259 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
260 EXPECT_EQ(48, row->GetCFAValue().GetOffset());
261
262 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
263 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
264 EXPECT_EQ(-16, regloc.GetOffset());
265
266 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 16);
273 EXPECT_EQ(16, row->GetOffset());
274 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
275 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
276 EXPECT_EQ(16, row->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 = unwind_plan.GetRowForFunctionOffset(offset: 28);
281 EXPECT_EQ(28, row->GetOffset());
282 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
283 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
284 EXPECT_EQ(48, row->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 = unwind_plan.GetRowForFunctionOffset(offset: 32);
289 EXPECT_EQ(32, row->GetOffset());
290
291 // I'd prefer if these restored registers were cleared entirely instead of set
292 // to IsSame...
293 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
294 EXPECT_TRUE(regloc.IsSame());
295
296 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 36);
302 EXPECT_EQ(36, row->GetOffset());
303
304 EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
305 EXPECT_TRUE(regloc.IsSame());
306
307 EXPECT_TRUE(row->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 = unwind_plan.GetRowForFunctionOffset(offset: 40);
313 EXPECT_EQ(40, row->GetOffset());
314 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
315 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
316 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
317
318 EXPECT_TRUE(row->GetRegisterInfo(gpr_x21_arm64, regloc));
319 EXPECT_TRUE(regloc.IsSame());
320
321 EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, regloc));
322 EXPECT_TRUE(regloc.IsSame());
323}
324
325TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 0);
376 EXPECT_EQ(0, row->GetOffset());
377 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
378 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
379 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
380
381 row = unwind_plan.GetRowForFunctionOffset(offset: 32);
382 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
383 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
384 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
385
386 EXPECT_FALSE(row->GetRegisterInfo(gpr_x19_arm64, regloc));
387 EXPECT_FALSE(row->GetRegisterInfo(gpr_x20_arm64, regloc));
388 EXPECT_FALSE(row->GetRegisterInfo(gpr_x21_arm64, regloc));
389 EXPECT_FALSE(row->GetRegisterInfo(gpr_x22_arm64, regloc));
390 EXPECT_FALSE(row->GetRegisterInfo(gpr_x23_arm64, regloc));
391 EXPECT_FALSE(row->GetRegisterInfo(gpr_x24_arm64, regloc));
392 EXPECT_FALSE(row->GetRegisterInfo(gpr_x25_arm64, regloc));
393 EXPECT_FALSE(row->GetRegisterInfo(gpr_x26_arm64, regloc));
394 EXPECT_FALSE(row->GetRegisterInfo(gpr_x27_arm64, regloc));
395 EXPECT_FALSE(row->GetRegisterInfo(gpr_x28_arm64, regloc));
396
397 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));
398 EXPECT_TRUE(regloc.IsSame());
399
400 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));
401 EXPECT_TRUE(regloc.IsSame());
402
403 row = unwind_plan.GetRowForFunctionOffset(offset: 36);
404 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
405 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
406 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
407
408 row = unwind_plan.GetRowForFunctionOffset(offset: 52);
409 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
410 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
411 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
412
413 row = unwind_plan.GetRowForFunctionOffset(offset: 56);
414 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
415 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
416 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
417
418 row = unwind_plan.GetRowForFunctionOffset(offset: 60);
419 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
420 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
421 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
422}
423
424TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 36);
506 EXPECT_EQ(28, row->GetOffset());
507 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
508 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
509 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
510
511 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));
512 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
513 EXPECT_EQ(-32, regloc.GetOffset());
514
515 row = unwind_plan.GetRowForFunctionOffset(offset: 40);
516 EXPECT_EQ(28, row->GetOffset());
517 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
518 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
519 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
520
521 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));
522 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
523 EXPECT_EQ(-32, regloc.GetOffset());
524}
525
526TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 28);
622 EXPECT_EQ(28, row->GetOffset());
623 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
624 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
625 EXPECT_EQ(16, row->GetCFAValue().GetOffset());
626
627 EXPECT_TRUE(row->GetRegisterInfo(fpu_d15_arm64, regloc));
628 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
629 EXPECT_EQ(-96, regloc.GetOffset());
630
631 EXPECT_TRUE(row->GetRegisterInfo(fpu_d14_arm64, regloc));
632 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
633 EXPECT_EQ(-88, regloc.GetOffset());
634
635 EXPECT_TRUE(row->GetRegisterInfo(fpu_d13_arm64, regloc));
636 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
637 EXPECT_EQ(-80, regloc.GetOffset());
638
639 EXPECT_TRUE(row->GetRegisterInfo(fpu_d12_arm64, regloc));
640 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
641 EXPECT_EQ(-72, regloc.GetOffset());
642
643 EXPECT_TRUE(row->GetRegisterInfo(fpu_d11_arm64, regloc));
644 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
645 EXPECT_EQ(-64, regloc.GetOffset());
646
647 EXPECT_TRUE(row->GetRegisterInfo(fpu_d10_arm64, regloc));
648 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
649 EXPECT_EQ(-56, regloc.GetOffset());
650
651 EXPECT_TRUE(row->GetRegisterInfo(fpu_d9_arm64, regloc));
652 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
653 EXPECT_EQ(-48, regloc.GetOffset());
654
655 EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));
656 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());
657 EXPECT_EQ(-40, regloc.GetOffset());
658
659 // 60: CFA=sp +0 =>
660 row = unwind_plan.GetRowForFunctionOffset(offset: 60);
661 EXPECT_EQ(60, row->GetOffset());
662 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
663 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
664 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
665
666 if (row->GetRegisterInfo(reg_num: fpu_d8_arm64, register_location&: regloc)) {
667 EXPECT_TRUE(regloc.IsSame());
668 }
669 if (row->GetRegisterInfo(reg_num: fpu_d9_arm64, register_location&: regloc)) {
670 EXPECT_TRUE(regloc.IsSame());
671 }
672 if (row->GetRegisterInfo(reg_num: fpu_d10_arm64, register_location&: regloc)) {
673 EXPECT_TRUE(regloc.IsSame());
674 }
675 if (row->GetRegisterInfo(reg_num: fpu_d11_arm64, register_location&: regloc)) {
676 EXPECT_TRUE(regloc.IsSame());
677 }
678 if (row->GetRegisterInfo(reg_num: fpu_d12_arm64, register_location&: regloc)) {
679 EXPECT_TRUE(regloc.IsSame());
680 }
681 if (row->GetRegisterInfo(reg_num: fpu_d13_arm64, register_location&: regloc)) {
682 EXPECT_TRUE(regloc.IsSame());
683 }
684 if (row->GetRegisterInfo(reg_num: fpu_d14_arm64, register_location&: regloc)) {
685 EXPECT_TRUE(regloc.IsSame());
686 }
687 if (row->GetRegisterInfo(reg_num: fpu_d15_arm64, register_location&: regloc)) {
688 EXPECT_TRUE(regloc.IsSame());
689 }
690 if (row->GetRegisterInfo(reg_num: gpr_x27_arm64, register_location&: regloc)) {
691 EXPECT_TRUE(regloc.IsSame());
692 }
693 if (row->GetRegisterInfo(reg_num: gpr_x28_arm64, register_location&: regloc)) {
694 EXPECT_TRUE(regloc.IsSame());
695 }
696}
697
698TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 40);
768 EXPECT_EQ(40, row->GetOffset());
769 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
770 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
771 EXPECT_EQ(0, row->GetCFAValue().GetOffset());
772
773 // After the 'ret', confirm we're back to the correct CFA of $fp+16
774 row = unwind_plan.GetRowForFunctionOffset(offset: 44);
775 EXPECT_EQ(44, row->GetOffset());
776 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
777 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
778 EXPECT_EQ(16, row->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 = unwind_plan.GetRowForFunctionOffset(offset: 60);
783 EXPECT_EQ(44, row->GetOffset());
784 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
785 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
786 EXPECT_EQ(16, row->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 = unwind_plan.GetRowForFunctionOffset(offset: 64);
791 EXPECT_EQ(64, row->GetOffset());
792 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
793 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
794 EXPECT_EQ(32, row->GetCFAValue().GetOffset());
795}
796
797TEST_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 const UnwindPlan::Row *row;
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 = unwind_plan.GetRowForFunctionOffset(offset: 12);
848 EXPECT_EQ(12, row->GetOffset());
849 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);
850 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
851
852 // Confirm that after restoring $fp to caller's value, CFA is now in
853 // terms of $sp
854 row = unwind_plan.GetRowForFunctionOffset(offset: 16);
855 EXPECT_EQ(16, row->GetOffset());
856 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);
857 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);
858}
859

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