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
22using namespace lldb_private;
23using namespace lldb;
24
25class PECallFrameInfoTest : public testing::Test {
26 SubsystemRAII<FileSystem, ObjectFilePECOFF> subsystems;
27
28protected:
29 void GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const;
30};
31
32void PECallFrameInfoTest::GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const {
33 llvm::Expected<TestFile> ExpectedFile = TestFile::fromYaml(
34 Yaml: R"(
35--- !COFF
36OptionalHeader:
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
98header:
99 Machine: IMAGE_FILE_MACHINE_AMD64
100 Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
101sections:
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
190symbols: []
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
208TEST_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
250TEST_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
272TEST_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

source code of lldb/unittests/ObjectFile/PECOFF/TestPECallFrameInfo.cpp