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 "llvm/BinaryFormat/ELF.h"
11#include "llvm/DebugInfo/DWARF/DWARFContext.h"
12#include "llvm/Support/TargetSelect.h"
13#include "gtest/gtest.h"
14
15using namespace llvm;
16using namespace llvm::object;
17using namespace llvm::ELF;
18using namespace bolt;
19
20namespace {
21struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
22 void SetUp() override {
23 initalizeLLVM();
24 prepareElf();
25 initializeBOLT();
26 }
27
28protected:
29 void initalizeLLVM() {
30 llvm::InitializeAllTargetInfos();
31 llvm::InitializeAllTargetMCs();
32 llvm::InitializeAllAsmParsers();
33 llvm::InitializeAllDisassemblers();
34 llvm::InitializeAllTargets();
35 llvm::InitializeAllAsmPrinters();
36 }
37
38 void prepareElf() {
39 memcpy(dest: ElfBuf, src: "\177ELF", n: 4);
40 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
41 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
42 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
43 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
44 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
45 ObjFile = cantFail(ValOrErr: ObjectFile::createObjectFile(Object: Source));
46 }
47
48 void initializeBOLT() {
49 BC = cantFail(ValOrErr: BinaryContext::createBinaryContext(
50 TheTriple: ObjFile->makeTriple(), InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true,
51 DwCtx: DWARFContext::create(Obj: *ObjFile.get()), Logger: {.Out: llvm::outs(), .Err: llvm::errs()}));
52 ASSERT_FALSE(!BC);
53 }
54
55 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
56 std::unique_ptr<ObjectFile> ObjFile;
57 std::unique_ptr<BinaryContext> BC;
58};
59} // namespace
60
61#ifdef X86_AVAILABLE
62
63INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
64 ::testing::Values(Triple::x86_64));
65
66#endif
67
68#ifdef AARCH64_AVAILABLE
69
70INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
71 ::testing::Values(Triple::aarch64));
72
73TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
74 if (GetParam() != Triple::aarch64)
75 GTEST_SKIP();
76
77 // This test checks that encodeValueAArch64 used by flushPendingRelocations
78 // returns correctly encoded values for CALL26 relocation for both backward
79 // and forward branches.
80 //
81 // The offsets layout is:
82 // 4: func1
83 // 8: bl func1
84 // 12: bl func2
85 // 16: func2
86
87 constexpr size_t DataSize = 20;
88 uint8_t *Data = new uint8_t[DataSize];
89 BinarySection &BS = BC->registerOrUpdateSection(
90 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,
91 Size: DataSize, Alignment: 4);
92 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1");
93 ASSERT_TRUE(RelSymbol1);
94 BS.addRelocation(Offset: 8, Symbol: RelSymbol1, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true);
95 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2");
96 ASSERT_TRUE(RelSymbol2);
97 BS.addRelocation(Offset: 12, Symbol: RelSymbol2, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true);
98
99 std::error_code EC;
100 SmallVector<char> Vect(DataSize);
101 raw_svector_ostream OS(Vect);
102
103 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) {
104 return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
105 });
106
107 const uint8_t Func1Call[4] = {255, 255, 255, 151};
108 const uint8_t Func2Call[4] = {1, 0, 0, 148};
109
110 EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
111 EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
112}
113
114TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
115 if (GetParam() != Triple::aarch64)
116 GTEST_SKIP();
117
118 // This test checks that encodeValueAArch64 used by flushPendingRelocations
119 // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
120 // backward and forward branches.
121 //
122 // The offsets layout is:
123 // 4: func1
124 // 8: b func1
125 // 12: b func2
126 // 16: func2
127
128 const uint64_t Size = 20;
129 char *Data = new char[Size];
130 BinarySection &BS = BC->registerOrUpdateSection(
131 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
132 Data: (uint8_t *)Data, Size, Alignment: 4);
133 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1");
134 ASSERT_TRUE(RelSymbol1);
135 BS.addRelocation(Offset: 8, Symbol: RelSymbol1, Type: ELF::R_AARCH64_JUMP26, Addend: 0, Value: 0, Pending: true);
136 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2");
137 ASSERT_TRUE(RelSymbol2);
138 BS.addRelocation(Offset: 12, Symbol: RelSymbol2, Type: ELF::R_AARCH64_JUMP26, Addend: 0, Value: 0, Pending: true);
139
140 std::error_code EC;
141 SmallVector<char> Vect(Size);
142 raw_svector_ostream OS(Vect);
143
144 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) {
145 return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
146 });
147
148 const uint8_t Func1Call[4] = {255, 255, 255, 23};
149 const uint8_t Func2Call[4] = {1, 0, 0, 20};
150
151 EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4))
152 << "Wrong backward branch value\n";
153 EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4))
154 << "Wrong forward branch value\n";
155}
156
157#endif
158
159TEST_P(BinaryContextTester, BaseAddress) {
160 // Check that base address calculation is correct for a binary with the
161 // following segment layout:
162 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x10e8c2b4, .FileOffset: 0, .FileSize: 0x10e8c2b4, .Alignment: 0x1000};
163 BC->SegmentMapInfo[0x10e8d2b4] =
164 SegmentInfo{.Address: 0x10e8d2b4, .Size: 0x3952faec, .FileOffset: 0x10e8c2b4, .FileSize: 0x3952faec, .Alignment: 0x1000};
165 BC->SegmentMapInfo[0x4a3bddc0] =
166 SegmentInfo{.Address: 0x4a3bddc0, .Size: 0x148e828, .FileOffset: 0x4a3bbdc0, .FileSize: 0x148e828, .Alignment: 0x1000};
167 BC->SegmentMapInfo[0x4b84d5e8] =
168 SegmentInfo{.Address: 0x4b84d5e8, .Size: 0x294f830, .FileOffset: 0x4b84a5e8, .FileSize: 0x3d3820, .Alignment: 0x1000};
169
170 std::optional<uint64_t> BaseAddress =
171 BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x10e8c000);
172 ASSERT_TRUE(BaseAddress.has_value());
173 ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
174
175 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x137a000);
176 ASSERT_FALSE(BaseAddress.has_value());
177}
178
179TEST_P(BinaryContextTester, BaseAddress2) {
180 // Check that base address calculation is correct for a binary if the
181 // alignment in ELF file are different from pagesize.
182 // The segment layout is as follows:
183 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x2177c, .FileOffset: 0, .FileSize: 0x2177c, .Alignment: 0x10000};
184 BC->SegmentMapInfo[0x31860] =
185 SegmentInfo{.Address: 0x31860, .Size: 0x370, .FileOffset: 0x21860, .FileSize: 0x370, .Alignment: 0x10000};
186 BC->SegmentMapInfo[0x41c20] =
187 SegmentInfo{.Address: 0x41c20, .Size: 0x1f8, .FileOffset: 0x21c20, .FileSize: 0x1f8, .Alignment: 0x10000};
188 BC->SegmentMapInfo[0x54e18] =
189 SegmentInfo{.Address: 0x54e18, .Size: 0x51, .FileOffset: 0x24e18, .FileSize: 0x51, .Alignment: 0x10000};
190
191 std::optional<uint64_t> BaseAddress =
192 BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x21000);
193 ASSERT_TRUE(BaseAddress.has_value());
194 ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
195
196 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x11000);
197 ASSERT_FALSE(BaseAddress.has_value());
198}
199

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