1 | //===-- TestArm64InstEmulation.cpp ----------------------------------------===// |
2 | |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #include "gtest/gtest.h" |
11 | |
12 | #include <vector> |
13 | |
14 | #include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" |
15 | |
16 | #include "lldb/Core/Address.h" |
17 | #include "lldb/Core/AddressRange.h" |
18 | #include "lldb/Symbol/UnwindPlan.h" |
19 | #include "lldb/Target/UnwindAssembly.h" |
20 | #include "lldb/Utility/ArchSpec.h" |
21 | |
22 | #include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h" |
23 | #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" |
24 | #include "Plugins/Process/Utility/lldb-arm64-register-enums.h" |
25 | #include "llvm/Support/TargetSelect.h" |
26 | |
27 | using namespace lldb; |
28 | using namespace lldb_private; |
29 | |
30 | class TestArm64InstEmulation : public testing::Test { |
31 | public: |
32 | static void SetUpTestCase(); |
33 | static void TearDownTestCase(); |
34 | |
35 | // virtual void SetUp() override { } |
36 | // virtual void TearDown() override { } |
37 | |
38 | protected: |
39 | }; |
40 | |
41 | void TestArm64InstEmulation::SetUpTestCase() { |
42 | llvm::InitializeAllTargets(); |
43 | llvm::InitializeAllAsmPrinters(); |
44 | llvm::InitializeAllTargetMCs(); |
45 | llvm::InitializeAllDisassemblers(); |
46 | DisassemblerLLVMC::Initialize(); |
47 | EmulateInstructionARM64::Initialize(); |
48 | } |
49 | |
50 | void TestArm64InstEmulation::TearDownTestCase() { |
51 | DisassemblerLLVMC::Terminate(); |
52 | EmulateInstructionARM64::Terminate(); |
53 | } |
54 | |
55 | TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) { |
56 | ArchSpec arch("arm64-apple-ios10" ); |
57 | std::unique_ptr<UnwindAssemblyInstEmulation> engine( |
58 | static_cast<UnwindAssemblyInstEmulation *>( |
59 | UnwindAssemblyInstEmulation::CreateInstance(arch))); |
60 | ASSERT_NE(nullptr, engine); |
61 | |
62 | UnwindPlan::RowSP row_sp; |
63 | AddressRange sample_range; |
64 | UnwindPlan unwind_plan(eRegisterKindLLDB); |
65 | UnwindPlan::Row::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 | |
151 | TEST_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 | |
313 | TEST_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 | |
408 | TEST_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 | |
510 | TEST_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 | |
682 | TEST_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 | |
781 | TEST_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 | |