1 | //===-- TestArm64InstEmulation.cpp ----------------------------------------===// |
2 | |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #include "gtest/gtest.h" |
11 | |
12 | #include <vector> |
13 | |
14 | #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" |
15 | |
16 | #include "lldb/Core/Address.h" |
17 | #include "lldb/Core/AddressRange.h" |
18 | #include "lldb/Symbol/UnwindPlan.h" |
19 | #include "lldb/Target/UnwindAssembly.h" |
20 | #include "lldb/Utility/ArchSpec.h" |
21 | |
22 | #include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h" |
23 | #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" |
24 | #include "Plugins/Process/Utility/lldb-arm64-register-enums.h" |
25 | #include "llvm/Support/TargetSelect.h" |
26 | |
27 | using namespace lldb; |
28 | using namespace lldb_private; |
29 | |
30 | class TestArm64InstEmulation : public testing::Test { |
31 | public: |
32 | static void SetUpTestCase(); |
33 | static void TearDownTestCase(); |
34 | |
35 | // virtual void SetUp() override { } |
36 | // virtual void TearDown() override { } |
37 | |
38 | protected: |
39 | }; |
40 | |
41 | void TestArm64InstEmulation::SetUpTestCase() { |
42 | llvm::InitializeAllTargets(); |
43 | llvm::InitializeAllAsmPrinters(); |
44 | llvm::InitializeAllTargetMCs(); |
45 | llvm::InitializeAllDisassemblers(); |
46 | DisassemblerLLVMC::Initialize(); |
47 | EmulateInstructionARM64::Initialize(); |
48 | } |
49 | |
50 | void TestArm64InstEmulation::TearDownTestCase() { |
51 | DisassemblerLLVMC::Terminate(); |
52 | EmulateInstructionARM64::Terminate(); |
53 | } |
54 | |
55 | TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) { |
56 | ArchSpec arch("arm64-apple-ios10" ); |
57 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
58 | static_cast<UnwindAssemblyInstEmulation *>( |
59 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
60 | ASSERT_NE(nullptr, engine); |
61 | |
62 | 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 | |
163 | TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) { |
164 | ArchSpec arch("arm64-apple-ios10" ); |
165 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
166 | static_cast<UnwindAssemblyInstEmulation *>( |
167 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
168 | ASSERT_NE(nullptr, engine); |
169 | |
170 | 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 | |
325 | TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) { |
326 | ArchSpec arch("arm64-apple-ios10" ); |
327 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
328 | static_cast<UnwindAssemblyInstEmulation *>( |
329 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
330 | ASSERT_NE(nullptr, engine); |
331 | |
332 | 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 | |
424 | TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) { |
425 | ArchSpec arch("arm64-apple-ios10" ); |
426 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
427 | static_cast<UnwindAssemblyInstEmulation *>( |
428 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
429 | ASSERT_NE(nullptr, engine); |
430 | |
431 | 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 | |
526 | TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) { |
527 | ArchSpec arch("arm64-apple-ios10" ); |
528 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
529 | static_cast<UnwindAssemblyInstEmulation *>( |
530 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
531 | ASSERT_NE(nullptr, engine); |
532 | |
533 | 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 | |
698 | TEST_F(TestArm64InstEmulation, TestCFARegisterTrackedAcrossJumps) { |
699 | ArchSpec arch("arm64-apple-ios10" ); |
700 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
701 | static_cast<UnwindAssemblyInstEmulation *>( |
702 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
703 | ASSERT_NE(nullptr, engine); |
704 | |
705 | 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 | |
797 | TEST_F(TestArm64InstEmulation, TestCFAResetToSP) { |
798 | ArchSpec arch("arm64-apple-ios15" ); |
799 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
800 | static_cast<UnwindAssemblyInstEmulation *>( |
801 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
802 | ASSERT_NE(nullptr, engine); |
803 | |
804 | 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 | |