1 //===-- TestPECallFrameInfo.cpp ------------------------------*- C++ -*-===// 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 "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" 13 #include "Plugins/Process/Utility/lldb-x86-register-enums.h" 14 #include "TestingSupport/TestUtilities.h" 15 16 #include "lldb/Core/Module.h" 17 #include "lldb/Symbol/CallFrameInfo.h" 18 #include "lldb/Symbol/UnwindPlan.h" 19 #include "llvm/Testing/Support/Error.h" 20 21 using namespace lldb_private; 22 using namespace lldb; 23 24 class PECallFrameInfoTest : public testing::Test { 25 public: 26 void SetUp() override { 27 FileSystem::Initialize(); 28 ObjectFilePECOFF::Initialize(); 29 } 30 31 void TearDown() override { 32 ObjectFilePECOFF::Terminate(); 33 FileSystem::Terminate(); 34 } 35 36 protected: 37 void GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const; 38 }; 39 40 void PECallFrameInfoTest::GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const { 41 llvm::Expected<TestFile> ExpectedFile = TestFile::fromYaml( 42 R"( 43 --- !COFF 44 OptionalHeader: 45 AddressOfEntryPoint: 0 46 ImageBase: 16777216 47 SectionAlignment: 4096 48 FileAlignment: 512 49 MajorOperatingSystemVersion: 6 50 MinorOperatingSystemVersion: 0 51 MajorImageVersion: 0 52 MinorImageVersion: 0 53 MajorSubsystemVersion: 6 54 MinorSubsystemVersion: 0 55 Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI 56 DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ] 57 SizeOfStackReserve: 1048576 58 SizeOfStackCommit: 4096 59 SizeOfHeapReserve: 1048576 60 SizeOfHeapCommit: 4096 61 ExportTable: 62 RelativeVirtualAddress: 0 63 Size: 0 64 ImportTable: 65 RelativeVirtualAddress: 0 66 Size: 0 67 ResourceTable: 68 RelativeVirtualAddress: 0 69 Size: 0 70 ExceptionTable: 71 RelativeVirtualAddress: 12288 72 Size: 60 73 CertificateTable: 74 RelativeVirtualAddress: 0 75 Size: 0 76 BaseRelocationTable: 77 RelativeVirtualAddress: 0 78 Size: 0 79 Debug: 80 RelativeVirtualAddress: 0 81 Size: 0 82 Architecture: 83 RelativeVirtualAddress: 0 84 Size: 0 85 GlobalPtr: 86 RelativeVirtualAddress: 0 87 Size: 0 88 TlsTable: 89 RelativeVirtualAddress: 0 90 Size: 0 91 LoadConfigTable: 92 RelativeVirtualAddress: 0 93 Size: 0 94 BoundImport: 95 RelativeVirtualAddress: 0 96 Size: 0 97 IAT: 98 RelativeVirtualAddress: 0 99 Size: 0 100 DelayImportDescriptor: 101 RelativeVirtualAddress: 0 102 Size: 0 103 ClrRuntimeHeader: 104 RelativeVirtualAddress: 0 105 Size: 0 106 header: 107 Machine: IMAGE_FILE_MACHINE_AMD64 108 Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ] 109 sections: 110 - Name: .text 111 Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] 112 VirtualAddress: 4096 113 VirtualSize: 4096 114 - Name: .rdata 115 Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] 116 VirtualAddress: 8192 117 VirtualSize: 68 118 SectionData: 010C06000C3208F006E00470036002302105020005540D0000100000001100000020000019400E352F74670028646600213465001A3315015E000EF00CE00AD008C00650 119 120 121 # Unwind info at 0x2000: 122 # 01 0C 06 00 No chained info, prolog size = 0xC, unwind codes size is 6 words, no frame register 123 # 0C 32 UOP_AllocSmall(2) 3 * 8 + 8 bytes, offset in prolog is 0xC 124 # 08 F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 8 125 # 06 E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 6 126 # 04 70 UOP_PushNonVol(0) RDI(7), offset in prolog is 4 127 # 03 60 UOP_PushNonVol(0) RSI(6), offset in prolog is 3 128 # 02 30 UOP_PushNonVol(0) RBX(3), offset in prolog is 2 129 # Corresponding prolog: 130 # 00 push rbx 131 # 02 push rsi 132 # 03 push rdi 133 # 04 push r14 134 # 06 push r15 135 # 08 sub rsp, 20h 136 137 # Unwind info at 0x2010: 138 # 21 05 02 00 Has chained info, prolog size = 5, unwind codes size is 2 words, no frame register 139 # 05 54 0D 00 UOP_SaveNonVol(4) RBP(5) to RSP + 0xD * 8, offset in prolog is 5 140 # Chained runtime function: 141 # 00 10 00 00 Start address is 0x1000 142 # 00 11 00 00 End address is 0x1100 143 # 00 20 00 00 Unwind info RVA is 0x2000 144 # Corresponding prolog: 145 # 00 mov [rsp+68h], rbp 146 147 # Unwind info at 0x2024: 148 # 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 149 # 2F 74 67 00 UOP_SaveNonVol(4) RDI(7) to RSP + 0x67 * 8, offset in prolog is 0x2F 150 # 28 64 66 00 UOP_SaveNonVol(4) RSI(6) to RSP + 0x66 * 8, offset in prolog is 0x28 151 # 21 34 65 00 UOP_SaveNonVol(4) RBX(3) to RSP + 0x65 * 8, offset in prolog is 0x21 152 # 1A 33 UOP_SetFPReg(3), offset in prolog is 0x1A 153 # 15 01 5E 00 UOP_AllocLarge(1) 0x5E * 8 bytes, offset in prolog is 0x15 154 # 0E F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 0xE 155 # 0C E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 0xC 156 # 0A D0 UOP_PushNonVol(0) R13(0xD), offset in prolog is 0xA 157 # 08 C0 UOP_PushNonVol(0) R12(0xC), offset in prolog is 8 158 # 06 50 UOP_PushNonVol(0) RBP(5), offset in prolog is 6 159 # Corresponding prolog: 160 # 00 mov [rsp+8], rcx 161 # 05 push rbp 162 # 06 push r12 163 # 08 push r13 164 # 0A push r14 165 # 0C push r15 166 # 0E sub rsp, 2F0h 167 # 15 lea rbp, [rsp+30h] 168 # 1A mov [rbp+2F8h], rbx 169 # 21 mov [rbp+300h], rsi 170 # 28 mov [rbp+308h], rdi 171 172 - Name: .pdata 173 Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] 174 VirtualAddress: 12288 175 VirtualSize: 60 176 SectionData: 000000000000000000000000000000000000000000000000001000000011000000200000001100000012000010200000001200000013000024200000 177 178 # 00 00 00 00 179 # 00 00 00 00 Test correct processing of empty runtime functions at begin 180 # 00 00 00 00 181 182 # 00 00 00 00 183 # 00 00 00 00 Test correct processing of empty runtime functions at begin 184 # 00 00 00 00 185 186 # 00 10 00 00 Start address is 0x1000 187 # 00 11 00 00 End address is 0x1100 188 # 00 20 00 00 Unwind info RVA is 0x2000 189 190 # 00 11 00 00 Start address is 0x1100 191 # 00 12 00 00 End address is 0x1200 192 # 10 20 00 00 Unwind info RVA is 0x2010 193 194 # 00 12 00 00 Start address is 0x1200 195 # 00 13 00 00 End address is 0x1300 196 # 24 20 00 00 Unwind info RVA is 0x2024 197 198 symbols: [] 199 ... 200 )"); 201 ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); 202 203 ModuleSP module_sp = std::make_shared<Module>(ModuleSpec(FileSpec(ExpectedFile->name()))); 204 ObjectFile *object_file = module_sp->GetObjectFile(); 205 ASSERT_NE(object_file, nullptr); 206 207 std::unique_ptr<CallFrameInfo> cfi = object_file->CreateCallFrameInfo(); 208 ASSERT_NE(cfi.get(), nullptr); 209 210 SectionList *sect_list = object_file->GetSectionList(); 211 ASSERT_NE(sect_list, nullptr); 212 213 EXPECT_TRUE(cfi->GetUnwindPlan(Address(file_addr, sect_list), plan)); 214 } 215 216 TEST_F(PECallFrameInfoTest, Basic_eh) { 217 UnwindPlan plan(eRegisterKindLLDB); 218 GetUnwindPlan(0x1001080, plan); 219 EXPECT_EQ(plan.GetRowCount(), 7); 220 221 UnwindPlan::Row row; 222 row.SetOffset(0); 223 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); 224 row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); 225 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); 226 EXPECT_EQ(*plan.GetRowAtIndex(0), row); 227 228 row.SetOffset(2); 229 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); 230 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); 231 EXPECT_EQ(*plan.GetRowAtIndex(1), row); 232 233 row.SetOffset(3); 234 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); 235 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); 236 EXPECT_EQ(*plan.GetRowAtIndex(2), row); 237 238 row.SetOffset(4); 239 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); 240 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); 241 EXPECT_EQ(*plan.GetRowAtIndex(3), row); 242 243 row.SetOffset(6); 244 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); 245 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); 246 EXPECT_EQ(*plan.GetRowAtIndex(4), row); 247 248 row.SetOffset(8); 249 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); 250 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); 251 EXPECT_EQ(*plan.GetRowAtIndex(5), row); 252 253 row.SetOffset(0xC); 254 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); 255 EXPECT_EQ(*plan.GetRowAtIndex(6), row); 256 } 257 258 TEST_F(PECallFrameInfoTest, Chained_eh) { 259 UnwindPlan plan(eRegisterKindLLDB); 260 GetUnwindPlan(0x1001180, plan); 261 EXPECT_EQ(plan.GetRowCount(), 2); 262 263 UnwindPlan::Row row; 264 row.SetOffset(0); 265 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); 266 row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); 267 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); 268 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); 269 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); 270 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); 271 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); 272 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); 273 EXPECT_EQ(*plan.GetRowAtIndex(0), row); 274 275 row.SetOffset(5); 276 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, 0x18, true); 277 EXPECT_EQ(*plan.GetRowAtIndex(1), row); 278 } 279 280 TEST_F(PECallFrameInfoTest, Frame_reg_eh) { 281 UnwindPlan plan(eRegisterKindLLDB); 282 GetUnwindPlan(0x1001280, plan); 283 EXPECT_EQ(plan.GetRowCount(), 11); 284 285 UnwindPlan::Row row; 286 row.SetOffset(0); 287 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); 288 row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); 289 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); 290 EXPECT_EQ(*plan.GetRowAtIndex(0), row); 291 292 row.SetOffset(6); 293 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); 294 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, -0x10, true); 295 EXPECT_EQ(*plan.GetRowAtIndex(1), row); 296 297 row.SetOffset(8); 298 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); 299 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r12_x86_64, -0x18, true); 300 EXPECT_EQ(*plan.GetRowAtIndex(2), row); 301 302 row.SetOffset(0xA); 303 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); 304 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r13_x86_64, -0x20, true); 305 EXPECT_EQ(*plan.GetRowAtIndex(3), row); 306 307 row.SetOffset(0xC); 308 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); 309 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); 310 EXPECT_EQ(*plan.GetRowAtIndex(4), row); 311 312 row.SetOffset(0xE); 313 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); 314 row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); 315 EXPECT_EQ(*plan.GetRowAtIndex(5), row); 316 317 row.SetOffset(0x15); 318 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x320); 319 EXPECT_EQ(*plan.GetRowAtIndex(6), row); 320 321 row.SetOffset(0x1A); 322 row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rbp_x86_64, 0x2F0); 323 EXPECT_EQ(*plan.GetRowAtIndex(7), row); 324 325 row.SetOffset(0x21); 326 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, 8, true); 327 EXPECT_EQ(*plan.GetRowAtIndex(8), row); 328 329 row.SetOffset(0x28); 330 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, 0x10, true); 331 EXPECT_EQ(*plan.GetRowAtIndex(9), row); 332 333 row.SetOffset(0x2F); 334 row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, 0x18, true); 335 EXPECT_EQ(*plan.GetRowAtIndex(10), row); 336 } 337