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