1074 lines · cpp
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-exception7//8//===----------------------------------------------------------------------===//9 10#include "gtest/gtest.h"11 12#include <vector>13 14#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h"15 16#include "lldb/Core/Address.h"17#include "lldb/Core/AddressRange.h"18#include "lldb/Symbol/UnwindPlan.h"19#include "lldb/Target/UnwindAssembly.h"20#include "lldb/Utility/ArchSpec.h"21 22#include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h"23#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"24#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"25#include "llvm/Support/TargetSelect.h"26 27using namespace lldb;28using namespace lldb_private;29 30class TestArm64InstEmulation : public testing::Test {31public:32 static void SetUpTestCase();33 static void TearDownTestCase();34 35 // virtual void SetUp() override { }36 // virtual void TearDown() override { }37 38protected:39};40 41void TestArm64InstEmulation::SetUpTestCase() {42 llvm::InitializeAllTargets();43 llvm::InitializeAllAsmPrinters();44 llvm::InitializeAllTargetMCs();45 llvm::InitializeAllDisassemblers();46 DisassemblerLLVMC::Initialize();47 EmulateInstructionARM64::Initialize();48}49 50void TestArm64InstEmulation::TearDownTestCase() {51 DisassemblerLLVMC::Terminate();52 EmulateInstructionARM64::Terminate();53}54 55TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) {56 ArchSpec arch("arm64-apple-ios10");57 std::unique_ptr<UnwindAssemblyInstEmulation> engine(58 static_cast<UnwindAssemblyInstEmulation *>(59 UnwindAssemblyInstEmulation::CreateInstance(arch)));60 ASSERT_NE(nullptr, engine);61 62 const UnwindPlan::Row *row;63 AddressRange sample_range;64 UnwindPlan unwind_plan(eRegisterKindLLDB);65 UnwindPlan::Row::AbstractRegisterLocation regloc;66 67 // 'int main() { }' compiled for arm64-apple-ios with clang68 uint8_t data[] = {69 0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]!70 0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp71 0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x1072 73 0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x2974 0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #1675 0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret76 };77 78 // UnwindPlan we expect:79 80 // row[0]: 0: CFA=sp +0 => fp= <same> lr= <same>81 // row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]82 // row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]83 // row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]84 // row[3]: 20: CFA=sp +0 => fp= <same> lr= <same>85 86 sample_range = AddressRange(0x1000, sizeof(data));87 88 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(89 sample_range, data, sizeof(data), unwind_plan));90 91 // CFA=sp +0 => fp= <same> lr= <same>92 row = unwind_plan.GetRowForFunctionOffset(0);93 EXPECT_EQ(0, row->GetOffset());94 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);95 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);96 EXPECT_EQ(0, row->GetCFAValue().GetOffset());97 98 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));99 EXPECT_TRUE(regloc.IsSame());100 101 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));102 EXPECT_TRUE(regloc.IsSame());103 104 // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]105 row = unwind_plan.GetRowForFunctionOffset(4);106 EXPECT_EQ(4, row->GetOffset());107 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);108 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);109 EXPECT_EQ(16, row->GetCFAValue().GetOffset());110 111 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));112 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());113 EXPECT_EQ(-16, regloc.GetOffset());114 115 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));116 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());117 EXPECT_EQ(-8, regloc.GetOffset());118 119 // CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]120 row = unwind_plan.GetRowForFunctionOffset(8);121 EXPECT_EQ(8, row->GetOffset());122 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);123 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);124 EXPECT_EQ(16, row->GetCFAValue().GetOffset());125 126 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));127 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());128 EXPECT_EQ(-16, regloc.GetOffset());129 130 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));131 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());132 EXPECT_EQ(-8, regloc.GetOffset());133 134 // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]135 row = unwind_plan.GetRowForFunctionOffset(16);136 EXPECT_EQ(16, row->GetOffset());137 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);138 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);139 EXPECT_EQ(16, row->GetCFAValue().GetOffset());140 141 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));142 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());143 EXPECT_EQ(-16, regloc.GetOffset());144 145 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));146 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());147 EXPECT_EQ(-8, regloc.GetOffset());148 149 // CFA=sp +0 => fp= <same> lr= <same>150 row = unwind_plan.GetRowForFunctionOffset(20);151 EXPECT_EQ(20, row->GetOffset());152 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);153 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);154 EXPECT_EQ(0, row->GetCFAValue().GetOffset());155 156 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));157 EXPECT_TRUE(regloc.IsSame());158 159 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));160 EXPECT_TRUE(regloc.IsSame());161}162 163TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) {164 ArchSpec arch("arm64-apple-ios10");165 std::unique_ptr<UnwindAssemblyInstEmulation> engine(166 static_cast<UnwindAssemblyInstEmulation *>(167 UnwindAssemblyInstEmulation::CreateInstance(arch)));168 ASSERT_NE(nullptr, engine);169 170 const UnwindPlan::Row *row;171 AddressRange sample_range;172 UnwindPlan unwind_plan(eRegisterKindLLDB);173 UnwindPlan::Row::AbstractRegisterLocation regloc;174 175 // disassembly of -[NSPlaceholderString initWithBytes:length:encoding:]176 // from Foundation for iOS.177 uint8_t data[] = {178 0xf6, 0x57, 0xbd, 0xa9, // 0: 0xa9bd57f6 stp x22, x21, [sp, #-48]!179 0xf4, 0x4f, 0x01, 0xa9, // 4: 0xa9014ff4 stp x20, x19, [sp, #16]180 0xfd, 0x7b, 0x02, 0xa9, // 8: 0xa9027bfd stp x29, x30, [sp, #32]181 0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32182 0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16183 184 // [... function body ...]185 0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop186 187 0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32188 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], #48191 0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b 0x18f640524 ; symbol stub192 // for: CFStringCreateWithBytes193 };194 195 // UnwindPlan we expect:196 // 0: CFA=sp +0 =>197 // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]198 // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]199 // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]200 // fp=[CFA-16] lr=[CFA-8]201 // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]202 // fp=[CFA-16] lr=[CFA-8]203 204 // [... function body ...]205 206 // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]207 // fp=[CFA-16] lr=[CFA-8]208 // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=209 // <same> lr= <same>210 // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=211 // <same> lr= <same>212 // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>213 // lr= <same>214 215 sample_range = AddressRange(0x1000, sizeof(data));216 217 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(218 sample_range, data, sizeof(data), unwind_plan));219 220 // 0: CFA=sp +0 =>221 row = unwind_plan.GetRowForFunctionOffset(0);222 EXPECT_EQ(0, row->GetOffset());223 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);224 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);225 EXPECT_EQ(0, row->GetCFAValue().GetOffset());226 227 // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48]228 row = unwind_plan.GetRowForFunctionOffset(4);229 EXPECT_EQ(4, row->GetOffset());230 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);231 EXPECT_EQ(48, row->GetCFAValue().GetOffset());232 233 EXPECT_TRUE(row->GetRegisterInfo(gpr_x21_arm64, regloc));234 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());235 EXPECT_EQ(-40, regloc.GetOffset());236 237 EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, regloc));238 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());239 EXPECT_EQ(-48, regloc.GetOffset());240 241 // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]242 row = unwind_plan.GetRowForFunctionOffset(8);243 EXPECT_EQ(8, row->GetOffset());244 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);245 EXPECT_EQ(48, row->GetCFAValue().GetOffset());246 247 EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));248 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());249 EXPECT_EQ(-24, regloc.GetOffset());250 251 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));252 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());253 EXPECT_EQ(-32, regloc.GetOffset());254 255 // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]256 // fp=[CFA-16] lr=[CFA-8]257 row = unwind_plan.GetRowForFunctionOffset(12);258 EXPECT_EQ(12, row->GetOffset());259 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);260 EXPECT_EQ(48, row->GetCFAValue().GetOffset());261 262 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));263 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());264 EXPECT_EQ(-16, regloc.GetOffset());265 266 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));267 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());268 EXPECT_EQ(-8, regloc.GetOffset());269 270 // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]271 // fp=[CFA-16] lr=[CFA-8]272 row = unwind_plan.GetRowForFunctionOffset(16);273 EXPECT_EQ(16, row->GetOffset());274 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);275 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);276 EXPECT_EQ(16, row->GetCFAValue().GetOffset());277 278 // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]279 // fp=[CFA-16] lr=[CFA-8]280 row = unwind_plan.GetRowForFunctionOffset(28);281 EXPECT_EQ(28, row->GetOffset());282 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);283 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);284 EXPECT_EQ(48, row->GetCFAValue().GetOffset());285 286 // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp=287 // <same> lr= <same>288 row = unwind_plan.GetRowForFunctionOffset(32);289 EXPECT_EQ(32, row->GetOffset());290 291 // I'd prefer if these restored registers were cleared entirely instead of set292 // to IsSame...293 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));294 EXPECT_TRUE(regloc.IsSame());295 296 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));297 EXPECT_TRUE(regloc.IsSame());298 299 // 36: CFA=sp+48 => x19= <same> x20= <same> x21=[CFA-40] x22=[CFA-48] fp=300 // <same> lr= <same>301 row = unwind_plan.GetRowForFunctionOffset(36);302 EXPECT_EQ(36, row->GetOffset());303 304 EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));305 EXPECT_TRUE(regloc.IsSame());306 307 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));308 EXPECT_TRUE(regloc.IsSame());309 310 // 40: CFA=sp +0 => x19= <same> x20= <same> x21= <same> x22= <same> fp= <same>311 // lr= <same>312 row = unwind_plan.GetRowForFunctionOffset(40);313 EXPECT_EQ(40, row->GetOffset());314 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);315 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);316 EXPECT_EQ(0, row->GetCFAValue().GetOffset());317 318 EXPECT_TRUE(row->GetRegisterInfo(gpr_x21_arm64, regloc));319 EXPECT_TRUE(regloc.IsSame());320 321 EXPECT_TRUE(row->GetRegisterInfo(gpr_x22_arm64, regloc));322 EXPECT_TRUE(regloc.IsSame());323}324 325TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) {326 ArchSpec arch("arm64-apple-ios10");327 std::unique_ptr<UnwindAssemblyInstEmulation> engine(328 static_cast<UnwindAssemblyInstEmulation *>(329 UnwindAssemblyInstEmulation::CreateInstance(arch)));330 ASSERT_NE(nullptr, engine);331 332 const UnwindPlan::Row *row;333 AddressRange sample_range;334 UnwindPlan unwind_plan(eRegisterKindLLDB);335 UnwindPlan::Row::AbstractRegisterLocation regloc;336 337 // disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u>338 // from JavaScriptcore for iOS.339 uint8_t data[] = {340 0x08, 0x3c, 0x0f, 0x53, // 0: 0x530f3c08 ubfx w8, w0, #15, #1341 0x68, 0x00, 0x00, 0x39, // 4: 0x39000068 strb w8, [x3]342 0x08, 0x3c, 0x40, 0xd2, // 8: 0xd2403c08 eor x8, x0, #0xffff343 0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst x0, #0x8000344 345 // [...]346 347 0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp x9, x12348 0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34349 0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str wzr, [x2]350 0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr w0, wzr, #0x1351 0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret352 0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor x9, x12, x9353 354 // [...]355 356 0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add w8, w8, #0x1357 0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str w8, [x2]358 0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr w0, wzr, #0x1359 0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret360 0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov w0, #0x0361 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret362 363 };364 365 // UnwindPlan we expect:366 // 0: CFA=sp +0 =>367 // (possibly with additional rows at offsets 36 and 56 saying the same thing)368 369 sample_range = AddressRange(0x1000, sizeof(data));370 371 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(372 sample_range, data, sizeof(data), unwind_plan));373 374 // 0: CFA=sp +0 =>375 row = unwind_plan.GetRowForFunctionOffset(0);376 EXPECT_EQ(0, row->GetOffset());377 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);378 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);379 EXPECT_EQ(0, row->GetCFAValue().GetOffset());380 381 row = unwind_plan.GetRowForFunctionOffset(32);382 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);383 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);384 EXPECT_EQ(0, row->GetCFAValue().GetOffset());385 386 EXPECT_FALSE(row->GetRegisterInfo(gpr_x19_arm64, regloc));387 EXPECT_FALSE(row->GetRegisterInfo(gpr_x20_arm64, regloc));388 EXPECT_FALSE(row->GetRegisterInfo(gpr_x21_arm64, regloc));389 EXPECT_FALSE(row->GetRegisterInfo(gpr_x22_arm64, regloc));390 EXPECT_FALSE(row->GetRegisterInfo(gpr_x23_arm64, regloc));391 EXPECT_FALSE(row->GetRegisterInfo(gpr_x24_arm64, regloc));392 EXPECT_FALSE(row->GetRegisterInfo(gpr_x25_arm64, regloc));393 EXPECT_FALSE(row->GetRegisterInfo(gpr_x26_arm64, regloc));394 EXPECT_FALSE(row->GetRegisterInfo(gpr_x27_arm64, regloc));395 EXPECT_FALSE(row->GetRegisterInfo(gpr_x28_arm64, regloc));396 397 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));398 EXPECT_TRUE(regloc.IsSame());399 400 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));401 EXPECT_TRUE(regloc.IsSame());402 403 row = unwind_plan.GetRowForFunctionOffset(36);404 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);405 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);406 EXPECT_EQ(0, row->GetCFAValue().GetOffset());407 408 row = unwind_plan.GetRowForFunctionOffset(52);409 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);410 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);411 EXPECT_EQ(0, row->GetCFAValue().GetOffset());412 413 row = unwind_plan.GetRowForFunctionOffset(56);414 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);415 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);416 EXPECT_EQ(0, row->GetCFAValue().GetOffset());417 418 row = unwind_plan.GetRowForFunctionOffset(60);419 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);420 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);421 EXPECT_EQ(0, row->GetCFAValue().GetOffset());422}423 424TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) {425 ArchSpec arch("arm64-apple-ios10");426 std::unique_ptr<UnwindAssemblyInstEmulation> engine(427 static_cast<UnwindAssemblyInstEmulation *>(428 UnwindAssemblyInstEmulation::CreateInstance(arch)));429 ASSERT_NE(nullptr, engine);430 431 const UnwindPlan::Row *row;432 AddressRange sample_range;433 UnwindPlan unwind_plan(eRegisterKindLLDB);434 UnwindPlan::Row::AbstractRegisterLocation regloc;435 436 // disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS.437 uint8_t data[] = {438 439 0xfc, 0x6f, 0xba, 0xa9, // 0: 0xa9ba6ffc stp x28, x27, [sp, #-0x60]!440 0xfa, 0x67, 0x01, 0xa9, // 4: 0xa90167fa stp x26, x25, [sp, #0x10]441 0xf8, 0x5f, 0x02, 0xa9, // 8: 0xa9025ff8 stp x24, x23, [sp, #0x20]442 0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp x22, x21, [sp, #0x30]443 0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp x20, x19, [sp, #0x40]444 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50]445 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50446 0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub sp, sp, #0x30447 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 not450 // 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 from454 // their original locations. unwind plan should ignore this.455 0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp x20, x19, [sp, #0x10]456 457 // epilogue458 0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub sp, x29, #0x50459 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], #0x60465 0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret466 };467 468 // UnwindPlan we expect:469 // 0: CFA=sp +0 =>470 // 4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96]471 // 8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]472 // 12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]473 // x27=[CFA-88] x28=[CFA-96]474 // 16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]475 // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]476 // 20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]477 // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]478 // x28=[CFA-96]479 // 24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]480 // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]481 // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]482 // 28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]483 // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]484 // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]485 486 // 44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]487 // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]488 // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8]489 // 48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48]490 // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88]491 // x28=[CFA-96]492 // 52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64]493 // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]494 // 56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80]495 // x27=[CFA-88] x28=[CFA-96]496 // 60: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96]497 // 64: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96]498 // 68: CFA=sp +0 =>499 500 sample_range = AddressRange(0x1000, sizeof(data));501 502 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(503 sample_range, data, sizeof(data), unwind_plan));504 505 row = unwind_plan.GetRowForFunctionOffset(36);506 EXPECT_EQ(28, row->GetOffset());507 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);508 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);509 EXPECT_EQ(16, row->GetCFAValue().GetOffset());510 511 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));512 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());513 EXPECT_EQ(-32, regloc.GetOffset());514 515 row = unwind_plan.GetRowForFunctionOffset(40);516 EXPECT_EQ(28, row->GetOffset());517 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);518 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);519 EXPECT_EQ(16, row->GetCFAValue().GetOffset());520 521 EXPECT_TRUE(row->GetRegisterInfo(gpr_x20_arm64, regloc));522 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());523 EXPECT_EQ(-32, regloc.GetOffset());524}525 526TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) {527 ArchSpec arch("arm64-apple-ios10");528 std::unique_ptr<UnwindAssemblyInstEmulation> engine(529 static_cast<UnwindAssemblyInstEmulation *>(530 UnwindAssemblyInstEmulation::CreateInstance(arch)));531 ASSERT_NE(nullptr, engine);532 533 const UnwindPlan::Row *row;534 AddressRange sample_range;535 UnwindPlan unwind_plan(eRegisterKindLLDB);536 UnwindPlan::Row::AbstractRegisterLocation regloc;537 538 // this file built with clang for iOS arch arm64 optimization -Os539 // #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-saved559 // floating point registers.560 uint8_t data[] = {561 // prologue562 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, #0x50569 0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff sub sp, sp, #0x110570 571 // epilogue572 0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf sub sp, x29, #0x50573 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], #0x60579 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret580 };581 582 // UnwindPlan we expect:583 // 0: CFA=sp +0 =>584 // 4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]585 // 8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]586 // 12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]587 // d14=[CFA-88] d15=[CFA-96]588 // 16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]589 // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]590 // 20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]591 // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]592 // d15=[CFA-96]593 // 24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]594 // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]595 // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]596 // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]597 // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]598 // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]599 // 36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]600 // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]601 // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]602 // 40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48]603 // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88]604 // d15=[CFA-96]605 // 44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64]606 // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]607 // 48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80]608 // d14=[CFA-88] d15=[CFA-96]609 // 52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]610 // 56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96]611 // 60: CFA=sp +0 =>612 613 sample_range = AddressRange(0x1000, sizeof(data));614 615 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(616 sample_range, data, sizeof(data), unwind_plan));617 618 // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8]619 // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72]620 // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96]621 row = unwind_plan.GetRowForFunctionOffset(28);622 EXPECT_EQ(28, row->GetOffset());623 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);624 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);625 EXPECT_EQ(16, row->GetCFAValue().GetOffset());626 627 EXPECT_TRUE(row->GetRegisterInfo(fpu_d15_arm64, regloc));628 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());629 EXPECT_EQ(-96, regloc.GetOffset());630 631 EXPECT_TRUE(row->GetRegisterInfo(fpu_d14_arm64, regloc));632 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());633 EXPECT_EQ(-88, regloc.GetOffset());634 635 EXPECT_TRUE(row->GetRegisterInfo(fpu_d13_arm64, regloc));636 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());637 EXPECT_EQ(-80, regloc.GetOffset());638 639 EXPECT_TRUE(row->GetRegisterInfo(fpu_d12_arm64, regloc));640 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());641 EXPECT_EQ(-72, regloc.GetOffset());642 643 EXPECT_TRUE(row->GetRegisterInfo(fpu_d11_arm64, regloc));644 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());645 EXPECT_EQ(-64, regloc.GetOffset());646 647 EXPECT_TRUE(row->GetRegisterInfo(fpu_d10_arm64, regloc));648 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());649 EXPECT_EQ(-56, regloc.GetOffset());650 651 EXPECT_TRUE(row->GetRegisterInfo(fpu_d9_arm64, regloc));652 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());653 EXPECT_EQ(-48, regloc.GetOffset());654 655 EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));656 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());657 EXPECT_EQ(-40, regloc.GetOffset());658 659 // 60: CFA=sp +0 =>660 row = unwind_plan.GetRowForFunctionOffset(60);661 EXPECT_EQ(60, row->GetOffset());662 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);663 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);664 EXPECT_EQ(0, row->GetCFAValue().GetOffset());665 666 if (row->GetRegisterInfo(fpu_d8_arm64, regloc)) {667 EXPECT_TRUE(regloc.IsSame());668 }669 if (row->GetRegisterInfo(fpu_d9_arm64, regloc)) {670 EXPECT_TRUE(regloc.IsSame());671 }672 if (row->GetRegisterInfo(fpu_d10_arm64, regloc)) {673 EXPECT_TRUE(regloc.IsSame());674 }675 if (row->GetRegisterInfo(fpu_d11_arm64, regloc)) {676 EXPECT_TRUE(regloc.IsSame());677 }678 if (row->GetRegisterInfo(fpu_d12_arm64, regloc)) {679 EXPECT_TRUE(regloc.IsSame());680 }681 if (row->GetRegisterInfo(fpu_d13_arm64, regloc)) {682 EXPECT_TRUE(regloc.IsSame());683 }684 if (row->GetRegisterInfo(fpu_d14_arm64, regloc)) {685 EXPECT_TRUE(regloc.IsSame());686 }687 if (row->GetRegisterInfo(fpu_d15_arm64, regloc)) {688 EXPECT_TRUE(regloc.IsSame());689 }690 if (row->GetRegisterInfo(gpr_x27_arm64, regloc)) {691 EXPECT_TRUE(regloc.IsSame());692 }693 if (row->GetRegisterInfo(gpr_x28_arm64, regloc)) {694 EXPECT_TRUE(regloc.IsSame());695 }696}697 698TEST_F(TestArm64InstEmulation, TestCFARegisterTrackedAcrossJumps) {699 ArchSpec arch("arm64-apple-ios10");700 std::unique_ptr<UnwindAssemblyInstEmulation> engine(701 static_cast<UnwindAssemblyInstEmulation *>(702 UnwindAssemblyInstEmulation::CreateInstance(arch)));703 ASSERT_NE(nullptr, engine);704 705 const UnwindPlan::Row *row;706 AddressRange sample_range;707 UnwindPlan unwind_plan(eRegisterKindLLDB);708 UnwindPlan::Row::AbstractRegisterLocation regloc;709 710 uint8_t data[] = {711 // prologue712 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, #0x10715 0xff, 0x43, 0x00, 0xd1, // 12: 0xd10043ff sub sp, sp, #0x10716 // conditional branch over a mid-function epilogue717 0xeb, 0x00, 0x00, 0x54, // 16: 0x540000eb b.lt <+44>718 // mid-function epilogue719 0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop720 0xe0, 0x03, 0x13, 0xaa, // 24: 0xaa1303e0 mov x0, x19721 0xbf, 0x43, 0x00, 0xd1, // 28: 0xd10043bf sub sp, x29, #0x10722 0xfd, 0x7b, 0x41, 0xa9, // 32: 0xa9417bfd ldp x29, x30, [sp, #0x10]723 0xf4, 0x4f, 0xc2, 0xa8, // 36: 0xa8c24ff4 ldp x20, x19, [sp], #0x20724 0xc0, 0x03, 0x5f, 0xd6, // 40: 0xd65f03c0 ret725 // unwind state restored, we're using a frame pointer, let's change the726 // stack pointer and see no change in how the CFA is computed727 0x1f, 0x20, 0x03, 0xd5, // 44: 0xd503201f nop728 0xff, 0x43, 0x00, 0xd1, // 48: 0xd10043ff sub sp, sp, #0x10729 0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f nop730 // final epilogue731 0xe0, 0x03, 0x13, 0xaa, // 56: 0xaa1303e0 mov x0, x19732 0xbf, 0x43, 0x00, 0xd1, // 60: 0xd10043bf sub sp, x29, #0x10733 0xfd, 0x7b, 0x41, 0xa9, // 64: 0xa9417bfd ldp x29, x30, [sp, #0x10]734 0xf4, 0x4f, 0xc2, 0xa8, // 68: 0xa8c24ff4 ldp x20, x19, [sp], #0x20735 0xc0, 0x03, 0x5f, 0xd6, // 72: 0xd65f03c0 ret736 737 0x1f, 0x20, 0x03, 0xd5, // 52: 0xd503201f nop738 };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+0767 row = unwind_plan.GetRowForFunctionOffset(40);768 EXPECT_EQ(40, row->GetOffset());769 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);770 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);771 EXPECT_EQ(0, row->GetCFAValue().GetOffset());772 773 // After the 'ret', confirm we're back to the correct CFA of $fp+16774 row = unwind_plan.GetRowForFunctionOffset(44);775 EXPECT_EQ(44, row->GetOffset());776 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);777 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);778 EXPECT_EQ(16, row->GetCFAValue().GetOffset());779 780 // Confirm that we have no additional UnwindPlan rows before the 781 // real epilogue -- we still get the Row at offset 44.782 row = unwind_plan.GetRowForFunctionOffset(60);783 EXPECT_EQ(44, row->GetOffset());784 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);785 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);786 EXPECT_EQ(16, row->GetCFAValue().GetOffset());787 788 // And in the epilogue, confirm that we start by switching back to 789 // defining the CFA in terms of $sp.790 row = unwind_plan.GetRowForFunctionOffset(64);791 EXPECT_EQ(64, row->GetOffset());792 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);793 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);794 EXPECT_EQ(32, row->GetCFAValue().GetOffset());795}796 797TEST_F(TestArm64InstEmulation, TestCFAResetToSP) {798 ArchSpec arch("arm64-apple-ios15");799 std::unique_ptr<UnwindAssemblyInstEmulation> engine(800 static_cast<UnwindAssemblyInstEmulation *>(801 UnwindAssemblyInstEmulation::CreateInstance(arch)));802 ASSERT_NE(nullptr, engine);803 804 const UnwindPlan::Row *row;805 AddressRange sample_range;806 UnwindPlan unwind_plan(eRegisterKindLLDB);807 UnwindPlan::Row::AbstractRegisterLocation regloc;808 809 // The called_from_nodebug() from TestStepNoDebug.py810 // Most of the previous unit tests have $sp being set as811 // $fp plus an offset, and the unwinder recognizes that812 // as a CFA change. This codegen overwrites $fp and we813 // need to know that CFA is now in terms of $sp.814 uint8_t data[] = {815 // prologue816 0xff, 0x83, 0x00, 0xd1, // 0: 0xd10083ff sub sp, sp, #0x20817 0xfd, 0x7b, 0x01, 0xa9, // 4: 0xa9017bfd stp x29, x30, [sp, #0x10]818 0xfd, 0x43, 0x00, 0x91, // 8: 0x910043fd add x29, sp, #0x10819 820 // epilogue821 0xfd, 0x7b, 0x41, 0xa9, // 12: 0xa9417bfd ldp x29, x30, [sp, #0x10]822 0xff, 0x83, 0x00, 0x91, // 16: 0x910083ff add sp, sp, #0x20823 0xc0, 0x03, 0x5f, 0xd6, // 20: 0xd65f03c0 ret824 };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 the835 // 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 is838 // left as being in terms of the frame pointer, $fp now has the839 // 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 $fp847 row = unwind_plan.GetRowForFunctionOffset(12);848 EXPECT_EQ(12, row->GetOffset());849 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);850 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);851 852 // Confirm that after restoring $fp to caller's value, CFA is now in853 // terms of $sp854 row = unwind_plan.GetRowForFunctionOffset(16);855 EXPECT_EQ(16, row->GetOffset());856 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);857 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);858}859 860TEST_F(TestArm64InstEmulation, TestPrologueStartsWithStrD8) {861 ArchSpec arch("aarch64");862 std::unique_ptr<UnwindAssemblyInstEmulation> engine(863 static_cast<UnwindAssemblyInstEmulation *>(864 UnwindAssemblyInstEmulation::CreateInstance(arch)));865 ASSERT_NE(nullptr, engine);866 867 const UnwindPlan::Row *row;868 AddressRange sample_range;869 UnwindPlan unwind_plan(eRegisterKindLLDB);870 UnwindPlan::Row::AbstractRegisterLocation regloc;871 872 // The sample function is built with 'clang --target aarch64 -O1':873 //874 // int bar(float x);875 // int foo(float x) {876 // return bar(x) + bar(x);877 // }878 //879 // The function uses one floating point register and spills it with880 // 'str d8, [sp, #-0x20]!'.881 882 // clang-format off883 uint8_t data[] = {884 // prologue885 0xe8, 0x0f, 0x1e, 0xfc, // 0: fc1e0fe8 str d8, [sp, #-0x20]!886 0xfd, 0xfb, 0x00, 0xa9, // 4: a900fbfd stp x29, x30, [sp, #0x8]887 0xf3, 0x0f, 0x00, 0xf9, // 8: f9000ff3 str x19, [sp, #0x18]888 0xfd, 0x23, 0x00, 0x91, // 12: 910023fd add x29, sp, #0x8889 890 // epilogue891 0xfd, 0xfb, 0x40, 0xa9, // 16: a940fbfd ldp x29, x30, [sp, #0x8]892 0xf3, 0x0f, 0x40, 0xf9, // 20: f9400ff3 ldr x19, [sp, #0x18]893 0xe8, 0x07, 0x42, 0xfc, // 24: fc4207e8 ldr d8, [sp], #0x20894 0xc0, 0x03, 0x5f, 0xd6, // 28: d65f03c0 ret895 };896 // clang-format on897 898 // UnwindPlan we expect:899 // 0: CFA=sp +0 =>900 // 4: CFA=sp+32 => d8=[CFA-32]901 // 8: CFA=sp+32 => fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]902 // 12: CFA=sp+32 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]903 // 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]904 // 20: CFA=sp+32 => x19=[CFA-8] fp=<same> lr=<same> d8=[CFA-32]905 // 24: CFA=sp+32 => x19=<same> fp=<same> lr=<same> d8=[CFA-32]906 // 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>907 908 sample_range = AddressRange(0x1000, sizeof(data));909 910 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(911 sample_range, data, sizeof(data), unwind_plan));912 913 // 4: CFA=sp+32 => d8=[CFA-32]914 row = unwind_plan.GetRowForFunctionOffset(4);915 EXPECT_EQ(4, row->GetOffset());916 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);917 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);918 EXPECT_EQ(32, row->GetCFAValue().GetOffset());919 920 EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));921 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());922 EXPECT_EQ(-32, regloc.GetOffset());923 924 // 16: CFA=fp+24 => x19=[CFA-8] fp=[CFA-24] lr=[CFA-16] d8=[CFA-32]925 row = unwind_plan.GetRowForFunctionOffset(16);926 EXPECT_EQ(16, row->GetOffset());927 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64);928 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);929 EXPECT_EQ(24, row->GetCFAValue().GetOffset());930 931 EXPECT_TRUE(row->GetRegisterInfo(gpr_x19_arm64, regloc));932 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());933 EXPECT_EQ(-8, regloc.GetOffset());934 935 EXPECT_TRUE(row->GetRegisterInfo(gpr_fp_arm64, regloc));936 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());937 EXPECT_EQ(-24, regloc.GetOffset());938 939 EXPECT_TRUE(row->GetRegisterInfo(gpr_lr_arm64, regloc));940 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());941 EXPECT_EQ(-16, regloc.GetOffset());942 943 EXPECT_TRUE(row->GetRegisterInfo(fpu_d8_arm64, regloc));944 EXPECT_TRUE(regloc.IsAtCFAPlusOffset());945 EXPECT_EQ(-32, regloc.GetOffset());946 947 // 28: CFA=sp +0 => x19=<same> fp=<same> lr=<same> d8=<same>948 row = unwind_plan.GetRowForFunctionOffset(28);949 EXPECT_EQ(28, row->GetOffset());950 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);951 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset() == true);952 EXPECT_EQ(0, row->GetCFAValue().GetOffset());953 954 if (row->GetRegisterInfo(gpr_x19_arm64, regloc)) {955 EXPECT_TRUE(regloc.IsSame());956 }957 if (row->GetRegisterInfo(gpr_fp_arm64, regloc)) {958 EXPECT_TRUE(regloc.IsSame());959 }960 if (row->GetRegisterInfo(gpr_lr_arm64, regloc)) {961 EXPECT_TRUE(regloc.IsSame());962 }963 if (row->GetRegisterInfo(fpu_d8_arm64, regloc)) {964 EXPECT_TRUE(regloc.IsSame());965 }966}967 968TEST_F(TestArm64InstEmulation, TestMidFunctionEpilogueAndBackwardsJump) {969 ArchSpec arch("arm64-apple-ios15");970 std::unique_ptr<UnwindAssemblyInstEmulation> engine(971 static_cast<UnwindAssemblyInstEmulation *>(972 UnwindAssemblyInstEmulation::CreateInstance(arch)));973 ASSERT_NE(nullptr, engine);974 975 const UnwindPlan::Row *row;976 AddressRange sample_range;977 UnwindPlan unwind_plan(eRegisterKindLLDB);978 UnwindPlan::Row::AbstractRegisterLocation regloc;979 980 // clang-format off981 uint8_t data[] = {982 0xff, 0xc3, 0x00, 0xd1, // <+0>: sub sp, sp, #0x30983 0xfd, 0x7b, 0x02, 0xa9, // <+4>: stp x29, x30, [sp, #0x20]984 0xfd, 0x83, 0x00, 0x91, // <+8>: add x29, sp, #0x20985 0x1f, 0x04, 0x00, 0xf1, // <+12>: cmp x0, #0x1986 0x21, 0x01, 0x00, 0x54, // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE987 0xfd, 0x7b, 0x42, 0xa9, // <+20>: ldp x29, x30, [sp, #0x20]988 0xff, 0xc3, 0x00, 0x91, // <+24>: add sp, sp, #0x30989 0xc0, 0x03, 0x5f, 0xd6, // <+28>: ret990 // AFTER_EPILOGUE: LLDB computes the next 5 unwind states incorrectly.991 0x37, 0x00, 0x80, 0xd2, // <+32>: mov x23, #0x1992 0xf6, 0x5f, 0x41, 0xa9, // <+36>: ldp x22, x23, [sp, #0x10]993 0xfd, 0x7b, 0x42, 0xa9, // <+40>: ldp x29, x30, [sp, #0x20]994 0xff, 0xc3, 0x00, 0x91, // <+44>: add sp, sp, #0x30995 0xc0, 0x03, 0x5f, 0xd6, // <+48>: ret996 // DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE997 0xf6, 0x5f, 0x01, 0xa9, // <+52>: stp x22, x23, [sp, #0x10]998 0x36, 0x00, 0x80, 0xd2, // <+56>: mov x22, #0x1999 0x37, 0x00, 0x80, 0xd2, // <+60>: mov x23, #0x11000 0xf8, 0xff, 0xff, 0x17, // <+64>: b ; <+32> AFTER_EPILOGUE1001 };1002 1003 // UnwindPlan we expect:1004 // row[0]: 0: CFA=sp +0 =>1005 // row[1]: 4: CFA=sp+48 =>1006 // row[2]: 8: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8]1007 // row[3]: 12: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]1008 // row[4]: 24: CFA=sp+48 => fp=<same> lr=<same>1009 //1010 // This must come from +561011 // row[5]: 32: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]1012 // row[6]: 40: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=same, x23 = same1013 // row[6]: 44: CFA=sp+48 => fp=same lr=same x22=same, x23 = same1014 // row[6]: 48: CFA=sp0 => fp=same lr=same x22=same, x23 = same1015 //1016 // row[x]: 52: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8]1017 // row[x]: 56: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] x22=[CFA-32], x23=[CFA-24]1018 // clang-format on1019 1020 sample_range = AddressRange(0x1000, sizeof(data));1021 1022 EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly(1023 sample_range, data, sizeof(data), unwind_plan));1024 1025 // At the end of prologue (+12), CFA = fp + 16.1026 // <+0>: sub sp, sp, #0x301027 // <+4>: stp x29, x30, [sp, #0x20]1028 // <+8>: add x29, sp, #0x201029 row = unwind_plan.GetRowForFunctionOffset(12);1030 EXPECT_EQ(12, row->GetOffset());1031 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());1032 EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);1033 EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);1034 1035 // +16 and +20 are the same as +12.1036 // <+12>: cmp x0, #0x11037 // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE1038 EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(16)->GetOffset());1039 EXPECT_EQ(12, unwind_plan.GetRowForFunctionOffset(20)->GetOffset());1040 1041 // After restoring $fp to caller's value, CFA = $sp + 481042 // <+20>: ldp x29, x30, [sp, #0x20]1043 row = unwind_plan.GetRowForFunctionOffset(24);1044 EXPECT_EQ(24, row->GetOffset());1045 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());1046 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);1047 EXPECT_EQ(row->GetCFAValue().GetOffset(), 48);1048 1049 // $sp has been restored1050 // <+24>: add sp, sp, #0x301051 row = unwind_plan.GetRowForFunctionOffset(28);1052 EXPECT_EQ(28, row->GetOffset());1053 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());1054 EXPECT_TRUE(row->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64);1055 EXPECT_EQ(row->GetCFAValue().GetOffset(), 0);1056 1057 // FIXME: Row for offset +32 incorrectly inherits the state of the `ret`1058 // instruction, but +32 _never_ executes after the `ret`.1059 // <+28>: ret1060 // <+32>: mov x23, #0x11061 row = unwind_plan.GetRowForFunctionOffset(32);1062 // FIXME: EXPECT_NE(28, row->GetOffset());1063 1064 // Check that the state of this branch1065 // <+16>: b.ne ; <+52> DO_SOMETHING_AND_GOTO_AFTER_EPILOGUE1066 // was forwarded to the branch target:1067 // <+52>: stp x22, x23, [sp, #0x10]1068 row = unwind_plan.GetRowForFunctionOffset(52);1069 EXPECT_EQ(52, row->GetOffset());1070 EXPECT_TRUE(row->GetCFAValue().IsRegisterPlusOffset());1071 EXPECT_EQ(row->GetCFAValue().GetRegisterNumber(), gpr_fp_arm64);1072 EXPECT_EQ(row->GetCFAValue().GetOffset(), 16);1073}1074