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 | Yaml: 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>(args: 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(file_addr: 0x1001080, plan); |
211 | EXPECT_EQ(plan.GetRowCount(), 7); |
212 | |
213 | UnwindPlan::Row row; |
214 | row.SetOffset(0); |
215 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 8); |
216 | row.SetRegisterLocationToIsCFAPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0, can_replace: true); |
217 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rip_x86_64, offset: -8, can_replace: true); |
218 | EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
219 | |
220 | row.SetOffset(2); |
221 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x10); |
222 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rbx_x86_64, offset: -0x10, can_replace: true); |
223 | EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
224 | |
225 | row.SetOffset(3); |
226 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x18); |
227 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rsi_x86_64, offset: -0x18, can_replace: true); |
228 | EXPECT_EQ(*plan.GetRowAtIndex(2), row); |
229 | |
230 | row.SetOffset(4); |
231 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x20); |
232 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rdi_x86_64, offset: -0x20, can_replace: true); |
233 | EXPECT_EQ(*plan.GetRowAtIndex(3), row); |
234 | |
235 | row.SetOffset(6); |
236 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x28); |
237 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r14_x86_64, offset: -0x28, can_replace: true); |
238 | EXPECT_EQ(*plan.GetRowAtIndex(4), row); |
239 | |
240 | row.SetOffset(8); |
241 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x30); |
242 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r15_x86_64, offset: -0x30, can_replace: true); |
243 | EXPECT_EQ(*plan.GetRowAtIndex(5), row); |
244 | |
245 | row.SetOffset(0xC); |
246 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x50); |
247 | EXPECT_EQ(*plan.GetRowAtIndex(6), row); |
248 | } |
249 | |
250 | TEST_F(PECallFrameInfoTest, Chained_eh) { |
251 | UnwindPlan plan(eRegisterKindLLDB); |
252 | GetUnwindPlan(file_addr: 0x1001180, plan); |
253 | EXPECT_EQ(plan.GetRowCount(), 2); |
254 | |
255 | UnwindPlan::Row row; |
256 | row.SetOffset(0); |
257 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x50); |
258 | row.SetRegisterLocationToIsCFAPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0, can_replace: true); |
259 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rip_x86_64, offset: -8, can_replace: true); |
260 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rbx_x86_64, offset: -0x10, can_replace: true); |
261 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rsi_x86_64, offset: -0x18, can_replace: true); |
262 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rdi_x86_64, offset: -0x20, can_replace: true); |
263 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r14_x86_64, offset: -0x28, can_replace: true); |
264 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r15_x86_64, offset: -0x30, can_replace: true); |
265 | EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
266 | |
267 | row.SetOffset(5); |
268 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rbp_x86_64, offset: 0x18, can_replace: true); |
269 | EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
270 | } |
271 | |
272 | TEST_F(PECallFrameInfoTest, Frame_reg_eh) { |
273 | UnwindPlan plan(eRegisterKindLLDB); |
274 | GetUnwindPlan(file_addr: 0x1001280, plan); |
275 | EXPECT_EQ(plan.GetRowCount(), 11); |
276 | |
277 | UnwindPlan::Row row; |
278 | row.SetOffset(0); |
279 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 8); |
280 | row.SetRegisterLocationToIsCFAPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0, can_replace: true); |
281 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rip_x86_64, offset: -8, can_replace: true); |
282 | EXPECT_EQ(*plan.GetRowAtIndex(0), row); |
283 | |
284 | row.SetOffset(6); |
285 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x10); |
286 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rbp_x86_64, offset: -0x10, can_replace: true); |
287 | EXPECT_EQ(*plan.GetRowAtIndex(1), row); |
288 | |
289 | row.SetOffset(8); |
290 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x18); |
291 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r12_x86_64, offset: -0x18, can_replace: true); |
292 | EXPECT_EQ(*plan.GetRowAtIndex(2), row); |
293 | |
294 | row.SetOffset(0xA); |
295 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x20); |
296 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r13_x86_64, offset: -0x20, can_replace: true); |
297 | EXPECT_EQ(*plan.GetRowAtIndex(3), row); |
298 | |
299 | row.SetOffset(0xC); |
300 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x28); |
301 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r14_x86_64, offset: -0x28, can_replace: true); |
302 | EXPECT_EQ(*plan.GetRowAtIndex(4), row); |
303 | |
304 | row.SetOffset(0xE); |
305 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x30); |
306 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_r15_x86_64, offset: -0x30, can_replace: true); |
307 | EXPECT_EQ(*plan.GetRowAtIndex(5), row); |
308 | |
309 | row.SetOffset(0x15); |
310 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rsp_x86_64, offset: 0x320); |
311 | EXPECT_EQ(*plan.GetRowAtIndex(6), row); |
312 | |
313 | row.SetOffset(0x1A); |
314 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: lldb_rbp_x86_64, offset: 0x2F0); |
315 | EXPECT_EQ(*plan.GetRowAtIndex(7), row); |
316 | |
317 | row.SetOffset(0x21); |
318 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rbx_x86_64, offset: 8, can_replace: true); |
319 | EXPECT_EQ(*plan.GetRowAtIndex(8), row); |
320 | |
321 | row.SetOffset(0x28); |
322 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rsi_x86_64, offset: 0x10, can_replace: true); |
323 | EXPECT_EQ(*plan.GetRowAtIndex(9), row); |
324 | |
325 | row.SetOffset(0x2F); |
326 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: lldb_rdi_x86_64, offset: 0x18, can_replace: true); |
327 | EXPECT_EQ(*plan.GetRowAtIndex(10), row); |
328 | } |
329 | |