1 | //===- ELFObjectFileTest.cpp - Tests for ELFObjectFile --------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/Object/ELFObjectFile.h" |
10 | #include "llvm/ADT/STLExtras.h" |
11 | #include "llvm/ObjectYAML/yaml2obj.h" |
12 | #include "llvm/Support/BlockFrequency.h" |
13 | #include "llvm/Support/MemoryBuffer.h" |
14 | #include "llvm/Support/YAMLTraits.h" |
15 | #include "llvm/Testing/Support/Error.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | #include "llvm/Support/thread.h" |
19 | #include "llvm/TargetParser/Host.h" |
20 | |
21 | using namespace llvm; |
22 | using namespace llvm::object; |
23 | |
24 | namespace { |
25 | |
26 | // A struct to initialize a buffer to represent an ELF object file. |
27 | struct DataForTest { |
28 | std::vector<uint8_t> Data; |
29 | |
30 | template <typename T> |
31 | std::vector<uint8_t> makeElfData(uint8_t Class, uint8_t Encoding, |
32 | uint16_t Machine, uint8_t OS, |
33 | uint16_t Flags) { |
34 | T Ehdr{}; // Zero-initialise the header. |
35 | Ehdr.e_ident[ELF::EI_MAG0] = 0x7f; |
36 | Ehdr.e_ident[ELF::EI_MAG1] = 'E'; |
37 | Ehdr.e_ident[ELF::EI_MAG2] = 'L'; |
38 | Ehdr.e_ident[ELF::EI_MAG3] = 'F'; |
39 | Ehdr.e_ident[ELF::EI_CLASS] = Class; |
40 | Ehdr.e_ident[ELF::EI_DATA] = Encoding; |
41 | Ehdr.e_ident[ELF::EI_VERSION] = 1; |
42 | Ehdr.e_ident[ELF::EI_OSABI] = OS; |
43 | Ehdr.e_type = ELF::ET_REL; |
44 | Ehdr.e_machine = Machine; |
45 | Ehdr.e_version = 1; |
46 | Ehdr.e_flags = Flags; |
47 | Ehdr.e_ehsize = sizeof(T); |
48 | |
49 | bool IsLittleEndian = Encoding == ELF::ELFDATA2LSB; |
50 | if (sys::IsLittleEndianHost != IsLittleEndian) { |
51 | sys::swapByteOrder(Ehdr.e_type); |
52 | sys::swapByteOrder(Ehdr.e_machine); |
53 | sys::swapByteOrder(Ehdr.e_version); |
54 | sys::swapByteOrder(Ehdr.e_ehsize); |
55 | } |
56 | |
57 | uint8_t *EhdrBytes = reinterpret_cast<uint8_t *>(&Ehdr); |
58 | std::vector<uint8_t> Bytes; |
59 | std::copy(first: EhdrBytes, last: EhdrBytes + sizeof(Ehdr), result: std::back_inserter(x&: Bytes)); |
60 | return Bytes; |
61 | } |
62 | |
63 | DataForTest(uint8_t Class, uint8_t Encoding, uint16_t Machine, |
64 | uint8_t OS = ELF::ELFOSABI_NONE, uint16_t Flags = 0) { |
65 | if (Class == ELF::ELFCLASS64) |
66 | Data = makeElfData<ELF::Elf64_Ehdr>(Class, Encoding, Machine, OS, Flags); |
67 | else { |
68 | assert(Class == ELF::ELFCLASS32); |
69 | Data = makeElfData<ELF::Elf32_Ehdr>(Class, Encoding, Machine, OS, Flags); |
70 | } |
71 | } |
72 | }; |
73 | |
74 | void checkFormatAndArch(const DataForTest &D, StringRef Fmt, |
75 | Triple::ArchType Arch) { |
76 | Expected<std::unique_ptr<ObjectFile>> ELFObjOrErr = |
77 | object::ObjectFile::createELFObjectFile( |
78 | Object: MemoryBufferRef(toStringRef(Input: D.Data), "dummyELF" )); |
79 | ASSERT_THAT_EXPECTED(ELFObjOrErr, Succeeded()); |
80 | |
81 | const ObjectFile &File = *(*ELFObjOrErr).get(); |
82 | EXPECT_EQ(Fmt, File.getFileFormatName()); |
83 | EXPECT_EQ(Arch, File.getArch()); |
84 | } |
85 | |
86 | std::array<DataForTest, 4> generateData(uint16_t Machine) { |
87 | return {DataForTest(ELF::ELFCLASS32, ELF::ELFDATA2LSB, Machine), |
88 | DataForTest(ELF::ELFCLASS32, ELF::ELFDATA2MSB, Machine), |
89 | DataForTest(ELF::ELFCLASS64, ELF::ELFDATA2LSB, Machine), |
90 | DataForTest(ELF::ELFCLASS64, ELF::ELFDATA2MSB, Machine)}; |
91 | } |
92 | |
93 | } // namespace |
94 | |
95 | TEST(ELFObjectFileTest, MachineTestForNoneOrUnused) { |
96 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
97 | "elf64-unknown" , "elf64-unknown" }; |
98 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_NONE))) |
99 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::UnknownArch); |
100 | |
101 | // Test an arbitrary unused EM_* value (255). |
102 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: 255))) |
103 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::UnknownArch); |
104 | } |
105 | |
106 | TEST(ELFObjectFileTest, MachineTestForVE) { |
107 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
108 | "elf64-ve" , "elf64-ve" }; |
109 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_VE))) |
110 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::ve); |
111 | } |
112 | |
113 | TEST(ELFObjectFileTest, MachineTestForX86_64) { |
114 | std::array<StringRef, 4> Formats = {"elf32-x86-64" , "elf32-x86-64" , |
115 | "elf64-x86-64" , "elf64-x86-64" }; |
116 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_X86_64))) |
117 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::x86_64); |
118 | } |
119 | |
120 | TEST(ELFObjectFileTest, MachineTestFor386) { |
121 | std::array<StringRef, 4> Formats = {"elf32-i386" , "elf32-i386" , "elf64-i386" , |
122 | "elf64-i386" }; |
123 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_386))) |
124 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::x86); |
125 | } |
126 | |
127 | TEST(ELFObjectFileTest, MachineTestForMIPS) { |
128 | std::array<StringRef, 4> Formats = {"elf32-mips" , "elf32-mips" , "elf64-mips" , |
129 | "elf64-mips" }; |
130 | std::array<Triple::ArchType, 4> Archs = {Triple::mipsel, Triple::mips, |
131 | Triple::mips64el, Triple::mips64}; |
132 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_MIPS))) |
133 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
134 | } |
135 | |
136 | TEST(ELFObjectFileTest, MachineTestForAMDGPU) { |
137 | std::array<StringRef, 4> Formats = {"elf32-amdgpu" , "elf32-amdgpu" , |
138 | "elf64-amdgpu" , "elf64-amdgpu" }; |
139 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_AMDGPU))) |
140 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::UnknownArch); |
141 | } |
142 | |
143 | TEST(ELFObjectFileTest, MachineTestForIAMCU) { |
144 | std::array<StringRef, 4> Formats = {"elf32-iamcu" , "elf32-iamcu" , |
145 | "elf64-unknown" , "elf64-unknown" }; |
146 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_IAMCU))) |
147 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::x86); |
148 | } |
149 | |
150 | TEST(ELFObjectFileTest, MachineTestForAARCH64) { |
151 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
152 | "elf64-littleaarch64" , |
153 | "elf64-bigaarch64" }; |
154 | std::array<Triple::ArchType, 4> Archs = {Triple::aarch64, Triple::aarch64_be, |
155 | Triple::aarch64, Triple::aarch64_be}; |
156 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_AARCH64))) |
157 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
158 | } |
159 | |
160 | TEST(ELFObjectFileTest, MachineTestForPPC64) { |
161 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
162 | "elf64-powerpcle" , "elf64-powerpc" }; |
163 | std::array<Triple::ArchType, 4> Archs = {Triple::ppc64le, Triple::ppc64, |
164 | Triple::ppc64le, Triple::ppc64}; |
165 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_PPC64))) |
166 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
167 | } |
168 | |
169 | TEST(ELFObjectFileTest, MachineTestForPPC) { |
170 | std::array<StringRef, 4> Formats = {"elf32-powerpcle" , "elf32-powerpc" , |
171 | "elf64-unknown" , "elf64-unknown" }; |
172 | std::array<Triple::ArchType, 4> Archs = {Triple::ppcle, Triple::ppc, |
173 | Triple::ppcle, Triple::ppc}; |
174 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_PPC))) |
175 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
176 | } |
177 | |
178 | TEST(ELFObjectFileTest, MachineTestForRISCV) { |
179 | std::array<StringRef, 4> Formats = {"elf32-littleriscv" , "elf32-littleriscv" , |
180 | "elf64-littleriscv" , "elf64-littleriscv" }; |
181 | std::array<Triple::ArchType, 4> Archs = {Triple::riscv32, Triple::riscv32, |
182 | Triple::riscv64, Triple::riscv64}; |
183 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_RISCV))) |
184 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
185 | } |
186 | |
187 | TEST(ELFObjectFileTest, MachineTestForARM) { |
188 | std::array<StringRef, 4> Formats = {"elf32-littlearm" , "elf32-bigarm" , |
189 | "elf64-unknown" , "elf64-unknown" }; |
190 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_ARM))) |
191 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::arm); |
192 | } |
193 | |
194 | TEST(ELFObjectFileTest, MachineTestForS390) { |
195 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
196 | "elf64-s390" , "elf64-s390" }; |
197 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_S390))) |
198 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::systemz); |
199 | } |
200 | |
201 | TEST(ELFObjectFileTest, MachineTestForSPARCV9) { |
202 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
203 | "elf64-sparc" , "elf64-sparc" }; |
204 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_SPARCV9))) |
205 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::sparcv9); |
206 | } |
207 | |
208 | TEST(ELFObjectFileTest, MachineTestForSPARC) { |
209 | std::array<StringRef, 4> Formats = {"elf32-sparc" , "elf32-sparc" , |
210 | "elf64-unknown" , "elf64-unknown" }; |
211 | std::array<Triple::ArchType, 4> Archs = {Triple::sparcel, Triple::sparc, |
212 | Triple::sparcel, Triple::sparc}; |
213 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_SPARC))) |
214 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
215 | } |
216 | |
217 | TEST(ELFObjectFileTest, MachineTestForSPARC32PLUS) { |
218 | std::array<StringRef, 4> Formats = {"elf32-sparc" , "elf32-sparc" , |
219 | "elf64-unknown" , "elf64-unknown" }; |
220 | std::array<Triple::ArchType, 4> Archs = {Triple::sparcel, Triple::sparc, |
221 | Triple::sparcel, Triple::sparc}; |
222 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_SPARC32PLUS))) |
223 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
224 | } |
225 | |
226 | TEST(ELFObjectFileTest, MachineTestForBPF) { |
227 | std::array<StringRef, 4> Formats = {"elf32-unknown" , "elf32-unknown" , |
228 | "elf64-bpf" , "elf64-bpf" }; |
229 | std::array<Triple::ArchType, 4> Archs = {Triple::bpfel, Triple::bpfeb, |
230 | Triple::bpfel, Triple::bpfeb}; |
231 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_BPF))) |
232 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
233 | } |
234 | |
235 | TEST(ELFObjectFileTest, MachineTestForAVR) { |
236 | std::array<StringRef, 4> Formats = {"elf32-avr" , "elf32-avr" , "elf64-unknown" , |
237 | "elf64-unknown" }; |
238 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_AVR))) |
239 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::avr); |
240 | } |
241 | |
242 | TEST(ELFObjectFileTest, MachineTestForHEXAGON) { |
243 | std::array<StringRef, 4> Formats = {"elf32-hexagon" , "elf32-hexagon" , |
244 | "elf64-unknown" , "elf64-unknown" }; |
245 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_HEXAGON))) |
246 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::hexagon); |
247 | } |
248 | |
249 | TEST(ELFObjectFileTest, MachineTestForLANAI) { |
250 | std::array<StringRef, 4> Formats = {"elf32-lanai" , "elf32-lanai" , |
251 | "elf64-unknown" , "elf64-unknown" }; |
252 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_LANAI))) |
253 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::lanai); |
254 | } |
255 | |
256 | TEST(ELFObjectFileTest, MachineTestForMSP430) { |
257 | std::array<StringRef, 4> Formats = {"elf32-msp430" , "elf32-msp430" , |
258 | "elf64-unknown" , "elf64-unknown" }; |
259 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_MSP430))) |
260 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::msp430); |
261 | } |
262 | |
263 | TEST(ELFObjectFileTest, MachineTestForLoongArch) { |
264 | std::array<StringRef, 4> Formats = {"elf32-loongarch" , "elf32-loongarch" , |
265 | "elf64-loongarch" , "elf64-loongarch" }; |
266 | std::array<Triple::ArchType, 4> Archs = { |
267 | Triple::loongarch32, Triple::loongarch32, Triple::loongarch64, |
268 | Triple::loongarch64}; |
269 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_LOONGARCH))) |
270 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Archs[Idx]); |
271 | } |
272 | |
273 | TEST(ELFObjectFileTest, MachineTestForCSKY) { |
274 | std::array<StringRef, 4> Formats = {"elf32-csky" , "elf32-csky" , |
275 | "elf64-unknown" , "elf64-unknown" }; |
276 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_CSKY))) |
277 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::csky); |
278 | } |
279 | |
280 | TEST(ELFObjectFileTest, MachineTestForXtensa) { |
281 | std::array<StringRef, 4> Formats = {"elf32-xtensa" , "elf32-xtensa" , |
282 | "elf64-unknown" , "elf64-unknown" }; |
283 | for (auto [Idx, Data] : enumerate(First: generateData(Machine: ELF::EM_XTENSA))) |
284 | checkFormatAndArch(D: Data, Fmt: Formats[Idx], Arch: Triple::xtensa); |
285 | } |
286 | |
287 | TEST(ELFObjectFileTest, CheckOSAndTriple) { |
288 | std::tuple<uint16_t, uint8_t, StringRef> Formats[] = { |
289 | {ELF::EM_AMDGPU, ELF::ELFOSABI_AMDGPU_HSA, "amdgcn-amd-amdhsa" }, |
290 | {ELF::EM_X86_64, ELF::ELFOSABI_LINUX, "x86_64--linux" }, |
291 | {ELF::EM_X86_64, ELF::ELFOSABI_NETBSD, "x86_64--netbsd" }, |
292 | {ELF::EM_X86_64, ELF::ELFOSABI_HURD, "x86_64--hurd" }, |
293 | {ELF::EM_X86_64, ELF::ELFOSABI_SOLARIS, "x86_64--solaris" }, |
294 | {ELF::EM_X86_64, ELF::ELFOSABI_AIX, "x86_64--aix" }, |
295 | {ELF::EM_X86_64, ELF::ELFOSABI_FREEBSD, "x86_64--freebsd" }, |
296 | {ELF::EM_X86_64, ELF::ELFOSABI_OPENBSD, "x86_64--openbsd" }, |
297 | {ELF::EM_CUDA, ELF::ELFOSABI_CUDA, "nvptx64-nvidia-cuda" }}; |
298 | for (auto [Machine, OS, Triple] : Formats) { |
299 | const DataForTest D(ELF::ELFCLASS64, ELF::ELFDATA2LSB, Machine, OS, |
300 | ELF::EF_AMDGPU_MACH_AMDGCN_LAST); |
301 | Expected<ELF64LEObjectFile> ELFObjOrErr = ELF64LEObjectFile::create( |
302 | Object: MemoryBufferRef(toStringRef(Input: D.Data), "dummyELF" )); |
303 | ASSERT_THAT_EXPECTED(ELFObjOrErr, Succeeded()); |
304 | |
305 | auto &ELFObj = *ELFObjOrErr; |
306 | llvm::Triple TheTriple = ELFObj.makeTriple(); |
307 | |
308 | // The AMDGPU architecture will be unknown on big-endian testers. |
309 | if (TheTriple.getArch() == Triple::UnknownArch) |
310 | continue; |
311 | |
312 | EXPECT_EQ(Triple, TheTriple.getTriple()); |
313 | } |
314 | } |
315 | |
316 | // ELF relative relocation type test. |
317 | TEST(ELFObjectFileTest, RelativeRelocationTypeTest) { |
318 | EXPECT_EQ(ELF::R_CKCORE_RELATIVE, getELFRelativeRelocationType(ELF::EM_CSKY)); |
319 | } |
320 | |
321 | template <class ELFT> |
322 | static Expected<ELFObjectFile<ELFT>> toBinary(SmallVectorImpl<char> &Storage, |
323 | StringRef Yaml) { |
324 | raw_svector_ostream OS(Storage); |
325 | yaml::Input YIn(Yaml); |
326 | if (!yaml::convertYAML(YIn, Out&: OS, ErrHandler: [](const Twine &Msg) {})) |
327 | return createStringError(EC: std::errc::invalid_argument, |
328 | Fmt: "unable to convert YAML" ); |
329 | return ELFObjectFile<ELFT>::create(MemoryBufferRef(OS.str(), "dummyELF" )); |
330 | } |
331 | |
332 | // Check we are able to create an ELFObjectFile even when the content of the |
333 | // SHT_SYMTAB_SHNDX section can't be read properly. |
334 | TEST(ELFObjectFileTest, InvalidSymtabShndxTest) { |
335 | SmallString<0> Storage; |
336 | Expected<ELFObjectFile<ELF64LE>> ExpectedFile = toBinary<ELF64LE>(Storage, Yaml: R"( |
337 | --- !ELF |
338 | FileHeader: |
339 | Class: ELFCLASS64 |
340 | Data: ELFDATA2LSB |
341 | Type: ET_REL |
342 | Sections: |
343 | - Name: .symtab_shndx |
344 | Type: SHT_SYMTAB_SHNDX |
345 | Entries: [ 0 ] |
346 | ShSize: 0xFFFFFFFF |
347 | )" ); |
348 | |
349 | ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); |
350 | } |
351 | |
352 | // Test that we are able to create an ELFObjectFile even when loadable segments |
353 | // are unsorted by virtual address. |
354 | // Test that ELFFile<ELFT>::toMappedAddr works properly in this case. |
355 | |
356 | TEST(ELFObjectFileTest, InvalidLoadSegmentsOrderTest) { |
357 | SmallString<0> Storage; |
358 | Expected<ELFObjectFile<ELF64LE>> ExpectedFile = toBinary<ELF64LE>(Storage, Yaml: R"( |
359 | --- !ELF |
360 | FileHeader: |
361 | Class: ELFCLASS64 |
362 | Data: ELFDATA2LSB |
363 | Type: ET_EXEC |
364 | Sections: |
365 | - Name: .foo |
366 | Type: SHT_PROGBITS |
367 | Address: 0x1000 |
368 | Offset: 0x3000 |
369 | ContentArray: [ 0x11 ] |
370 | - Name: .bar |
371 | Type: SHT_PROGBITS |
372 | Address: 0x2000 |
373 | Offset: 0x4000 |
374 | ContentArray: [ 0x99 ] |
375 | ProgramHeaders: |
376 | - Type: PT_LOAD |
377 | VAddr: 0x2000 |
378 | FirstSec: .bar |
379 | LastSec: .bar |
380 | - Type: PT_LOAD |
381 | VAddr: 0x1000 |
382 | FirstSec: .foo |
383 | LastSec: .foo |
384 | )" ); |
385 | |
386 | ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); |
387 | |
388 | std::string WarnString; |
389 | auto ToMappedAddr = [&](uint64_t Addr) -> const uint8_t * { |
390 | Expected<const uint8_t *> DataOrErr = |
391 | ExpectedFile->getELFFile().toMappedAddr(VAddr: Addr, WarnHandler: [&](const Twine &Msg) { |
392 | EXPECT_TRUE(WarnString.empty()); |
393 | WarnString = Msg.str(); |
394 | return Error::success(); |
395 | }); |
396 | |
397 | if (!DataOrErr) { |
398 | ADD_FAILURE() << toString(E: DataOrErr.takeError()); |
399 | return nullptr; |
400 | } |
401 | |
402 | EXPECT_TRUE(WarnString == |
403 | "loadable segments are unsorted by virtual address" ); |
404 | WarnString = "" ; |
405 | return *DataOrErr; |
406 | }; |
407 | |
408 | const uint8_t *Data = ToMappedAddr(0x1000); |
409 | ASSERT_TRUE(Data); |
410 | MemoryBufferRef Buf = ExpectedFile->getMemoryBufferRef(); |
411 | EXPECT_EQ((const char *)Data - Buf.getBufferStart(), 0x3000); |
412 | EXPECT_EQ(Data[0], 0x11); |
413 | |
414 | Data = ToMappedAddr(0x2000); |
415 | ASSERT_TRUE(Data); |
416 | Buf = ExpectedFile->getMemoryBufferRef(); |
417 | EXPECT_EQ((const char *)Data - Buf.getBufferStart(), 0x4000); |
418 | EXPECT_EQ(Data[0], 0x99); |
419 | } |
420 | |
421 | // This is a test for API that is related to symbols. |
422 | // We check that errors are properly reported here. |
423 | TEST(ELFObjectFileTest, InvalidSymbolTest) { |
424 | SmallString<0> Storage; |
425 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = toBinary<ELF64LE>(Storage, Yaml: R"( |
426 | --- !ELF |
427 | FileHeader: |
428 | Class: ELFCLASS64 |
429 | Data: ELFDATA2LSB |
430 | Type: ET_DYN |
431 | Machine: EM_X86_64 |
432 | Sections: |
433 | - Name: .symtab |
434 | Type: SHT_SYMTAB |
435 | )" ); |
436 | |
437 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
438 | const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile(); |
439 | const ELFObjectFile<ELF64LE> &Obj = *ElfOrErr; |
440 | |
441 | Expected<const typename ELF64LE::Shdr *> SymtabSecOrErr = Elf.getSection(Index: 1); |
442 | ASSERT_THAT_EXPECTED(SymtabSecOrErr, Succeeded()); |
443 | ASSERT_EQ((*SymtabSecOrErr)->sh_type, ELF::SHT_SYMTAB); |
444 | |
445 | auto DoCheck = [&](unsigned BrokenSymIndex, const char *ErrMsg) { |
446 | ELFSymbolRef BrokenSym = Obj.toSymbolRef(SymTable: *SymtabSecOrErr, SymbolNum: BrokenSymIndex); |
447 | |
448 | // 1) Check the behavior of ELFObjectFile<ELFT>::getSymbolName(). |
449 | // SymbolRef::getName() calls it internally. We can't test it directly, |
450 | // because it is protected. |
451 | EXPECT_THAT_ERROR(BrokenSym.getName().takeError(), |
452 | FailedWithMessage(ErrMsg)); |
453 | |
454 | // 2) Check the behavior of ELFObjectFile<ELFT>::getSymbol(). |
455 | EXPECT_THAT_ERROR(Obj.getSymbol(BrokenSym.getRawDataRefImpl()).takeError(), |
456 | FailedWithMessage(ErrMsg)); |
457 | |
458 | // 3) Check the behavior of ELFObjectFile<ELFT>::getSymbolSection(). |
459 | // SymbolRef::getSection() calls it internally. We can't test it |
460 | // directly, because it is protected. |
461 | EXPECT_THAT_ERROR(BrokenSym.getSection().takeError(), |
462 | FailedWithMessage(ErrMsg)); |
463 | |
464 | // 4) Check the behavior of ELFObjectFile<ELFT>::getSymbolFlags(). |
465 | // SymbolRef::getFlags() calls it internally. We can't test it directly, |
466 | // because it is protected. |
467 | EXPECT_THAT_ERROR(BrokenSym.getFlags().takeError(), |
468 | FailedWithMessage(ErrMsg)); |
469 | |
470 | // 5) Check the behavior of ELFObjectFile<ELFT>::getSymbolType(). |
471 | // SymbolRef::getType() calls it internally. We can't test it directly, |
472 | // because it is protected. |
473 | EXPECT_THAT_ERROR(BrokenSym.getType().takeError(), |
474 | FailedWithMessage(ErrMsg)); |
475 | |
476 | // 6) Check the behavior of ELFObjectFile<ELFT>::getSymbolAddress(). |
477 | // SymbolRef::getAddress() calls it internally. We can't test it |
478 | // directly, because it is protected. |
479 | EXPECT_THAT_ERROR(BrokenSym.getAddress().takeError(), |
480 | FailedWithMessage(ErrMsg)); |
481 | |
482 | // Finally, check the `ELFFile<ELFT>::getEntry` API. This is an underlying |
483 | // method that generates errors for all cases above. |
484 | EXPECT_THAT_EXPECTED( |
485 | Elf.getEntry<typename ELF64LE::Sym>(**SymtabSecOrErr, 0), Succeeded()); |
486 | EXPECT_THAT_ERROR( |
487 | Elf.getEntry<typename ELF64LE::Sym>(**SymtabSecOrErr, BrokenSymIndex) |
488 | .takeError(), |
489 | FailedWithMessage(ErrMsg)); |
490 | }; |
491 | |
492 | // We create a symbol with an index that is too large to exist in the symbol |
493 | // table. |
494 | DoCheck(0x1, "can't read an entry at 0x18: it goes past the end of the " |
495 | "section (0x18)" ); |
496 | |
497 | // We create a symbol with an index that is too large to exist in the object. |
498 | DoCheck(0xFFFFFFFF, "can't read an entry at 0x17ffffffe8: it goes past the " |
499 | "end of the section (0x18)" ); |
500 | } |
501 | |
502 | // Tests for error paths of the ELFFile::decodeBBAddrMap API. |
503 | TEST(ELFObjectFileTest, InvalidDecodeBBAddrMap) { |
504 | StringRef CommonYamlString(R"( |
505 | --- !ELF |
506 | FileHeader: |
507 | Class: ELFCLASS64 |
508 | Data: ELFDATA2LSB |
509 | Type: ET_EXEC |
510 | Sections: |
511 | - Type: SHT_LLVM_BB_ADDR_MAP |
512 | Name: .llvm_bb_addr_map |
513 | Entries: |
514 | )" ); |
515 | |
516 | auto DoCheck = [&](StringRef YamlString, const char *ErrMsg) { |
517 | SmallString<0> Storage; |
518 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
519 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
520 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
521 | const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile(); |
522 | |
523 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
524 | Elf.getSection(Index: 1); |
525 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
526 | EXPECT_THAT_ERROR(Elf.decodeBBAddrMap(**BBAddrMapSecOrErr).takeError(), |
527 | FailedWithMessage(ErrMsg)); |
528 | }; |
529 | |
530 | // Check that we can detect unsupported versions. |
531 | SmallString<128> UnsupportedVersionYamlString(CommonYamlString); |
532 | UnsupportedVersionYamlString += R"( |
533 | - Version: 3 |
534 | BBRanges: |
535 | - BaseAddress: 0x11111 |
536 | BBEntries: |
537 | - AddressOffset: 0x0 |
538 | Size: 0x1 |
539 | Metadata: 0x2 |
540 | )" ; |
541 | |
542 | { |
543 | SCOPED_TRACE("unsupported version" ); |
544 | DoCheck(UnsupportedVersionYamlString, |
545 | "unsupported SHT_LLVM_BB_ADDR_MAP version: 3" ); |
546 | } |
547 | |
548 | SmallString<128> ZeroBBRangesYamlString(CommonYamlString); |
549 | ZeroBBRangesYamlString += R"( |
550 | - Version: 2 |
551 | Feature: 0x8 |
552 | BBRanges: [] |
553 | )" ; |
554 | { |
555 | SCOPED_TRACE("zero bb ranges" ); |
556 | DoCheck(ZeroBBRangesYamlString, |
557 | "invalid zero number of BB ranges at offset 3 in " |
558 | "SHT_LLVM_BB_ADDR_MAP section with index 1" ); |
559 | } |
560 | |
561 | SmallString<128> CommonVersionedYamlString(CommonYamlString); |
562 | CommonVersionedYamlString += R"( |
563 | - Version: 2 |
564 | BBRanges: |
565 | - BaseAddress: 0x11111 |
566 | BBEntries: |
567 | - AddressOffset: 0x0 |
568 | Size: 0x1 |
569 | Metadata: 0x2 |
570 | )" ; |
571 | |
572 | // Check that we can detect the malformed encoding when the section is |
573 | // truncated. |
574 | SmallString<128> TruncatedYamlString(CommonVersionedYamlString); |
575 | TruncatedYamlString += R"( |
576 | ShSize: 0xb |
577 | )" ; |
578 | { |
579 | SCOPED_TRACE("truncated section" ); |
580 | DoCheck(TruncatedYamlString, |
581 | "unable to decode LEB128 at offset 0x0000000b: " |
582 | "malformed uleb128, extends past end" ); |
583 | } |
584 | |
585 | // Check that we can detect when the encoded BB entry fields exceed the UINT32 |
586 | // limit. |
587 | SmallVector<SmallString<128>, 3> OverInt32LimitYamlStrings( |
588 | 3, CommonVersionedYamlString); |
589 | OverInt32LimitYamlStrings[0] += R"( |
590 | - ID: 1 |
591 | AddressOffset: 0x100000000 |
592 | Size: 0xFFFFFFFF |
593 | Metadata: 0xFFFFFFFF |
594 | )" ; |
595 | |
596 | OverInt32LimitYamlStrings[1] += R"( |
597 | - ID: 2 |
598 | AddressOffset: 0xFFFFFFFF |
599 | Size: 0x100000000 |
600 | Metadata: 0xFFFFFFFF |
601 | )" ; |
602 | |
603 | OverInt32LimitYamlStrings[2] += R"( |
604 | - ID: 3 |
605 | AddressOffset: 0xFFFFFFFF |
606 | Size: 0xFFFFFFFF |
607 | Metadata: 0x100000000 |
608 | )" ; |
609 | |
610 | { |
611 | SCOPED_TRACE("overlimit fields" ); |
612 | DoCheck(OverInt32LimitYamlStrings[0], |
613 | "ULEB128 value at offset 0x10 exceeds UINT32_MAX (0x100000000)" ); |
614 | DoCheck(OverInt32LimitYamlStrings[1], |
615 | "ULEB128 value at offset 0x15 exceeds UINT32_MAX (0x100000000)" ); |
616 | DoCheck(OverInt32LimitYamlStrings[2], |
617 | "ULEB128 value at offset 0x1a exceeds UINT32_MAX (0x100000000)" ); |
618 | } |
619 | |
620 | // Check the proper error handling when the section has fields exceeding |
621 | // UINT32 and is also truncated. This is for checking that we don't generate |
622 | // unhandled errors. |
623 | SmallVector<SmallString<128>, 3> OverInt32LimitAndTruncated( |
624 | 3, OverInt32LimitYamlStrings[1]); |
625 | // Truncate before the end of the 5-byte field. |
626 | OverInt32LimitAndTruncated[0] += R"( |
627 | ShSize: 0x19 |
628 | )" ; |
629 | // Truncate at the end of the 5-byte field. |
630 | OverInt32LimitAndTruncated[1] += R"( |
631 | ShSize: 0x1a |
632 | )" ; |
633 | // Truncate after the end of the 5-byte field. |
634 | OverInt32LimitAndTruncated[2] += R"( |
635 | ShSize: 0x1b |
636 | )" ; |
637 | |
638 | { |
639 | SCOPED_TRACE("overlimit fields, truncated section" ); |
640 | DoCheck(OverInt32LimitAndTruncated[0], |
641 | "unable to decode LEB128 at offset 0x00000015: malformed uleb128, " |
642 | "extends past end" ); |
643 | DoCheck(OverInt32LimitAndTruncated[1], |
644 | "ULEB128 value at offset 0x15 exceeds UINT32_MAX (0x100000000)" ); |
645 | DoCheck(OverInt32LimitAndTruncated[2], |
646 | "ULEB128 value at offset 0x15 exceeds UINT32_MAX (0x100000000)" ); |
647 | } |
648 | |
649 | // Check for proper error handling when the 'NumBlocks' field is overridden |
650 | // with an out-of-range value. |
651 | SmallString<128> OverLimitNumBlocks(CommonVersionedYamlString); |
652 | OverLimitNumBlocks += R"( |
653 | NumBlocks: 0x100000000 |
654 | )" ; |
655 | |
656 | { |
657 | SCOPED_TRACE("overlimit 'NumBlocks' field" ); |
658 | DoCheck(OverLimitNumBlocks, |
659 | "ULEB128 value at offset 0xa exceeds UINT32_MAX (0x100000000)" ); |
660 | } |
661 | |
662 | // Check for proper error handling when the 'NumBBRanges' field is overridden |
663 | // with an out-of-range value. |
664 | SmallString<128> OverLimitNumBBRanges(CommonVersionedYamlString); |
665 | OverLimitNumBBRanges += R"( |
666 | NumBBRanges: 0x100000000 |
667 | Feature: 0x8 |
668 | )" ; |
669 | DoCheck(OverLimitNumBBRanges, |
670 | "ULEB128 value at offset 0x2 exceeds UINT32_MAX (0x100000000)" ); |
671 | } |
672 | |
673 | // Test for the ELFObjectFile::readBBAddrMap API. |
674 | TEST(ELFObjectFileTest, ReadBBAddrMap) { |
675 | StringRef CommonYamlString(R"( |
676 | --- !ELF |
677 | FileHeader: |
678 | Class: ELFCLASS64 |
679 | Data: ELFDATA2LSB |
680 | Type: ET_EXEC |
681 | Sections: |
682 | - Name: .llvm_bb_addr_map_1 |
683 | Type: SHT_LLVM_BB_ADDR_MAP |
684 | Link: 1 |
685 | Entries: |
686 | - Version: 2 |
687 | BBRanges: |
688 | - BaseAddress: 0x11111 |
689 | BBEntries: |
690 | - ID: 1 |
691 | AddressOffset: 0x0 |
692 | Size: 0x1 |
693 | Metadata: 0x2 |
694 | - Name: .llvm_bb_addr_map_2 |
695 | Type: SHT_LLVM_BB_ADDR_MAP |
696 | Link: 1 |
697 | Entries: |
698 | - Version: 2 |
699 | Feature: 0x8 |
700 | BBRanges: |
701 | - BaseAddress: 0x22222 |
702 | BBEntries: |
703 | - ID: 2 |
704 | AddressOffset: 0x0 |
705 | Size: 0x2 |
706 | Metadata: 0x4 |
707 | - BaseAddress: 0xFFFFF |
708 | BBEntries: |
709 | - ID: 15 |
710 | AddressOffset: 0xF0 |
711 | Size: 0xF1 |
712 | Metadata: 0x1F |
713 | - Name: .llvm_bb_addr_map_3 |
714 | Type: SHT_LLVM_BB_ADDR_MAP |
715 | Link: 2 |
716 | Entries: |
717 | - Version: 1 |
718 | BBRanges: |
719 | - BaseAddress: 0x33333 |
720 | BBEntries: |
721 | - ID: 0 |
722 | AddressOffset: 0x0 |
723 | Size: 0x3 |
724 | Metadata: 0x6 |
725 | - Name: .llvm_bb_addr_map_4 |
726 | Type: SHT_LLVM_BB_ADDR_MAP |
727 | # Link: 0 (by default, can be overriden) |
728 | Entries: |
729 | - Version: 2 |
730 | BBRanges: |
731 | - BaseAddress: 0x44444 |
732 | BBEntries: |
733 | - ID: 0 |
734 | AddressOffset: 0x0 |
735 | Size: 0x4 |
736 | Metadata: 0x18 |
737 | )" ); |
738 | |
739 | BBAddrMap E1 = { |
740 | .BBRanges: {{.BaseAddress: 0x11111, .BBEntries: {{1, 0x0, 0x1, {.HasReturn: false, .HasTailCall: true, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
741 | BBAddrMap E2 = { |
742 | .BBRanges: {{.BaseAddress: 0x22222, .BBEntries: {{2, 0x0, 0x2, {.HasReturn: false, .HasTailCall: false, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}}}, |
743 | {.BaseAddress: 0xFFFFF, .BBEntries: {{15, 0xF0, 0xF1, {.HasReturn: true, .HasTailCall: true, .IsEHPad: true, .CanFallThrough: true, .HasIndirectBranch: true}}}}}}; |
744 | BBAddrMap E3 = { |
745 | .BBRanges: {{.BaseAddress: 0x33333, .BBEntries: {{0, 0x0, 0x3, {.HasReturn: false, .HasTailCall: true, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
746 | BBAddrMap E4 = { |
747 | .BBRanges: {{.BaseAddress: 0x44444, .BBEntries: {{0, 0x0, 0x4, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: true, .HasIndirectBranch: true}}}}}}; |
748 | |
749 | std::vector<BBAddrMap> Section0BBAddrMaps = {E4}; |
750 | std::vector<BBAddrMap> Section1BBAddrMaps = {E3}; |
751 | std::vector<BBAddrMap> Section2BBAddrMaps = {E1, E2}; |
752 | std::vector<BBAddrMap> AllBBAddrMaps = {E1, E2, E3, E4}; |
753 | |
754 | auto DoCheckSucceeds = [&](StringRef YamlString, |
755 | std::optional<unsigned> TextSectionIndex, |
756 | std::vector<BBAddrMap> ExpectedResult) { |
757 | SCOPED_TRACE("for TextSectionIndex: " + |
758 | (TextSectionIndex ? llvm::Twine(*TextSectionIndex) : "{}" ) + |
759 | " and object yaml:\n" + YamlString); |
760 | SmallString<0> Storage; |
761 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
762 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
763 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
764 | |
765 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
766 | ElfOrErr->getELFFile().getSection(Index: 1); |
767 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
768 | auto BBAddrMaps = ElfOrErr->readBBAddrMap(TextSectionIndex); |
769 | ASSERT_THAT_EXPECTED(BBAddrMaps, Succeeded()); |
770 | EXPECT_EQ(*BBAddrMaps, ExpectedResult); |
771 | }; |
772 | |
773 | auto DoCheckFails = [&](StringRef YamlString, |
774 | std::optional<unsigned> TextSectionIndex, |
775 | const char *ErrMsg) { |
776 | SCOPED_TRACE("for TextSectionIndex: " + |
777 | (TextSectionIndex ? llvm::Twine(*TextSectionIndex) : "{}" ) + |
778 | " and object yaml:\n" + YamlString); |
779 | SmallString<0> Storage; |
780 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
781 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
782 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
783 | |
784 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
785 | ElfOrErr->getELFFile().getSection(Index: 1); |
786 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
787 | EXPECT_THAT_ERROR(ElfOrErr->readBBAddrMap(TextSectionIndex).takeError(), |
788 | FailedWithMessage(ErrMsg)); |
789 | }; |
790 | |
791 | { |
792 | SCOPED_TRACE("normal sections" ); |
793 | // Check that we can retrieve the data in the normal case. |
794 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/std::nullopt, |
795 | AllBBAddrMaps); |
796 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/0, |
797 | Section0BBAddrMaps); |
798 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/2, |
799 | Section1BBAddrMaps); |
800 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/1, |
801 | Section2BBAddrMaps); |
802 | // Check that when no bb-address-map section is found for a text section, |
803 | // we return an empty result. |
804 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/3, {}); |
805 | } |
806 | |
807 | // Check that we detect when a bb-addr-map section is linked to an invalid |
808 | // (not present) section. |
809 | SmallString<128> InvalidLinkedYamlString(CommonYamlString); |
810 | InvalidLinkedYamlString += R"( |
811 | Link: 121 |
812 | )" ; |
813 | |
814 | DoCheckFails(InvalidLinkedYamlString, /*TextSectionIndex=*/4, |
815 | "unable to get the linked-to section for " |
816 | "SHT_LLVM_BB_ADDR_MAP section with index 4: invalid section " |
817 | "index: 121" ); |
818 | { |
819 | SCOPED_TRACE("invalid linked section" ); |
820 | // Linked sections are not checked when we don't target a specific text |
821 | // section. |
822 | DoCheckSucceeds(InvalidLinkedYamlString, /*TextSectionIndex=*/std::nullopt, |
823 | AllBBAddrMaps); |
824 | } |
825 | |
826 | // Check that we can detect when bb-address-map decoding fails. |
827 | SmallString<128> TruncatedYamlString(CommonYamlString); |
828 | TruncatedYamlString += R"( |
829 | ShSize: 0xa |
830 | )" ; |
831 | |
832 | { |
833 | SCOPED_TRACE("truncated section" ); |
834 | DoCheckFails(TruncatedYamlString, /*TextSectionIndex=*/std::nullopt, |
835 | "unable to read SHT_LLVM_BB_ADDR_MAP section with index 4: " |
836 | "unable to decode LEB128 at offset 0x0000000a: malformed " |
837 | "uleb128, extends past end" ); |
838 | |
839 | // Check that we can read the other section's bb-address-maps which are |
840 | // valid. |
841 | DoCheckSucceeds(TruncatedYamlString, /*TextSectionIndex=*/2, |
842 | Section1BBAddrMaps); |
843 | } |
844 | } |
845 | |
846 | // Tests for error paths of the ELFFile::decodeBBAddrMap with PGOAnalysisMap |
847 | // API. |
848 | TEST(ELFObjectFileTest, InvalidDecodePGOAnalysisMap) { |
849 | StringRef CommonYamlString(R"( |
850 | --- !ELF |
851 | FileHeader: |
852 | Class: ELFCLASS64 |
853 | Data: ELFDATA2LSB |
854 | Type: ET_EXEC |
855 | Sections: |
856 | - Type: SHT_LLVM_BB_ADDR_MAP |
857 | Name: .llvm_bb_addr_map |
858 | Entries: |
859 | )" ); |
860 | |
861 | auto DoCheck = [&](StringRef YamlString, const char *ErrMsg) { |
862 | SmallString<0> Storage; |
863 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
864 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
865 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
866 | const ELFFile<ELF64LE> &Elf = ElfOrErr->getELFFile(); |
867 | |
868 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
869 | Elf.getSection(Index: 1); |
870 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
871 | |
872 | std::vector<PGOAnalysisMap> PGOAnalyses; |
873 | EXPECT_THAT_ERROR( |
874 | Elf.decodeBBAddrMap(**BBAddrMapSecOrErr, nullptr, &PGOAnalyses) |
875 | .takeError(), |
876 | FailedWithMessage(ErrMsg)); |
877 | }; |
878 | |
879 | // Check that we can detect unsupported versions that are too old. |
880 | SmallString<128> UnsupportedLowVersionYamlString(CommonYamlString); |
881 | UnsupportedLowVersionYamlString += R"( |
882 | - Version: 1 |
883 | Feature: 0x4 |
884 | BBRanges: |
885 | - BBEntries: |
886 | - AddressOffset: 0x0 |
887 | Size: 0x1 |
888 | Metadata: 0x2 |
889 | )" ; |
890 | |
891 | { |
892 | SCOPED_TRACE("unsupported version" ); |
893 | DoCheck(UnsupportedLowVersionYamlString, |
894 | "version should be >= 2 for SHT_LLVM_BB_ADDR_MAP when PGO features " |
895 | "are enabled: version = 1 feature = 4" ); |
896 | } |
897 | |
898 | SmallString<128> CommonVersionedYamlString(CommonYamlString); |
899 | CommonVersionedYamlString += R"( |
900 | - Version: 2 |
901 | BBRanges: |
902 | - BBEntries: |
903 | - ID: 1 |
904 | AddressOffset: 0x0 |
905 | Size: 0x1 |
906 | Metadata: 0x2 |
907 | )" ; |
908 | |
909 | // Check that we fail when function entry count is enabled but not provided. |
910 | SmallString<128> MissingFuncEntryCount(CommonYamlString); |
911 | MissingFuncEntryCount += R"( |
912 | - Version: 2 |
913 | Feature: 0x01 |
914 | )" ; |
915 | |
916 | { |
917 | SCOPED_TRACE("missing function entry count" ); |
918 | DoCheck(MissingFuncEntryCount, |
919 | "unexpected end of data at offset 0x2 while reading [0x2, 0xa)" ); |
920 | } |
921 | |
922 | // Check that we fail when basic block frequency is enabled but not provided. |
923 | SmallString<128> MissingBBFreq(CommonYamlString); |
924 | MissingBBFreq += R"( |
925 | - Version: 2 |
926 | Feature: 0x02 |
927 | BBRanges: |
928 | - BBEntries: |
929 | - ID: 1 |
930 | AddressOffset: 0x0 |
931 | Size: 0x1 |
932 | Metadata: 0x2 |
933 | )" ; |
934 | |
935 | { |
936 | SCOPED_TRACE("missing bb frequency" ); |
937 | DoCheck(MissingBBFreq, "unable to decode LEB128 at offset 0x0000000f: " |
938 | "malformed uleb128, extends past end" ); |
939 | } |
940 | |
941 | // Check that we fail when branch probability is enabled but not provided. |
942 | SmallString<128> MissingBrProb(CommonYamlString); |
943 | MissingBrProb += R"( |
944 | - Version: 2 |
945 | Feature: 0x04 |
946 | BBRanges: |
947 | - BBEntries: |
948 | - ID: 1 |
949 | AddressOffset: 0x0 |
950 | Size: 0x1 |
951 | Metadata: 0x6 |
952 | - ID: 2 |
953 | AddressOffset: 0x1 |
954 | Size: 0x1 |
955 | Metadata: 0x2 |
956 | - ID: 3 |
957 | AddressOffset: 0x2 |
958 | Size: 0x1 |
959 | Metadata: 0x2 |
960 | PGOAnalyses: |
961 | - PGOBBEntries: |
962 | - Successors: |
963 | - ID: 2 |
964 | BrProb: 0x80000000 |
965 | - ID: 3 |
966 | BrProb: 0x80000000 |
967 | - Successors: |
968 | - ID: 3 |
969 | BrProb: 0xF0000000 |
970 | )" ; |
971 | |
972 | { |
973 | SCOPED_TRACE("missing branch probability" ); |
974 | DoCheck(MissingBrProb, "unable to decode LEB128 at offset 0x00000017: " |
975 | "malformed uleb128, extends past end" ); |
976 | } |
977 | } |
978 | |
979 | // Test for the ELFObjectFile::readBBAddrMap API with PGOAnalysisMap. |
980 | TEST(ELFObjectFileTest, ReadPGOAnalysisMap) { |
981 | StringRef CommonYamlString(R"( |
982 | --- !ELF |
983 | FileHeader: |
984 | Class: ELFCLASS64 |
985 | Data: ELFDATA2LSB |
986 | Type: ET_EXEC |
987 | Sections: |
988 | - Name: .llvm_bb_addr_map_1 |
989 | Type: SHT_LLVM_BB_ADDR_MAP |
990 | Link: 1 |
991 | Entries: |
992 | - Version: 2 |
993 | Feature: 0x1 |
994 | BBRanges: |
995 | - BaseAddress: 0x11111 |
996 | BBEntries: |
997 | - ID: 1 |
998 | AddressOffset: 0x0 |
999 | Size: 0x1 |
1000 | Metadata: 0x2 |
1001 | PGOAnalyses: |
1002 | - FuncEntryCount: 892 |
1003 | - Name: .llvm_bb_addr_map_2 |
1004 | Type: SHT_LLVM_BB_ADDR_MAP |
1005 | Link: 1 |
1006 | Entries: |
1007 | - Version: 2 |
1008 | Feature: 0x2 |
1009 | BBRanges: |
1010 | - BaseAddress: 0x22222 |
1011 | BBEntries: |
1012 | - ID: 2 |
1013 | AddressOffset: 0x0 |
1014 | Size: 0x2 |
1015 | Metadata: 0x4 |
1016 | PGOAnalyses: |
1017 | - PGOBBEntries: |
1018 | - BBFreq: 343 |
1019 | - Name: .llvm_bb_addr_map_3 |
1020 | Type: SHT_LLVM_BB_ADDR_MAP |
1021 | Link: 2 |
1022 | Entries: |
1023 | - Version: 2 |
1024 | Feature: 0x4 |
1025 | BBRanges: |
1026 | - BaseAddress: 0x33333 |
1027 | BBEntries: |
1028 | - ID: 0 |
1029 | AddressOffset: 0x0 |
1030 | Size: 0x3 |
1031 | Metadata: 0x6 |
1032 | - ID: 1 |
1033 | AddressOffset: 0x0 |
1034 | Size: 0x3 |
1035 | Metadata: 0x4 |
1036 | - ID: 2 |
1037 | AddressOffset: 0x0 |
1038 | Size: 0x3 |
1039 | Metadata: 0x0 |
1040 | PGOAnalyses: |
1041 | - PGOBBEntries: |
1042 | - Successors: |
1043 | - ID: 1 |
1044 | BrProb: 0x11111111 |
1045 | - ID: 2 |
1046 | BrProb: 0xeeeeeeee |
1047 | - Successors: |
1048 | - ID: 2 |
1049 | BrProb: 0xffffffff |
1050 | - Successors: [] |
1051 | - Name: .llvm_bb_addr_map_4 |
1052 | Type: SHT_LLVM_BB_ADDR_MAP |
1053 | # Link: 0 (by default, can be overriden) |
1054 | Entries: |
1055 | - Version: 2 |
1056 | Feature: 0x7 |
1057 | BBRanges: |
1058 | - BaseAddress: 0x44444 |
1059 | BBEntries: |
1060 | - ID: 0 |
1061 | AddressOffset: 0x0 |
1062 | Size: 0x4 |
1063 | Metadata: 0x18 |
1064 | - ID: 1 |
1065 | AddressOffset: 0x0 |
1066 | Size: 0x4 |
1067 | Metadata: 0x0 |
1068 | - ID: 2 |
1069 | AddressOffset: 0x0 |
1070 | Size: 0x4 |
1071 | Metadata: 0x0 |
1072 | - ID: 3 |
1073 | AddressOffset: 0x0 |
1074 | Size: 0x4 |
1075 | Metadata: 0x0 |
1076 | PGOAnalyses: |
1077 | - FuncEntryCount: 1000 |
1078 | PGOBBEntries: |
1079 | - BBFreq: 1000 |
1080 | Successors: |
1081 | - ID: 1 |
1082 | BrProb: 0x22222222 |
1083 | - ID: 2 |
1084 | BrProb: 0x33333333 |
1085 | - ID: 3 |
1086 | BrProb: 0xaaaaaaaa |
1087 | - BBFreq: 133 |
1088 | Successors: |
1089 | - ID: 2 |
1090 | BrProb: 0x11111111 |
1091 | - ID: 3 |
1092 | BrProb: 0xeeeeeeee |
1093 | - BBFreq: 18 |
1094 | Successors: |
1095 | - ID: 3 |
1096 | BrProb: 0xffffffff |
1097 | - BBFreq: 1000 |
1098 | Successors: [] |
1099 | - Name: .llvm_bb_addr_map_5 |
1100 | Type: SHT_LLVM_BB_ADDR_MAP |
1101 | # Link: 0 (by default, can be overriden) |
1102 | Entries: |
1103 | - Version: 2 |
1104 | Feature: 0x0 |
1105 | BBRanges: |
1106 | - BaseAddress: 0x55555 |
1107 | BBEntries: |
1108 | - ID: 2 |
1109 | AddressOffset: 0x0 |
1110 | Size: 0x2 |
1111 | Metadata: 0x4 |
1112 | PGOAnalyses: [{}] |
1113 | - Name: .llvm_bb_addr_map_6 |
1114 | Type: SHT_LLVM_BB_ADDR_MAP |
1115 | # Link: 0 (by default, can be overriden) |
1116 | Entries: |
1117 | - Version: 2 |
1118 | Feature: 0xc |
1119 | BBRanges: |
1120 | - BaseAddress: 0x66666 |
1121 | BBEntries: |
1122 | - ID: 0 |
1123 | AddressOffset: 0x0 |
1124 | Size: 0x6 |
1125 | Metadata: 0x6 |
1126 | - ID: 1 |
1127 | AddressOffset: 0x0 |
1128 | Size: 0x6 |
1129 | Metadata: 0x4 |
1130 | - BaseAddress: 0x666661 |
1131 | BBEntries: |
1132 | - ID: 2 |
1133 | AddressOffset: 0x0 |
1134 | Size: 0x6 |
1135 | Metadata: 0x0 |
1136 | PGOAnalyses: |
1137 | - PGOBBEntries: |
1138 | - Successors: |
1139 | - ID: 1 |
1140 | BrProb: 0x22222222 |
1141 | - ID: 2 |
1142 | BrProb: 0xcccccccc |
1143 | - Successors: |
1144 | - ID: 2 |
1145 | BrProb: 0x88888888 |
1146 | - Successors: [] |
1147 | )" ); |
1148 | |
1149 | BBAddrMap E1 = { |
1150 | .BBRanges: {{.BaseAddress: 0x11111, .BBEntries: {{1, 0x0, 0x1, {.HasReturn: false, .HasTailCall: true, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1151 | PGOAnalysisMap P1 = {.FuncEntryCount: 892, .BBEntries: {}, .FeatEnable: {.FuncEntryCount: true, .BBFreq: false, .BrProb: false, .MultiBBRange: false}}; |
1152 | BBAddrMap E2 = { |
1153 | .BBRanges: {{.BaseAddress: 0x22222, .BBEntries: {{2, 0x0, 0x2, {.HasReturn: false, .HasTailCall: false, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1154 | PGOAnalysisMap P2 = { |
1155 | .FuncEntryCount: {}, .BBEntries: {{.BlockFreq: BlockFrequency(343), .Successors: {}}}, .FeatEnable: {.FuncEntryCount: false, .BBFreq: true, .BrProb: false, .MultiBBRange: false}}; |
1156 | BBAddrMap E3 = {.BBRanges: {{.BaseAddress: 0x33333, |
1157 | .BBEntries: {{0, 0x0, 0x3, {.HasReturn: false, .HasTailCall: true, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}, |
1158 | {1, 0x3, 0x3, {.HasReturn: false, .HasTailCall: false, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}, |
1159 | {2, 0x6, 0x3, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1160 | PGOAnalysisMap P3 = {.FuncEntryCount: {}, |
1161 | .BBEntries: {{.BlockFreq: {}, |
1162 | .Successors: {{.ID: 1, .Prob: BranchProbability::getRaw(N: 0x1111'1111)}, |
1163 | {.ID: 2, .Prob: BranchProbability::getRaw(N: 0xeeee'eeee)}}}, |
1164 | {.BlockFreq: {}, .Successors: {{.ID: 2, .Prob: BranchProbability::getRaw(N: 0xffff'ffff)}}}, |
1165 | {.BlockFreq: {}, .Successors: {}}}, |
1166 | .FeatEnable: {.FuncEntryCount: false, .BBFreq: false, .BrProb: true, .MultiBBRange: false}}; |
1167 | BBAddrMap E4 = {.BBRanges: {{.BaseAddress: 0x44444, |
1168 | .BBEntries: {{0, 0x0, 0x4, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: true, .HasIndirectBranch: true}}, |
1169 | {1, 0x4, 0x4, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}, |
1170 | {2, 0x8, 0x4, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}, |
1171 | {3, 0xc, 0x4, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1172 | PGOAnalysisMap P4 = { |
1173 | .FuncEntryCount: 1000, |
1174 | .BBEntries: {{.BlockFreq: BlockFrequency(1000), |
1175 | .Successors: {{.ID: 1, .Prob: BranchProbability::getRaw(N: 0x2222'2222)}, |
1176 | {.ID: 2, .Prob: BranchProbability::getRaw(N: 0x3333'3333)}, |
1177 | {.ID: 3, .Prob: BranchProbability::getRaw(N: 0xaaaa'aaaa)}}}, |
1178 | {.BlockFreq: BlockFrequency(133), |
1179 | .Successors: {{.ID: 2, .Prob: BranchProbability::getRaw(N: 0x1111'1111)}, |
1180 | {.ID: 3, .Prob: BranchProbability::getRaw(N: 0xeeee'eeee)}}}, |
1181 | {.BlockFreq: BlockFrequency(18), .Successors: {{.ID: 3, .Prob: BranchProbability::getRaw(N: 0xffff'ffff)}}}, |
1182 | {.BlockFreq: BlockFrequency(1000), .Successors: {}}}, |
1183 | .FeatEnable: {.FuncEntryCount: true, .BBFreq: true, .BrProb: true, .MultiBBRange: false}}; |
1184 | BBAddrMap E5 = { |
1185 | .BBRanges: {{.BaseAddress: 0x55555, .BBEntries: {{2, 0x0, 0x2, {.HasReturn: false, .HasTailCall: false, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1186 | PGOAnalysisMap P5 = {.FuncEntryCount: {}, .BBEntries: {}, .FeatEnable: {.FuncEntryCount: false, .BBFreq: false, .BrProb: false, .MultiBBRange: false}}; |
1187 | BBAddrMap E6 = { |
1188 | .BBRanges: {{.BaseAddress: 0x66666, |
1189 | .BBEntries: {{0, 0x0, 0x6, {.HasReturn: false, .HasTailCall: true, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}, |
1190 | {1, 0x6, 0x6, {.HasReturn: false, .HasTailCall: false, .IsEHPad: true, .CanFallThrough: false, .HasIndirectBranch: false}}}}, |
1191 | {.BaseAddress: 0x666661, .BBEntries: {{2, 0x0, 0x6, {.HasReturn: false, .HasTailCall: false, .IsEHPad: false, .CanFallThrough: false, .HasIndirectBranch: false}}}}}}; |
1192 | PGOAnalysisMap P6 = {.FuncEntryCount: {}, |
1193 | .BBEntries: {{.BlockFreq: {}, |
1194 | .Successors: {{.ID: 1, .Prob: BranchProbability::getRaw(N: 0x2222'2222)}, |
1195 | {.ID: 2, .Prob: BranchProbability::getRaw(N: 0xcccc'cccc)}}}, |
1196 | {.BlockFreq: {}, .Successors: {{.ID: 2, .Prob: BranchProbability::getRaw(N: 0x8888'8888)}}}, |
1197 | {.BlockFreq: {}, .Successors: {}}}, |
1198 | .FeatEnable: {.FuncEntryCount: false, .BBFreq: false, .BrProb: true, .MultiBBRange: true}}; |
1199 | |
1200 | std::vector<BBAddrMap> Section0BBAddrMaps = {E4, E5, E6}; |
1201 | std::vector<BBAddrMap> Section1BBAddrMaps = {E3}; |
1202 | std::vector<BBAddrMap> Section2BBAddrMaps = {E1, E2}; |
1203 | std::vector<BBAddrMap> AllBBAddrMaps = {E1, E2, E3, E4, E5, E6}; |
1204 | |
1205 | std::vector<PGOAnalysisMap> Section0PGOAnalysisMaps = {P4, P5, P6}; |
1206 | std::vector<PGOAnalysisMap> Section1PGOAnalysisMaps = {P3}; |
1207 | std::vector<PGOAnalysisMap> Section2PGOAnalysisMaps = {P1, P2}; |
1208 | std::vector<PGOAnalysisMap> AllPGOAnalysisMaps = {P1, P2, P3, P4, P5, P6}; |
1209 | |
1210 | auto DoCheckSucceeds = |
1211 | [&](StringRef YamlString, std::optional<unsigned> TextSectionIndex, |
1212 | std::vector<BBAddrMap> ExpectedResult, |
1213 | std::optional<std::vector<PGOAnalysisMap>> ExpectedPGO) { |
1214 | SCOPED_TRACE( |
1215 | "for TextSectionIndex: " + |
1216 | (TextSectionIndex ? llvm::Twine(*TextSectionIndex) : "{}" ) + |
1217 | " and object yaml:\n" + YamlString); |
1218 | SmallString<0> Storage; |
1219 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
1220 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
1221 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
1222 | |
1223 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
1224 | ElfOrErr->getELFFile().getSection(Index: 1); |
1225 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
1226 | |
1227 | std::vector<PGOAnalysisMap> PGOAnalyses; |
1228 | auto BBAddrMaps = ElfOrErr->readBBAddrMap( |
1229 | TextSectionIndex, PGOAnalyses: ExpectedPGO ? &PGOAnalyses : nullptr); |
1230 | ASSERT_THAT_EXPECTED(BBAddrMaps, Succeeded()); |
1231 | EXPECT_EQ(*BBAddrMaps, ExpectedResult); |
1232 | if (ExpectedPGO) { |
1233 | EXPECT_EQ(BBAddrMaps->size(), PGOAnalyses.size()); |
1234 | for (const auto &PGO : PGOAnalyses) { |
1235 | errs() << "FuncEntryCount: " << PGO.FuncEntryCount << "\n" ; |
1236 | for (const auto &PGOBB : PGO.BBEntries) |
1237 | errs() << "\tBB: " << PGOBB.BlockFreq.getFrequency() << "\n" ; |
1238 | } |
1239 | errs() << " To expected:\n" ; |
1240 | for (const auto &PGO : *ExpectedPGO) { |
1241 | errs() << "FuncEntryCount: " << PGO.FuncEntryCount << "\n" ; |
1242 | for (const auto &PGOBB : PGO.BBEntries) |
1243 | errs() << "\tBB: " << PGOBB.BlockFreq.getFrequency() << "\n" ; |
1244 | } |
1245 | EXPECT_EQ(PGOAnalyses, *ExpectedPGO); |
1246 | for (auto &&[BB, PGO] : llvm::zip(t&: *BBAddrMaps, u&: PGOAnalyses)) { |
1247 | if (PGO.FeatEnable.BBFreq || PGO.FeatEnable.BrProb) |
1248 | EXPECT_EQ(BB.getNumBBEntries(), PGO.BBEntries.size()); |
1249 | } |
1250 | } |
1251 | }; |
1252 | |
1253 | auto DoCheckFails = [&](StringRef YamlString, |
1254 | std::optional<unsigned> TextSectionIndex, |
1255 | const char *ErrMsg) { |
1256 | SCOPED_TRACE("for TextSectionIndex: " + |
1257 | (TextSectionIndex ? llvm::Twine(*TextSectionIndex) : "{}" ) + |
1258 | " and object yaml:\n" + YamlString); |
1259 | SmallString<0> Storage; |
1260 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
1261 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
1262 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
1263 | |
1264 | Expected<const typename ELF64LE::Shdr *> BBAddrMapSecOrErr = |
1265 | ElfOrErr->getELFFile().getSection(Index: 1); |
1266 | ASSERT_THAT_EXPECTED(BBAddrMapSecOrErr, Succeeded()); |
1267 | std::vector<PGOAnalysisMap> PGOAnalyses; |
1268 | EXPECT_THAT_ERROR( |
1269 | ElfOrErr->readBBAddrMap(TextSectionIndex, &PGOAnalyses).takeError(), |
1270 | FailedWithMessage(ErrMsg)); |
1271 | }; |
1272 | |
1273 | { |
1274 | SCOPED_TRACE("normal sections" ); |
1275 | // Check that we can retrieve the data in the normal case. |
1276 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/std::nullopt, |
1277 | AllBBAddrMaps, std::nullopt); |
1278 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/0, |
1279 | Section0BBAddrMaps, std::nullopt); |
1280 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/2, |
1281 | Section1BBAddrMaps, std::nullopt); |
1282 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/1, |
1283 | Section2BBAddrMaps, std::nullopt); |
1284 | |
1285 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/std::nullopt, |
1286 | AllBBAddrMaps, AllPGOAnalysisMaps); |
1287 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/0, |
1288 | Section0BBAddrMaps, Section0PGOAnalysisMaps); |
1289 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/2, |
1290 | Section1BBAddrMaps, Section1PGOAnalysisMaps); |
1291 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/1, |
1292 | Section2BBAddrMaps, Section2PGOAnalysisMaps); |
1293 | // Check that when no bb-address-map section is found for a text section, |
1294 | // we return an empty result. |
1295 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/3, {}, std::nullopt); |
1296 | DoCheckSucceeds(CommonYamlString, /*TextSectionIndex=*/3, {}, |
1297 | std::vector<PGOAnalysisMap>{}); |
1298 | } |
1299 | |
1300 | // Check that we detect when a bb-addr-map section is linked to an invalid |
1301 | // (not present) section. |
1302 | SmallString<128> InvalidLinkedYamlString(CommonYamlString); |
1303 | InvalidLinkedYamlString += R"( |
1304 | Link: 121 |
1305 | )" ; |
1306 | |
1307 | { |
1308 | SCOPED_TRACE("invalid linked section" ); |
1309 | DoCheckFails(InvalidLinkedYamlString, /*TextSectionIndex=*/5, |
1310 | "unable to get the linked-to section for " |
1311 | "SHT_LLVM_BB_ADDR_MAP section with index 6: invalid section " |
1312 | "index: 121" ); |
1313 | |
1314 | // Linked sections are not checked when we don't target a specific text |
1315 | // section. |
1316 | DoCheckSucceeds(InvalidLinkedYamlString, /*TextSectionIndex=*/std::nullopt, |
1317 | AllBBAddrMaps, std::nullopt); |
1318 | DoCheckSucceeds(InvalidLinkedYamlString, /*TextSectionIndex=*/std::nullopt, |
1319 | AllBBAddrMaps, AllPGOAnalysisMaps); |
1320 | } |
1321 | |
1322 | // Check that we can detect when bb-address-map decoding fails. |
1323 | SmallString<128> TruncatedYamlString(CommonYamlString); |
1324 | TruncatedYamlString += R"( |
1325 | ShSize: 0xa |
1326 | )" ; |
1327 | |
1328 | { |
1329 | SCOPED_TRACE("truncated section" ); |
1330 | DoCheckFails( |
1331 | TruncatedYamlString, /*TextSectionIndex=*/std::nullopt, |
1332 | "unable to read SHT_LLVM_BB_ADDR_MAP section with index 6: " |
1333 | "unexpected end of data at offset 0xa while reading [0x3, 0xb)" ); |
1334 | // Check that we can read the other section's bb-address-maps which are |
1335 | // valid. |
1336 | DoCheckSucceeds(TruncatedYamlString, /*TextSectionIndex=*/2, |
1337 | Section1BBAddrMaps, std::nullopt); |
1338 | DoCheckSucceeds(TruncatedYamlString, /*TextSectionIndex=*/2, |
1339 | Section1BBAddrMaps, Section1PGOAnalysisMaps); |
1340 | } |
1341 | } |
1342 | |
1343 | // Test for ObjectFile::getRelocatedSection: check that it returns a relocated |
1344 | // section for executable and relocatable files. |
1345 | TEST(ELFObjectFileTest, ExecutableWithRelocs) { |
1346 | StringRef (R"( |
1347 | --- !ELF |
1348 | FileHeader: |
1349 | Class: ELFCLASS64 |
1350 | Data: ELFDATA2LSB |
1351 | )" ); |
1352 | StringRef ContentsString(R"( |
1353 | Sections: |
1354 | - Name: .text |
1355 | Type: SHT_PROGBITS |
1356 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
1357 | - Name: .rela.text |
1358 | Type: SHT_RELA |
1359 | Flags: [ SHF_INFO_LINK ] |
1360 | Info: .text |
1361 | )" ); |
1362 | |
1363 | auto DoCheck = [&](StringRef YamlString) { |
1364 | SmallString<0> Storage; |
1365 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
1366 | toBinary<ELF64LE>(Storage, Yaml: YamlString); |
1367 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
1368 | const ELFObjectFile<ELF64LE> &Obj = *ElfOrErr; |
1369 | |
1370 | bool FoundRela; |
1371 | |
1372 | for (SectionRef Sec : Obj.sections()) { |
1373 | Expected<StringRef> SecNameOrErr = Sec.getName(); |
1374 | ASSERT_THAT_EXPECTED(SecNameOrErr, Succeeded()); |
1375 | StringRef SecName = *SecNameOrErr; |
1376 | if (SecName != ".rela.text" ) |
1377 | continue; |
1378 | FoundRela = true; |
1379 | Expected<section_iterator> RelSecOrErr = Sec.getRelocatedSection(); |
1380 | ASSERT_THAT_EXPECTED(RelSecOrErr, Succeeded()); |
1381 | section_iterator RelSec = *RelSecOrErr; |
1382 | ASSERT_NE(RelSec, Obj.section_end()); |
1383 | Expected<StringRef> TextSecNameOrErr = RelSec->getName(); |
1384 | ASSERT_THAT_EXPECTED(TextSecNameOrErr, Succeeded()); |
1385 | StringRef TextSecName = *TextSecNameOrErr; |
1386 | EXPECT_EQ(TextSecName, ".text" ); |
1387 | } |
1388 | ASSERT_TRUE(FoundRela); |
1389 | }; |
1390 | |
1391 | // Check ET_EXEC file (`ld --emit-relocs` use-case). |
1392 | SmallString<128> ExecFileYamlString(HeaderString); |
1393 | ExecFileYamlString += R"( |
1394 | Type: ET_EXEC |
1395 | )" ; |
1396 | ExecFileYamlString += ContentsString; |
1397 | DoCheck(ExecFileYamlString); |
1398 | |
1399 | // Check ET_REL file. |
1400 | SmallString<128> RelocatableFileYamlString(HeaderString); |
1401 | RelocatableFileYamlString += R"( |
1402 | Type: ET_REL |
1403 | )" ; |
1404 | RelocatableFileYamlString += ContentsString; |
1405 | DoCheck(RelocatableFileYamlString); |
1406 | } |
1407 | |
1408 | TEST(ELFObjectFileTest, GetSectionAndRelocations) { |
1409 | StringRef (R"( |
1410 | --- !ELF |
1411 | FileHeader: |
1412 | Class: ELFCLASS64 |
1413 | Data: ELFDATA2LSB |
1414 | Type: ET_EXEC |
1415 | )" ); |
1416 | |
1417 | using Elf_Shdr = Elf_Shdr_Impl<ELF64LE>; |
1418 | |
1419 | auto DoCheckSucceeds = [&](StringRef ContentsString, |
1420 | std::function<Expected<bool>(const Elf_Shdr &)> |
1421 | Matcher) { |
1422 | SmallString<0> Storage; |
1423 | SmallString<128> FullYamlString(HeaderString); |
1424 | FullYamlString += ContentsString; |
1425 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
1426 | toBinary<ELF64LE>(Storage, Yaml: FullYamlString); |
1427 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
1428 | |
1429 | Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> SecToRelocMapOrErr = |
1430 | ElfOrErr->getELFFile().getSectionAndRelocations(IsMatch: Matcher); |
1431 | ASSERT_THAT_EXPECTED(SecToRelocMapOrErr, Succeeded()); |
1432 | |
1433 | // Basic verification to make sure we have the correct section types. |
1434 | for (auto const &[Sec, RelaSec] : *SecToRelocMapOrErr) { |
1435 | ASSERT_EQ(Sec->sh_type, ELF::SHT_PROGBITS); |
1436 | ASSERT_EQ(RelaSec->sh_type, ELF::SHT_RELA); |
1437 | } |
1438 | }; |
1439 | |
1440 | auto DoCheckFails = [&](StringRef ContentsString, |
1441 | std::function<Expected<bool>(const Elf_Shdr &)> |
1442 | Matcher, |
1443 | const char *ErrorMessage) { |
1444 | SmallString<0> Storage; |
1445 | SmallString<128> FullYamlString(HeaderString); |
1446 | FullYamlString += ContentsString; |
1447 | Expected<ELFObjectFile<ELF64LE>> ElfOrErr = |
1448 | toBinary<ELF64LE>(Storage, Yaml: FullYamlString); |
1449 | ASSERT_THAT_EXPECTED(ElfOrErr, Succeeded()); |
1450 | |
1451 | Expected<MapVector<const Elf_Shdr *, const Elf_Shdr *>> SecToRelocMapOrErr = |
1452 | ElfOrErr->getELFFile().getSectionAndRelocations(IsMatch: Matcher); |
1453 | ASSERT_THAT_ERROR(SecToRelocMapOrErr.takeError(), |
1454 | FailedWithMessage(ErrorMessage)); |
1455 | }; |
1456 | |
1457 | auto DefaultMatcher = [](const Elf_Shdr &Sec) -> bool { |
1458 | return Sec.sh_type == ELF::SHT_PROGBITS; |
1459 | }; |
1460 | |
1461 | StringRef TwoTextSections = R"( |
1462 | Sections: |
1463 | - Name: .text |
1464 | Type: SHT_PROGBITS |
1465 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
1466 | - Name: .rela.text |
1467 | Type: SHT_RELA |
1468 | Flags: [ SHF_INFO_LINK ] |
1469 | Info: .text |
1470 | - Name: .text2 |
1471 | Type: SHT_PROGBITS |
1472 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
1473 | - Name: .rela.text2 |
1474 | Type: SHT_RELA |
1475 | Flags: [ SHF_INFO_LINK ] |
1476 | Info: .text2 |
1477 | )" ; |
1478 | DoCheckSucceeds(TwoTextSections, DefaultMatcher); |
1479 | |
1480 | StringRef OneTextSection = R"( |
1481 | Sections: |
1482 | - Name: .text |
1483 | Type: SHT_PROGBITS |
1484 | Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
1485 | )" ; |
1486 | |
1487 | auto ErroringMatcher = [](const Elf_Shdr &Sec) -> Expected<bool> { |
1488 | if (Sec.sh_type == ELF::SHT_PROGBITS) |
1489 | return createError(Err: "This was supposed to fail." ); |
1490 | return false; |
1491 | }; |
1492 | |
1493 | DoCheckFails(OneTextSection, ErroringMatcher, "This was supposed to fail." ); |
1494 | |
1495 | StringRef MissingRelocatableContent = R"( |
1496 | Sections: |
1497 | - Name: .rela.text |
1498 | Type: SHT_RELA |
1499 | Flags: [ SHF_INFO_LINK ] |
1500 | Info: 0xFF |
1501 | )" ; |
1502 | |
1503 | DoCheckFails(MissingRelocatableContent, DefaultMatcher, |
1504 | "SHT_RELA section with index 1: failed to get a " |
1505 | "relocated section: invalid section index: 255" ); |
1506 | } |
1507 | |