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