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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(fpu_d8_arm64, regloc)) { 651 EXPECT_TRUE(regloc.IsSame()); 652 } 653 if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) { 654 EXPECT_TRUE(regloc.IsSame()); 655 } 656 if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) { 657 EXPECT_TRUE(regloc.IsSame()); 658 } 659 if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) { 660 EXPECT_TRUE(regloc.IsSame()); 661 } 662 if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) { 663 EXPECT_TRUE(regloc.IsSame()); 664 } 665 if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) { 666 EXPECT_TRUE(regloc.IsSame()); 667 } 668 if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) { 669 EXPECT_TRUE(regloc.IsSame()); 670 } 671 if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) { 672 EXPECT_TRUE(regloc.IsSame()); 673 } 674 if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) { 675 EXPECT_TRUE(regloc.IsSame()); 676 } 677 if (row_sp->GetRegisterInfo(gpr_x28_arm64, 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(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(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(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(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