| //===-- TestPECallFrameInfo.cpp -------------------------------------------===// |
| // |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "gtest/gtest.h" |
| |
| #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" |
| #include "Plugins/Process/Utility/lldb-x86-register-enums.h" |
| #include "TestingSupport/SubsystemRAII.h" |
| #include "TestingSupport/TestUtilities.h" |
| |
| #include "lldb/Core/Module.h" |
| #include "lldb/Symbol/CallFrameInfo.h" |
| #include "lldb/Symbol/UnwindPlan.h" |
| #include "llvm/Testing/Support/Error.h" |
| |
| using namespace lldb_private; |
| using namespace lldb; |
| |
| class PECallFrameInfoTest : public testing::Test { |
| SubsystemRAII<FileSystem, ObjectFilePECOFF> subsystems; |
| |
| protected: |
| void GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const; |
| }; |
| |
| void PECallFrameInfoTest::GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const { |
| llvm::Expected<TestFile> ExpectedFile = TestFile::fromYaml( |
| R"( |
| --- !COFF |
| OptionalHeader: |
| AddressOfEntryPoint: 0 |
| ImageBase: 16777216 |
| SectionAlignment: 4096 |
| FileAlignment: 512 |
| MajorOperatingSystemVersion: 6 |
| MinorOperatingSystemVersion: 0 |
| MajorImageVersion: 0 |
| MinorImageVersion: 0 |
| MajorSubsystemVersion: 6 |
| MinorSubsystemVersion: 0 |
| Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI |
| DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ] |
| SizeOfStackReserve: 1048576 |
| SizeOfStackCommit: 4096 |
| SizeOfHeapReserve: 1048576 |
| SizeOfHeapCommit: 4096 |
| ExportTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| ImportTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| ResourceTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| ExceptionTable: |
| RelativeVirtualAddress: 12288 |
| Size: 60 |
| CertificateTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| BaseRelocationTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| Debug: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| Architecture: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| GlobalPtr: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| TlsTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| LoadConfigTable: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| BoundImport: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| IAT: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| DelayImportDescriptor: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| ClrRuntimeHeader: |
| RelativeVirtualAddress: 0 |
| Size: 0 |
| header: |
| Machine: IMAGE_FILE_MACHINE_AMD64 |
| Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ] |
| sections: |
| - Name: .text |
| Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] |
| VirtualAddress: 4096 |
| VirtualSize: 4096 |
| - Name: .rdata |
| Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] |
| VirtualAddress: 8192 |
| VirtualSize: 68 |
| SectionData: 010C06000C3208F006E00470036002302105020005540D0000100000001100000020000019400E352F74670028646600213465001A3315015E000EF00CE00AD008C00650 |
| |
| |
| # Unwind info at 0x2000: |
| # 01 0C 06 00 No chained info, prolog size = 0xC, unwind codes size is 6 words, no frame register |
| # 0C 32 UOP_AllocSmall(2) 3 * 8 + 8 bytes, offset in prolog is 0xC |
| # 08 F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 8 |
| # 06 E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 6 |
| # 04 70 UOP_PushNonVol(0) RDI(7), offset in prolog is 4 |
| # 03 60 UOP_PushNonVol(0) RSI(6), offset in prolog is 3 |
| # 02 30 UOP_PushNonVol(0) RBX(3), offset in prolog is 2 |
| # Corresponding prolog: |
| # 00 push rbx |
| # 02 push rsi |
| # 03 push rdi |
| # 04 push r14 |
| # 06 push r15 |
| # 08 sub rsp, 20h |
| |
| # Unwind info at 0x2010: |
| # 21 05 02 00 Has chained info, prolog size = 5, unwind codes size is 2 words, no frame register |
| # 05 54 0D 00 UOP_SaveNonVol(4) RBP(5) to RSP + 0xD * 8, offset in prolog is 5 |
| # Chained runtime function: |
| # 00 10 00 00 Start address is 0x1000 |
| # 00 11 00 00 End address is 0x1100 |
| # 00 20 00 00 Unwind info RVA is 0x2000 |
| # Corresponding prolog: |
| # 00 mov [rsp+68h], rbp |
| |
| # Unwind info at 0x2024: |
| # 19 40 0E 35 No chained info, prolog size = 0x40, unwind codes size is 0xE words, frame register is RBP, frame register offset is RSP + 3 * 16 |
| # 2F 74 67 00 UOP_SaveNonVol(4) RDI(7) to RSP + 0x67 * 8, offset in prolog is 0x2F |
| # 28 64 66 00 UOP_SaveNonVol(4) RSI(6) to RSP + 0x66 * 8, offset in prolog is 0x28 |
| # 21 34 65 00 UOP_SaveNonVol(4) RBX(3) to RSP + 0x65 * 8, offset in prolog is 0x21 |
| # 1A 33 UOP_SetFPReg(3), offset in prolog is 0x1A |
| # 15 01 5E 00 UOP_AllocLarge(1) 0x5E * 8 bytes, offset in prolog is 0x15 |
| # 0E F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 0xE |
| # 0C E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 0xC |
| # 0A D0 UOP_PushNonVol(0) R13(0xD), offset in prolog is 0xA |
| # 08 C0 UOP_PushNonVol(0) R12(0xC), offset in prolog is 8 |
| # 06 50 UOP_PushNonVol(0) RBP(5), offset in prolog is 6 |
| # Corresponding prolog: |
| # 00 mov [rsp+8], rcx |
| # 05 push rbp |
| # 06 push r12 |
| # 08 push r13 |
| # 0A push r14 |
| # 0C push r15 |
| # 0E sub rsp, 2F0h |
| # 15 lea rbp, [rsp+30h] |
| # 1A mov [rbp+2F8h], rbx |
| # 21 mov [rbp+300h], rsi |
| # 28 mov [rbp+308h], rdi |
| |
| - Name: .pdata |
| Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] |
| VirtualAddress: 12288 |
| VirtualSize: 60 |
| SectionData: 000000000000000000000000000000000000000000000000001000000011000000200000001100000012000010200000001200000013000024200000 |
| |
| # 00 00 00 00 |
| # 00 00 00 00 Test correct processing of empty runtime functions at begin |
| # 00 00 00 00 |
| |
| # 00 00 00 00 |
| # 00 00 00 00 Test correct processing of empty runtime functions at begin |
| # 00 00 00 00 |
| |
| # 00 10 00 00 Start address is 0x1000 |
| # 00 11 00 00 End address is 0x1100 |
| # 00 20 00 00 Unwind info RVA is 0x2000 |
| |
| # 00 11 00 00 Start address is 0x1100 |
| # 00 12 00 00 End address is 0x1200 |
| # 10 20 00 00 Unwind info RVA is 0x2010 |
| |
| # 00 12 00 00 Start address is 0x1200 |
| # 00 13 00 00 End address is 0x1300 |
| # 24 20 00 00 Unwind info RVA is 0x2024 |
| |
| symbols: [] |
| ... |
| )"); |
| ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
| |
| ModuleSP module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec()); |
| ObjectFile *object_file = module_sp->GetObjectFile(); |
| ASSERT_NE(object_file, nullptr); |
| |
| std::unique_ptr<CallFrameInfo> cfi = object_file->CreateCallFrameInfo(); |
| ASSERT_NE(cfi.get(), nullptr); |
| |
| SectionList *sect_list = object_file->GetSectionList(); |
| ASSERT_NE(sect_list, nullptr); |
| |
| EXPECT_TRUE(cfi->GetUnwindPlan(Address(file_addr, sect_list), plan)); |
| } |
| |
| TEST_F(PECallFrameInfoTest, Basic_eh) { |
| UnwindPlan plan(eRegisterKindLLDB); |
| GetUnwindPlan(0x1001080, plan); |
| EXPECT_EQ(plan.GetRowCount(), 7); |
| |
| UnwindPlan::Row row; |
| row.SetOffset(0); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); |
| row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
| |
| row.SetOffset(2); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
| |
| row.SetOffset(3); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(2), row); |
| |
| row.SetOffset(4); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(3), row); |
| |
| row.SetOffset(6); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(4), row); |
| |
| row.SetOffset(8); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(5), row); |
| |
| row.SetOffset(0xC); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); |
| EXPECT_EQ(*plan.GetRowAtIndex(6), row); |
| } |
| |
| TEST_F(PECallFrameInfoTest, Chained_eh) { |
| UnwindPlan plan(eRegisterKindLLDB); |
| GetUnwindPlan(0x1001180, plan); |
| EXPECT_EQ(plan.GetRowCount(), 2); |
| |
| UnwindPlan::Row row; |
| row.SetOffset(0); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); |
| row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
| |
| row.SetOffset(5); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, 0x18, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
| } |
| |
| TEST_F(PECallFrameInfoTest, Frame_reg_eh) { |
| UnwindPlan plan(eRegisterKindLLDB); |
| GetUnwindPlan(0x1001280, plan); |
| EXPECT_EQ(plan.GetRowCount(), 11); |
| |
| UnwindPlan::Row row; |
| row.SetOffset(0); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); |
| row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
| |
| row.SetOffset(6); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, -0x10, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
| |
| row.SetOffset(8); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r12_x86_64, -0x18, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(2), row); |
| |
| row.SetOffset(0xA); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r13_x86_64, -0x20, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(3), row); |
| |
| row.SetOffset(0xC); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(4), row); |
| |
| row.SetOffset(0xE); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(5), row); |
| |
| row.SetOffset(0x15); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x320); |
| EXPECT_EQ(*plan.GetRowAtIndex(6), row); |
| |
| row.SetOffset(0x1A); |
| row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rbp_x86_64, 0x2F0); |
| EXPECT_EQ(*plan.GetRowAtIndex(7), row); |
| |
| row.SetOffset(0x21); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, 8, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(8), row); |
| |
| row.SetOffset(0x28); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, 0x10, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(9), row); |
| |
| row.SetOffset(0x2F); |
| row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, 0x18, true); |
| EXPECT_EQ(*plan.GetRowAtIndex(10), row); |
| } |