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 
GetUnwindPlan(addr_t file_addr,UnwindPlan & plan) const32 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 
TEST_F(PECallFrameInfoTest,Basic_eh)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 
TEST_F(PECallFrameInfoTest,Chained_eh)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 
TEST_F(PECallFrameInfoTest,Frame_reg_eh)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