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