1 | //===-- TestObjectFileELF.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 "Plugins/ObjectFile/ELF/ObjectFileELF.h" |
11 | #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" |
12 | #include "TestingSupport/SubsystemRAII.h" |
13 | #include "TestingSupport/TestUtilities.h" |
14 | #include "lldb/Core/Module.h" |
15 | #include "lldb/Core/ModuleSpec.h" |
16 | #include "lldb/Core/Section.h" |
17 | #include "lldb/Host/FileSystem.h" |
18 | #include "lldb/Host/HostInfo.h" |
19 | #include "lldb/Utility/DataBufferHeap.h" |
20 | #include "llvm/Support/Compression.h" |
21 | #include "llvm/Support/FileUtilities.h" |
22 | #include "llvm/Support/Path.h" |
23 | #include "llvm/Support/Program.h" |
24 | #include "llvm/Support/raw_ostream.h" |
25 | #include "llvm/Testing/Support/Error.h" |
26 | #include "gtest/gtest.h" |
27 | |
28 | using namespace lldb_private; |
29 | using namespace lldb; |
30 | |
31 | class ObjectFileELFTest : public testing::Test { |
32 | SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, SymbolFileSymtab> |
33 | subsystems; |
34 | }; |
35 | |
36 | TEST_F(ObjectFileELFTest, SectionsResolveConsistently) { |
37 | auto ExpectedFile = TestFile::fromYaml(Yaml: R"( |
38 | --- !ELF |
39 | FileHeader: |
40 | Class: ELFCLASS64 |
41 | Data: ELFDATA2LSB |
42 | Type: ET_EXEC |
43 | Machine: EM_X86_64 |
44 | Entry: 0x0000000000400180 |
45 | Sections: |
46 | - Name: .note.gnu.build-id |
47 | Type: SHT_NOTE |
48 | Flags: [ SHF_ALLOC ] |
49 | Address: 0x0000000000400158 |
50 | AddressAlign: 0x0000000000000004 |
51 | Content: 040000001400000003000000474E55003F3EC29E3FD83E49D18C4D49CD8A730CC13117B6 |
52 | - Name: .text |
53 | Type: SHT_PROGBITS |
54 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
55 | Address: 0x0000000000400180 |
56 | AddressAlign: 0x0000000000000010 |
57 | Content: 554889E58B042500106000890425041060005DC3 |
58 | - Name: .data |
59 | Type: SHT_PROGBITS |
60 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
61 | Address: 0x0000000000601000 |
62 | AddressAlign: 0x0000000000000004 |
63 | Content: 2F000000 |
64 | - Name: .bss |
65 | Type: SHT_NOBITS |
66 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
67 | Address: 0x0000000000601004 |
68 | AddressAlign: 0x0000000000000004 |
69 | Size: 0x0000000000000004 |
70 | Symbols: |
71 | - Name: Y |
72 | Type: STT_OBJECT |
73 | Section: .data |
74 | Value: 0x0000000000601000 |
75 | Size: 0x0000000000000004 |
76 | Binding: STB_GLOBAL |
77 | - Name: _start |
78 | Type: STT_FUNC |
79 | Section: .text |
80 | Value: 0x0000000000400180 |
81 | Size: 0x0000000000000014 |
82 | Binding: STB_GLOBAL |
83 | - Name: X |
84 | Type: STT_OBJECT |
85 | Section: .bss |
86 | Value: 0x0000000000601004 |
87 | Size: 0x0000000000000004 |
88 | Binding: STB_GLOBAL |
89 | ... |
90 | )" ); |
91 | ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
92 | |
93 | auto module_sp = std::make_shared<Module>(args: ExpectedFile->moduleSpec()); |
94 | SectionList *list = module_sp->GetSectionList(); |
95 | ASSERT_NE(nullptr, list); |
96 | |
97 | auto bss_sp = list->FindSectionByName(section_dstr: ConstString(".bss" )); |
98 | ASSERT_NE(nullptr, bss_sp); |
99 | auto data_sp = list->FindSectionByName(section_dstr: ConstString(".data" )); |
100 | ASSERT_NE(nullptr, data_sp); |
101 | auto text_sp = list->FindSectionByName(section_dstr: ConstString(".text" )); |
102 | ASSERT_NE(nullptr, text_sp); |
103 | |
104 | const Symbol *X = module_sp->FindFirstSymbolWithNameAndType(name: ConstString("X" ), |
105 | symbol_type: eSymbolTypeAny); |
106 | ASSERT_NE(nullptr, X); |
107 | EXPECT_EQ(bss_sp, X->GetAddress().GetSection()); |
108 | |
109 | const Symbol *Y = module_sp->FindFirstSymbolWithNameAndType(name: ConstString("Y" ), |
110 | symbol_type: eSymbolTypeAny); |
111 | ASSERT_NE(nullptr, Y); |
112 | EXPECT_EQ(data_sp, Y->GetAddress().GetSection()); |
113 | |
114 | const Symbol *start = module_sp->FindFirstSymbolWithNameAndType( |
115 | name: ConstString("_start" ), symbol_type: eSymbolTypeAny); |
116 | ASSERT_NE(nullptr, start); |
117 | EXPECT_EQ(text_sp, start->GetAddress().GetSection()); |
118 | } |
119 | |
120 | // Test that GetModuleSpecifications works on an "atypical" object file which |
121 | // has section headers right after the ELF header (instead of the more common |
122 | // layout where the section headers are at the very end of the object file). |
123 | // |
124 | // Test file generated with yaml2obj (@svn rev 324254) from the following input: |
125 | /* |
126 | --- !ELF |
127 | FileHeader: |
128 | Class: ELFCLASS64 |
129 | Data: ELFDATA2LSB |
130 | Type: ET_EXEC |
131 | Machine: EM_X86_64 |
132 | Entry: 0x00000000004003D0 |
133 | Sections: |
134 | - Name: .note.gnu.build-id |
135 | Type: SHT_NOTE |
136 | Flags: [ SHF_ALLOC ] |
137 | Address: 0x0000000000400274 |
138 | AddressAlign: 0x0000000000000004 |
139 | Content: 040000001400000003000000474E55001B8A73AC238390E32A7FF4AC8EBE4D6A41ECF5C9 |
140 | - Name: .text |
141 | Type: SHT_PROGBITS |
142 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
143 | Address: 0x00000000004003D0 |
144 | AddressAlign: 0x0000000000000010 |
145 | Content: DEADBEEFBAADF00D |
146 | ... |
147 | */ |
148 | TEST_F(ObjectFileELFTest, GetModuleSpecifications_EarlySectionHeaders) { |
149 | std::string SO = GetInputFilePath(name: "early-section-headers.so" ); |
150 | ModuleSpecList Specs; |
151 | ASSERT_EQ(1u, ObjectFile::GetModuleSpecifications(FileSpec(SO), 0, 0, Specs)); |
152 | ModuleSpec Spec; |
153 | ASSERT_TRUE(Specs.GetModuleSpecAtIndex(0, Spec)) ; |
154 | UUID Uuid; |
155 | Uuid.SetFromStringRef("1b8a73ac238390e32a7ff4ac8ebe4d6a41ecf5c9" ); |
156 | EXPECT_EQ(Spec.GetUUID(), Uuid); |
157 | } |
158 | |
159 | TEST_F(ObjectFileELFTest, GetModuleSpecifications_OffsetSizeWithNormalFile) { |
160 | std::string SO = GetInputFilePath(name: "liboffset-test.so" ); |
161 | ModuleSpecList Specs; |
162 | ASSERT_EQ(1u, ObjectFile::GetModuleSpecifications(FileSpec(SO), 0, 0, Specs)); |
163 | ModuleSpec Spec; |
164 | ASSERT_TRUE(Specs.GetModuleSpecAtIndex(0, Spec)) ; |
165 | UUID Uuid; |
166 | Uuid.SetFromStringRef("7D6E4738" ); |
167 | EXPECT_EQ(Spec.GetUUID(), Uuid); |
168 | EXPECT_EQ(Spec.GetObjectOffset(), 0UL); |
169 | EXPECT_EQ(Spec.GetObjectSize(), 3600UL); |
170 | EXPECT_EQ(FileSystem::Instance().GetByteSize(FileSpec(SO)), 3600UL); |
171 | } |
172 | |
173 | TEST_F(ObjectFileELFTest, GetModuleSpecifications_OffsetSizeWithOffsetFile) { |
174 | // The contents of offset-test.bin are |
175 | // - 0-1023: \0 |
176 | // - 1024-4623: liboffset-test.so (offset: 1024, size: 3600, CRC32: 7D6E4738) |
177 | // - 4624-4639: \0 |
178 | std::string SO = GetInputFilePath(name: "offset-test.bin" ); |
179 | ModuleSpecList Specs; |
180 | ASSERT_EQ( |
181 | 1u, ObjectFile::GetModuleSpecifications(FileSpec(SO), 1024, 3600, Specs)); |
182 | ModuleSpec Spec; |
183 | ASSERT_TRUE(Specs.GetModuleSpecAtIndex(0, Spec)) ; |
184 | UUID Uuid; |
185 | Uuid.SetFromStringRef("7D6E4738" ); |
186 | EXPECT_EQ(Spec.GetUUID(), Uuid); |
187 | EXPECT_EQ(Spec.GetObjectOffset(), 1024UL); |
188 | EXPECT_EQ(Spec.GetObjectSize(), 3600UL); |
189 | EXPECT_EQ(FileSystem::Instance().GetByteSize(FileSpec(SO)), 4640UL); |
190 | } |
191 | |
192 | TEST_F(ObjectFileELFTest, GetSymtab_NoSymEntryPointArmThumbAddressClass) { |
193 | /* |
194 | // nosym-entrypoint-arm-thumb.s |
195 | .thumb_func |
196 | _start: |
197 | mov r0, #42 |
198 | mov r7, #1 |
199 | svc #0 |
200 | // arm-linux-androideabi-as nosym-entrypoint-arm-thumb.s |
201 | // -o nosym-entrypoint-arm-thumb.o |
202 | // arm-linux-androideabi-ld nosym-entrypoint-arm-thumb.o |
203 | // -o nosym-entrypoint-arm-thumb -e 0x8075 -s |
204 | */ |
205 | auto ExpectedFile = TestFile::fromYaml(Yaml: R"( |
206 | --- !ELF |
207 | FileHeader: |
208 | Class: ELFCLASS32 |
209 | Data: ELFDATA2LSB |
210 | Type: ET_EXEC |
211 | Machine: EM_ARM |
212 | Flags: [ EF_ARM_SOFT_FLOAT, EF_ARM_EABI_VER5 ] |
213 | Entry: 0x0000000000008075 |
214 | Sections: |
215 | - Name: .text |
216 | Type: SHT_PROGBITS |
217 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
218 | Address: 0x0000000000008074 |
219 | AddressAlign: 0x0000000000000002 |
220 | Content: 2A20012700DF |
221 | - Name: .data |
222 | Type: SHT_PROGBITS |
223 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
224 | Address: 0x0000000000009000 |
225 | AddressAlign: 0x0000000000000001 |
226 | Content: '' |
227 | - Name: .bss |
228 | Type: SHT_NOBITS |
229 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
230 | Address: 0x0000000000009000 |
231 | AddressAlign: 0x0000000000000001 |
232 | - Name: .note.gnu.gold-version |
233 | Type: SHT_NOTE |
234 | AddressAlign: 0x0000000000000004 |
235 | Content: 040000000900000004000000474E5500676F6C6420312E3131000000 |
236 | - Name: .ARM.attributes |
237 | Type: SHT_ARM_ATTRIBUTES |
238 | AddressAlign: 0x0000000000000001 |
239 | Content: '4113000000616561626900010900000006020901' |
240 | ... |
241 | )" ); |
242 | ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
243 | |
244 | auto module_sp = std::make_shared<Module>(args: ExpectedFile->moduleSpec()); |
245 | |
246 | auto entry_point_addr = module_sp->GetObjectFile()->GetEntryPointAddress(); |
247 | ASSERT_TRUE(entry_point_addr.GetOffset() & 1); |
248 | // Decrease the offsite by 1 to make it into a breakable address since this |
249 | // is Thumb. |
250 | entry_point_addr.SetOffset(entry_point_addr.GetOffset() - 1); |
251 | ASSERT_EQ(entry_point_addr.GetAddressClass(), |
252 | AddressClass::eCodeAlternateISA); |
253 | } |
254 | |
255 | TEST_F(ObjectFileELFTest, GetSymtab_NoSymEntryPointArmAddressClass) { |
256 | /* |
257 | // nosym-entrypoint-arm.s |
258 | _start: |
259 | movs r0, #42 |
260 | movs r7, #1 |
261 | svc #0 |
262 | // arm-linux-androideabi-as nosym-entrypoint-arm.s |
263 | // -o nosym-entrypoint-arm.o |
264 | // arm-linux-androideabi-ld nosym-entrypoint-arm.o |
265 | // -o nosym-entrypoint-arm -e 0x8074 -s |
266 | */ |
267 | auto ExpectedFile = TestFile::fromYaml(Yaml: R"( |
268 | --- !ELF |
269 | FileHeader: |
270 | Class: ELFCLASS32 |
271 | Data: ELFDATA2LSB |
272 | Type: ET_EXEC |
273 | Machine: EM_ARM |
274 | Flags: [ EF_ARM_SOFT_FLOAT, EF_ARM_EABI_VER5 ] |
275 | Entry: 0x0000000000008074 |
276 | Sections: |
277 | - Name: .text |
278 | Type: SHT_PROGBITS |
279 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
280 | Address: 0x0000000000008074 |
281 | AddressAlign: 0x0000000000000004 |
282 | Content: 2A00A0E30170A0E3000000EF |
283 | - Name: .data |
284 | Type: SHT_PROGBITS |
285 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
286 | Address: 0x0000000000009000 |
287 | AddressAlign: 0x0000000000000001 |
288 | Content: '' |
289 | - Name: .bss |
290 | Type: SHT_NOBITS |
291 | Flags: [ SHF_WRITE, SHF_ALLOC ] |
292 | Address: 0x0000000000009000 |
293 | AddressAlign: 0x0000000000000001 |
294 | - Name: .note.gnu.gold-version |
295 | Type: SHT_NOTE |
296 | AddressAlign: 0x0000000000000004 |
297 | Content: 040000000900000004000000474E5500676F6C6420312E3131000000 |
298 | - Name: .ARM.attributes |
299 | Type: SHT_ARM_ATTRIBUTES |
300 | AddressAlign: 0x0000000000000001 |
301 | Content: '4113000000616561626900010900000006010801' |
302 | ... |
303 | )" ); |
304 | ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); |
305 | |
306 | auto module_sp = std::make_shared<Module>(args: ExpectedFile->moduleSpec()); |
307 | |
308 | auto entry_point_addr = module_sp->GetObjectFile()->GetEntryPointAddress(); |
309 | ASSERT_EQ(entry_point_addr.GetAddressClass(), AddressClass::eCode); |
310 | } |
311 | |