forked from OSchip/llvm-project
329 lines
12 KiB
C++
329 lines
12 KiB
C++
//===-- 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>(ModuleSpec(FileSpec(ExpectedFile->name())));
|
|
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);
|
|
}
|