1 | //===-- TestDWARFCallFrameInfo.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/ELF/ObjectFileELF.h" |
13 | #include "Plugins/Process/Utility/RegisterContext_x86.h" |
14 | #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" |
15 | #include "TestingSupport/SubsystemRAII.h" |
16 | #include "TestingSupport/TestUtilities.h" |
17 | |
18 | #include "lldb/Core/Module.h" |
19 | #include "lldb/Core/ModuleSpec.h" |
20 | #include "lldb/Core/Section.h" |
21 | #include "lldb/Host/FileSystem.h" |
22 | #include "lldb/Host/HostInfo.h" |
23 | #include "lldb/Symbol/DWARFCallFrameInfo.h" |
24 | #include "lldb/Utility/StreamString.h" |
25 | #include "llvm/Testing/Support/Error.h" |
26 | |
27 | #include "llvm/Support/FileUtilities.h" |
28 | #include "llvm/Support/Path.h" |
29 | #include "llvm/Support/Program.h" |
30 | #include "llvm/Support/raw_ostream.h" |
31 | |
32 | using namespace lldb_private; |
33 | using namespace lldb; |
34 | |
35 | class DWARFCallFrameInfoTest : public testing::Test { |
36 | SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab> |
37 | subsystems; |
38 | |
39 | protected: |
40 | void TestBasic(DWARFCallFrameInfo::Type type, llvm::StringRef symbol); |
41 | }; |
42 | |
43 | namespace lldb_private { |
44 | static std::ostream &operator<<(std::ostream &OS, const UnwindPlan::Row &row) { |
45 | StreamString SS; |
46 | row.Dump(s&: SS, unwind_plan: nullptr, thread: nullptr, base_addr: 0); |
47 | return OS << SS.GetData(); |
48 | } |
49 | } // namespace lldb_private |
50 | |
51 | static UnwindPlan::Row GetExpectedRow0() { |
52 | UnwindPlan::Row row; |
53 | row.SetOffset(0); |
54 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rsp_x86_64, offset: 8); |
55 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
56 | return row; |
57 | } |
58 | |
59 | static UnwindPlan::Row GetExpectedRow1() { |
60 | UnwindPlan::Row row; |
61 | row.SetOffset(1); |
62 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rsp_x86_64, offset: 16); |
63 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
64 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rbp_x86_64, offset: -16, can_replace: false); |
65 | return row; |
66 | } |
67 | |
68 | static UnwindPlan::Row GetExpectedRow2() { |
69 | UnwindPlan::Row row; |
70 | row.SetOffset(4); |
71 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: dwarf_rbp_x86_64, offset: 16); |
72 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rip_x86_64, offset: -8, can_replace: false); |
73 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: dwarf_rbp_x86_64, offset: -16, can_replace: false); |
74 | return row; |
75 | } |
76 | |
77 | void DWARFCallFrameInfoTest::TestBasic(DWARFCallFrameInfo::Type type, |
78 | llvm::StringRef symbol) { |
79 | auto ExpectedFile = TestFile::fromYaml(Yaml: R"( |
80 | --- !ELF |
81 | FileHeader: |
82 | Class: ELFCLASS64 |
83 | Data: ELFDATA2LSB |
84 | Type: ET_DYN |
85 | Machine: EM_X86_64 |
86 | Entry: 0x0000000000000260 |
87 | Sections: |
88 | - Name: .text |
89 | Type: SHT_PROGBITS |
90 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
91 | Address: 0x0000000000000260 |
92 | AddressAlign: 0x0000000000000010 |
93 | Content: 554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC3 |
94 | #0000000000000260 <eh_frame>: |
95 | # 260: 55 push %rbp |
96 | # 261: 48 89 e5 mov %rsp,%rbp |
97 | # 264: 89 7d fc mov %edi,-0x4(%rbp) |
98 | # 267: 8b 45 fc mov -0x4(%rbp),%eax |
99 | # 26a: 5d pop %rbp |
100 | # 26b: c3 retq |
101 | # 26c: 0f 1f 40 00 nopl 0x0(%rax) |
102 | # |
103 | #0000000000000270 <debug_frame3>: |
104 | # 270: 55 push %rbp |
105 | # 271: 48 89 e5 mov %rsp,%rbp |
106 | # 274: 89 7d fc mov %edi,-0x4(%rbp) |
107 | # 277: 8b 45 fc mov -0x4(%rbp),%eax |
108 | # 27a: 5d pop %rbp |
109 | # 27b: c3 retq |
110 | # 27c: 0f 1f 40 00 nopl 0x0(%rax) |
111 | # |
112 | #0000000000000280 <debug_frame4>: |
113 | # 280: 55 push %rbp |
114 | # 281: 48 89 e5 mov %rsp,%rbp |
115 | # 284: 89 7d fc mov %edi,-0x4(%rbp) |
116 | # 287: 8b 45 fc mov -0x4(%rbp),%eax |
117 | # 28a: 5d pop %rbp |
118 | # 28b: c3 retq |
119 | - Name: .eh_frame |
120 | Type: SHT_X86_64_UNWIND |
121 | Flags: [ SHF_ALLOC ] |
122 | Address: 0x0000000000000290 |
123 | AddressAlign: 0x0000000000000008 |
124 | Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000 |
125 | #00000000 0000000000000014 00000000 CIE |
126 | # Version: 1 |
127 | # Augmentation: "zR" |
128 | # Code alignment factor: 1 |
129 | # Data alignment factor: -8 |
130 | # Return address column: 16 |
131 | # Augmentation data: 1b |
132 | # |
133 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
134 | # DW_CFA_offset: r16 (rip) at cfa-8 |
135 | # DW_CFA_nop |
136 | # DW_CFA_nop |
137 | # |
138 | #00000018 000000000000001c 0000001c FDE cie=00000000 pc=ffffffffffffffd0..ffffffffffffffdc |
139 | # DW_CFA_advance_loc: 1 to ffffffffffffffd1 |
140 | # DW_CFA_def_cfa_offset: 16 |
141 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
142 | # DW_CFA_advance_loc: 3 to ffffffffffffffd4 |
143 | # DW_CFA_def_cfa_register: r6 (rbp) |
144 | # DW_CFA_nop |
145 | # DW_CFA_nop |
146 | # DW_CFA_nop |
147 | # DW_CFA_nop |
148 | # DW_CFA_nop |
149 | # DW_CFA_nop |
150 | # DW_CFA_nop |
151 | - Name: .debug_frame |
152 | Type: SHT_PROGBITS |
153 | AddressAlign: 0x0000000000000008 |
154 | Content: 14000000FFFFFFFF03000178100C070890010000000000001C0000000000000070020000000000000C00000000000000410E108602430D0614000000FFFFFFFF040008000178100C07089001000000001C0000003800000080020000000000000C00000000000000410E108602430D06 |
155 | #00000000 0000000000000014 ffffffff CIE |
156 | # Version: 3 |
157 | # Augmentation: "" |
158 | # Code alignment factor: 1 |
159 | # Data alignment factor: -8 |
160 | # Return address column: 16 |
161 | # |
162 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
163 | # DW_CFA_offset: r16 (rip) at cfa-8 |
164 | # DW_CFA_nop |
165 | # DW_CFA_nop |
166 | # DW_CFA_nop |
167 | # DW_CFA_nop |
168 | # DW_CFA_nop |
169 | # DW_CFA_nop |
170 | # |
171 | #00000018 000000000000001c 00000000 FDE cie=00000000 pc=0000000000000270..000000000000027c |
172 | # DW_CFA_advance_loc: 1 to 0000000000000271 |
173 | # DW_CFA_def_cfa_offset: 16 |
174 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
175 | # DW_CFA_advance_loc: 3 to 0000000000000274 |
176 | # DW_CFA_def_cfa_register: r6 (rbp) |
177 | # |
178 | #00000038 0000000000000014 ffffffff CIE |
179 | # Version: 4 |
180 | # Augmentation: "" |
181 | # Pointer Size: 8 |
182 | # Segment Size: 0 |
183 | # Code alignment factor: 1 |
184 | # Data alignment factor: -8 |
185 | # Return address column: 16 |
186 | # |
187 | # DW_CFA_def_cfa: r7 (rsp) ofs 8 |
188 | # DW_CFA_offset: r16 (rip) at cfa-8 |
189 | # DW_CFA_nop |
190 | # DW_CFA_nop |
191 | # DW_CFA_nop |
192 | # DW_CFA_nop |
193 | # |
194 | #00000050 000000000000001c 00000038 FDE cie=00000038 pc=0000000000000280..000000000000028c |
195 | # DW_CFA_advance_loc: 1 to 0000000000000281 |
196 | # DW_CFA_def_cfa_offset: 16 |
197 | # DW_CFA_offset: r6 (rbp) at cfa-16 |
198 | # DW_CFA_advance_loc: 3 to 0000000000000284 |
199 | # DW_CFA_def_cfa_register: r6 (rbp) |
200 | Symbols: |
201 | - Name: eh_frame |
202 | Type: STT_FUNC |
203 | Section: .text |
204 | Value: 0x0000000000000260 |
205 | Size: 0x000000000000000C |
206 | Binding: STB_GLOBAL |
207 | - Name: debug_frame3 |
208 | Type: STT_FUNC |
209 | Section: .text |
210 | Value: 0x0000000000000270 |
211 | Size: 0x000000000000000C |
212 | Binding: STB_GLOBAL |
213 | - Name: debug_frame4 |
214 | Type: STT_FUNC |
215 | Section: .text |
216 | Value: 0x0000000000000280 |
217 | Size: 0x000000000000000C |
218 | Binding: STB_GLOBAL |
219 | ... |
220 | )" ); |
221 | ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
222 | |
223 | auto module_sp = std::make_shared<Module>(args: ExpectedFile->moduleSpec()); |
224 | SectionList *list = module_sp->GetSectionList(); |
225 | ASSERT_NE(nullptr, list); |
226 | |
227 | auto section_sp = list->FindSectionByType(sect_type: type == DWARFCallFrameInfo::EH |
228 | ? eSectionTypeEHFrame |
229 | : eSectionTypeDWARFDebugFrame, |
230 | check_children: false); |
231 | ASSERT_NE(nullptr, section_sp); |
232 | |
233 | DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, type); |
234 | |
235 | const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( |
236 | name: ConstString(symbol), symbol_type: eSymbolTypeAny); |
237 | ASSERT_NE(nullptr, sym); |
238 | |
239 | UnwindPlan plan(eRegisterKindGeneric); |
240 | ASSERT_TRUE(cfi.GetUnwindPlan(sym->GetAddress(), plan)); |
241 | ASSERT_EQ(3, plan.GetRowCount()); |
242 | EXPECT_EQ(GetExpectedRow0(), *plan.GetRowAtIndex(0)); |
243 | EXPECT_EQ(GetExpectedRow1(), *plan.GetRowAtIndex(1)); |
244 | EXPECT_EQ(GetExpectedRow2(), *plan.GetRowAtIndex(2)); |
245 | } |
246 | |
247 | TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) { |
248 | TestBasic(type: DWARFCallFrameInfo::DWARF, symbol: "debug_frame3" ); |
249 | } |
250 | |
251 | TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) { |
252 | TestBasic(type: DWARFCallFrameInfo::DWARF, symbol: "debug_frame4" ); |
253 | } |
254 | |
255 | TEST_F(DWARFCallFrameInfoTest, Basic_eh) { |
256 | TestBasic(type: DWARFCallFrameInfo::EH, symbol: "eh_frame" ); |
257 | } |
258 | |