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