1//===- bolt/unittest/Core/BinaryContext.cpp -------------------------------===//
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 "bolt/Core/BinaryContext.h"
10#include "bolt/Utils/CommandLineOpts.h"
11#include "llvm/BinaryFormat/ELF.h"
12#include "llvm/DebugInfo/DWARF/DWARFContext.h"
13#include "llvm/Support/TargetSelect.h"
14#include "gtest/gtest.h"
15
16using namespace llvm;
17using namespace llvm::object;
18using namespace llvm::ELF;
19using namespace bolt;
20
21namespace {
22struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
23 void SetUp() override {
24 initalizeLLVM();
25 prepareElf();
26 initializeBOLT();
27 }
28
29protected:
30 void initalizeLLVM() {
31#define BOLT_TARGET(target) \
32 LLVMInitialize##target##TargetInfo(); \
33 LLVMInitialize##target##TargetMC(); \
34 LLVMInitialize##target##AsmParser(); \
35 LLVMInitialize##target##Disassembler(); \
36 LLVMInitialize##target##Target(); \
37 LLVMInitialize##target##AsmPrinter();
38
39#include "bolt/Core/TargetConfig.def"
40 }
41
42 void prepareElf() {
43 memcpy(dest: ElfBuf, src: "\177ELF", n: 4);
44 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
45 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
46 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
47 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
48 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
49 ObjFile = cantFail(ValOrErr: ObjectFile::createObjectFile(Object: Source));
50 }
51
52 void initializeBOLT() {
53 Relocation::Arch = ObjFile->makeTriple().getArch();
54 BC = cantFail(ValOrErr: BinaryContext::createBinaryContext(
55 TheTriple: ObjFile->makeTriple(), SSP: std::make_shared<orc::SymbolStringPool>(),
56 InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true, DwCtx: DWARFContext::create(Obj: *ObjFile),
57 Logger: {.Out: llvm::outs(), .Err: llvm::errs()}));
58 ASSERT_FALSE(!BC);
59 }
60
61 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
62 std::unique_ptr<ObjectFile> ObjFile;
63 std::unique_ptr<BinaryContext> BC;
64};
65} // namespace
66
67#ifdef X86_AVAILABLE
68
69INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
70 ::testing::Values(Triple::x86_64));
71
72#endif
73
74#ifdef AARCH64_AVAILABLE
75
76INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
77 ::testing::Values(Triple::aarch64));
78
79TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
80 if (GetParam() != Triple::aarch64)
81 GTEST_SKIP();
82
83 // This test checks that encodeValueAArch64 used by flushPendingRelocations
84 // returns correctly encoded values for CALL26 relocation for both backward
85 // and forward branches.
86 //
87 // The offsets layout is:
88 // 4: func1
89 // 8: bl func1
90 // 12: bl func2
91 // 16: func2
92
93 constexpr size_t DataSize = 20;
94 uint8_t *Data = new uint8_t[DataSize];
95 BinarySection &BS = BC->registerOrUpdateSection(
96 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,
97 Size: DataSize, Alignment: 4);
98 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1");
99 ASSERT_TRUE(RelSymbol1);
100 BS.addPendingRelocation(
101 Rel: Relocation{8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0});
102 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2");
103 ASSERT_TRUE(RelSymbol2);
104 BS.addPendingRelocation(
105 Rel: Relocation{12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0});
106
107 SmallVector<char> Vect(DataSize);
108 raw_svector_ostream OS(Vect);
109
110 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) {
111 return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
112 });
113
114 const uint8_t Func1Call[4] = {255, 255, 255, 151};
115 const uint8_t Func2Call[4] = {1, 0, 0, 148};
116
117 EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
118 EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
119}
120
121TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
122 if (GetParam() != Triple::aarch64)
123 GTEST_SKIP();
124
125 // This test checks that encodeValueAArch64 used by flushPendingRelocations
126 // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
127 // backward and forward branches.
128 //
129 // The offsets layout is:
130 // 4: func1
131 // 8: b func1
132 // 12: b func2
133 // 16: func2
134
135 const uint64_t Size = 20;
136 char *Data = new char[Size];
137 BinarySection &BS = BC->registerOrUpdateSection(
138 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
139 Data: (uint8_t *)Data, Size, Alignment: 4);
140 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1");
141 ASSERT_TRUE(RelSymbol1);
142 BS.addPendingRelocation(
143 Rel: Relocation{8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0});
144 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2");
145 ASSERT_TRUE(RelSymbol2);
146 BS.addPendingRelocation(
147 Rel: Relocation{12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0});
148
149 SmallVector<char> Vect(Size);
150 raw_svector_ostream OS(Vect);
151
152 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) {
153 return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
154 });
155
156 const uint8_t Func1Call[4] = {255, 255, 255, 23};
157 const uint8_t Func2Call[4] = {1, 0, 0, 20};
158
159 EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4))
160 << "Wrong backward branch value\n";
161 EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4))
162 << "Wrong forward branch value\n";
163}
164
165TEST_P(BinaryContextTester,
166 FlushOptionalOutOfRangePendingRelocCALL26_ForcePatchOn) {
167 if (GetParam() != Triple::aarch64)
168 GTEST_SKIP();
169
170 // Tests that flushPendingRelocations can skip flushing any optional pending
171 // relocations that cannot be encoded, given that PatchEntries runs.
172 opts::ForcePatch = true;
173
174 opts::Verbosity = 1;
175 testing::internal::CaptureStdout();
176
177 BinarySection &BS = BC->registerOrUpdateSection(
178 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
179 MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func");
180 ASSERT_TRUE(RelSymbol);
181 Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};
182 Reloc.setOptional();
183 BS.addPendingRelocation(Rel: Reloc);
184
185 SmallVector<char> Vect;
186 raw_svector_ostream OS(Vect);
187
188 // Resolve relocation symbol to a high value so encoding will be out of range.
189 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) { return 0x800000F; });
190 outs().flush();
191 std::string CapturedStdOut = testing::internal::GetCapturedStdout();
192 EXPECT_EQ(CapturedStdOut,
193 "BOLT-INFO: skipped 1 out-of-range optional relocations\n");
194}
195
196#endif
197
198TEST_P(BinaryContextTester, BaseAddress) {
199 // Check that base address calculation is correct for a binary with the
200 // following segment layout:
201 BC->SegmentMapInfo[0] =
202 SegmentInfo{.Address: 0, .Size: 0x10e8c2b4, .FileOffset: 0, .FileSize: 0x10e8c2b4, .Alignment: 0x1000, .IsExecutable: true};
203 BC->SegmentMapInfo[0x10e8d2b4] =
204 SegmentInfo{.Address: 0x10e8d2b4, .Size: 0x3952faec, .FileOffset: 0x10e8c2b4, .FileSize: 0x3952faec, .Alignment: 0x1000, .IsExecutable: true};
205 BC->SegmentMapInfo[0x4a3bddc0] =
206 SegmentInfo{.Address: 0x4a3bddc0, .Size: 0x148e828, .FileOffset: 0x4a3bbdc0, .FileSize: 0x148e828, .Alignment: 0x1000, .IsExecutable: true};
207 BC->SegmentMapInfo[0x4b84d5e8] =
208 SegmentInfo{.Address: 0x4b84d5e8, .Size: 0x294f830, .FileOffset: 0x4b84a5e8, .FileSize: 0x3d3820, .Alignment: 0x1000, .IsExecutable: true};
209
210 std::optional<uint64_t> BaseAddress =
211 BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x10e8c000);
212 ASSERT_TRUE(BaseAddress.has_value());
213 ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
214
215 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x137a000);
216 ASSERT_FALSE(BaseAddress.has_value());
217}
218
219TEST_P(BinaryContextTester, BaseAddress2) {
220 // Check that base address calculation is correct for a binary if the
221 // alignment in ELF file are different from pagesize.
222 // The segment layout is as follows:
223 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x2177c, .FileOffset: 0, .FileSize: 0x2177c, .Alignment: 0x10000, .IsExecutable: true};
224 BC->SegmentMapInfo[0x31860] =
225 SegmentInfo{.Address: 0x31860, .Size: 0x370, .FileOffset: 0x21860, .FileSize: 0x370, .Alignment: 0x10000, .IsExecutable: true};
226 BC->SegmentMapInfo[0x41c20] =
227 SegmentInfo{.Address: 0x41c20, .Size: 0x1f8, .FileOffset: 0x21c20, .FileSize: 0x1f8, .Alignment: 0x10000, .IsExecutable: true};
228 BC->SegmentMapInfo[0x54e18] =
229 SegmentInfo{.Address: 0x54e18, .Size: 0x51, .FileOffset: 0x24e18, .FileSize: 0x51, .Alignment: 0x10000, .IsExecutable: true};
230
231 std::optional<uint64_t> BaseAddress =
232 BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x21000);
233 ASSERT_TRUE(BaseAddress.has_value());
234 ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
235
236 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x11000);
237 ASSERT_FALSE(BaseAddress.has_value());
238}
239
240TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
241 // Check that the correct segment is used to compute the base address
242 // when multiple segments are close together in the ELF file (closer
243 // than the required alignment in the process space).
244 // See https://github.com/llvm/llvm-project/issues/109384
245 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x1d1c, .FileOffset: 0, .FileSize: 0x1d1c, .Alignment: 0x10000, .IsExecutable: false};
246 BC->SegmentMapInfo[0x11d40] =
247 SegmentInfo{.Address: 0x11d40, .Size: 0x11e0, .FileOffset: 0x1d40, .FileSize: 0x11e0, .Alignment: 0x10000, .IsExecutable: true};
248 BC->SegmentMapInfo[0x22f20] =
249 SegmentInfo{.Address: 0x22f20, .Size: 0x10e0, .FileOffset: 0x2f20, .FileSize: 0x1f0, .Alignment: 0x10000, .IsExecutable: false};
250 BC->SegmentMapInfo[0x33110] =
251 SegmentInfo{.Address: 0x33110, .Size: 0x89, .FileOffset: 0x3110, .FileSize: 0x88, .Alignment: 0x10000, .IsExecutable: false};
252
253 std::optional<uint64_t> BaseAddress =
254 BC->getBaseAddressForMapping(MMapAddress: 0xaaaaaaab1000, FileOffset: 0x1000);
255 ASSERT_TRUE(BaseAddress.has_value());
256 ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
257}
258

source code of bolt/unittests/Core/BinaryContext.cpp