1 //===- bolt/unittest/Core/BinaryContext.cpp -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
16 using namespace llvm::object
;
17 using namespace llvm::ELF
;
21 struct BinaryContextTester
: public testing::TestWithParam
<Triple::ArchType
> {
22 void SetUp() override
{
29 void initalizeLLVM() {
30 llvm::InitializeAllTargetInfos();
31 llvm::InitializeAllTargetMCs();
32 llvm::InitializeAllAsmParsers();
33 llvm::InitializeAllDisassemblers();
34 llvm::InitializeAllTargets();
35 llvm::InitializeAllAsmPrinters();
39 memcpy(ElfBuf
, "\177ELF", 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(ObjectFile::createObjectFile(Source
));
48 void initializeBOLT() {
49 Relocation::Arch
= ObjFile
->makeTriple().getArch();
50 BC
= cantFail(BinaryContext::createBinaryContext(
51 ObjFile
->makeTriple(), ObjFile
->getFileName(), nullptr, true,
52 DWARFContext::create(*ObjFile
.get()), {llvm::outs(), llvm::errs()}));
56 char ElfBuf
[sizeof(typename
ELF64LE::Ehdr
)] = {};
57 std::unique_ptr
<ObjectFile
> ObjFile
;
58 std::unique_ptr
<BinaryContext
> BC
;
64 INSTANTIATE_TEST_SUITE_P(X86
, BinaryContextTester
,
65 ::testing::Values(Triple::x86_64
));
69 #ifdef AARCH64_AVAILABLE
71 INSTANTIATE_TEST_SUITE_P(AArch64
, BinaryContextTester
,
72 ::testing::Values(Triple::aarch64
));
74 TEST_P(BinaryContextTester
, FlushPendingRelocCALL26
) {
75 if (GetParam() != Triple::aarch64
)
78 // This test checks that encodeValueAArch64 used by flushPendingRelocations
79 // returns correctly encoded values for CALL26 relocation for both backward
80 // and forward branches.
82 // The offsets layout is:
88 constexpr size_t DataSize
= 20;
89 uint8_t *Data
= new uint8_t[DataSize
];
90 BinarySection
&BS
= BC
->registerOrUpdateSection(
91 ".text", ELF::SHT_PROGBITS
, ELF::SHF_EXECINSTR
| ELF::SHF_ALLOC
, Data
,
93 MCSymbol
*RelSymbol1
= BC
->getOrCreateGlobalSymbol(4, "Func1");
94 ASSERT_TRUE(RelSymbol1
);
95 BS
.addRelocation(8, RelSymbol1
, ELF::R_AARCH64_CALL26
, 0, 0, true);
96 MCSymbol
*RelSymbol2
= BC
->getOrCreateGlobalSymbol(16, "Func2");
97 ASSERT_TRUE(RelSymbol2
);
98 BS
.addRelocation(12, RelSymbol2
, ELF::R_AARCH64_CALL26
, 0, 0, true);
101 SmallVector
<char> Vect(DataSize
);
102 raw_svector_ostream
OS(Vect
);
104 BS
.flushPendingRelocations(OS
, [&](const MCSymbol
*S
) {
105 return S
== RelSymbol1
? 4 : S
== RelSymbol2
? 16 : 0;
108 const uint8_t Func1Call
[4] = {255, 255, 255, 151};
109 const uint8_t Func2Call
[4] = {1, 0, 0, 148};
111 EXPECT_FALSE(memcmp(Func1Call
, &Vect
[8], 4)) << "Wrong backward call value\n";
112 EXPECT_FALSE(memcmp(Func2Call
, &Vect
[12], 4)) << "Wrong forward call value\n";
115 TEST_P(BinaryContextTester
, FlushPendingRelocJUMP26
) {
116 if (GetParam() != Triple::aarch64
)
119 // This test checks that encodeValueAArch64 used by flushPendingRelocations
120 // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
121 // backward and forward branches.
123 // The offsets layout is:
129 const uint64_t Size
= 20;
130 char *Data
= new char[Size
];
131 BinarySection
&BS
= BC
->registerOrUpdateSection(
132 ".text", ELF::SHT_PROGBITS
, ELF::SHF_EXECINSTR
| ELF::SHF_ALLOC
,
133 (uint8_t *)Data
, Size
, 4);
134 MCSymbol
*RelSymbol1
= BC
->getOrCreateGlobalSymbol(4, "Func1");
135 ASSERT_TRUE(RelSymbol1
);
136 BS
.addRelocation(8, RelSymbol1
, ELF::R_AARCH64_JUMP26
, 0, 0, true);
137 MCSymbol
*RelSymbol2
= BC
->getOrCreateGlobalSymbol(16, "Func2");
138 ASSERT_TRUE(RelSymbol2
);
139 BS
.addRelocation(12, RelSymbol2
, ELF::R_AARCH64_JUMP26
, 0, 0, true);
142 SmallVector
<char> Vect(Size
);
143 raw_svector_ostream
OS(Vect
);
145 BS
.flushPendingRelocations(OS
, [&](const MCSymbol
*S
) {
146 return S
== RelSymbol1
? 4 : S
== RelSymbol2
? 16 : 0;
149 const uint8_t Func1Call
[4] = {255, 255, 255, 23};
150 const uint8_t Func2Call
[4] = {1, 0, 0, 20};
152 EXPECT_FALSE(memcmp(Func1Call
, &Vect
[8], 4))
153 << "Wrong backward branch value\n";
154 EXPECT_FALSE(memcmp(Func2Call
, &Vect
[12], 4))
155 << "Wrong forward branch value\n";
160 TEST_P(BinaryContextTester
, BaseAddress
) {
161 // Check that base address calculation is correct for a binary with the
162 // following segment layout:
163 BC
->SegmentMapInfo
[0] =
164 SegmentInfo
{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true};
165 BC
->SegmentMapInfo
[0x10e8d2b4] =
166 SegmentInfo
{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true};
167 BC
->SegmentMapInfo
[0x4a3bddc0] =
168 SegmentInfo
{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true};
169 BC
->SegmentMapInfo
[0x4b84d5e8] =
170 SegmentInfo
{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true};
172 std::optional
<uint64_t> BaseAddress
=
173 BC
->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);
174 ASSERT_TRUE(BaseAddress
.has_value());
175 ASSERT_EQ(*BaseAddress
, 0x7f13e46c9000ULL
);
177 BaseAddress
= BC
->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
178 ASSERT_FALSE(BaseAddress
.has_value());
181 TEST_P(BinaryContextTester
, BaseAddress2
) {
182 // Check that base address calculation is correct for a binary if the
183 // alignment in ELF file are different from pagesize.
184 // The segment layout is as follows:
185 BC
->SegmentMapInfo
[0] = SegmentInfo
{0, 0x2177c, 0, 0x2177c, 0x10000, true};
186 BC
->SegmentMapInfo
[0x31860] =
187 SegmentInfo
{0x31860, 0x370, 0x21860, 0x370, 0x10000, true};
188 BC
->SegmentMapInfo
[0x41c20] =
189 SegmentInfo
{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true};
190 BC
->SegmentMapInfo
[0x54e18] =
191 SegmentInfo
{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true};
193 std::optional
<uint64_t> BaseAddress
=
194 BC
->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
195 ASSERT_TRUE(BaseAddress
.has_value());
196 ASSERT_EQ(*BaseAddress
, 0xaaaaea413000ULL
);
198 BaseAddress
= BC
->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
199 ASSERT_FALSE(BaseAddress
.has_value());
202 TEST_P(BinaryContextTester
, BaseAddressSegmentsSmallerThanAlignment
) {
203 // Check that the correct segment is used to compute the base address
204 // when multiple segments are close together in the ELF file (closer
205 // than the required alignment in the process space).
206 // See https://github.com/llvm/llvm-project/issues/109384
207 BC
->SegmentMapInfo
[0] = SegmentInfo
{0, 0x1d1c, 0, 0x1d1c, 0x10000, false};
208 BC
->SegmentMapInfo
[0x11d40] =
209 SegmentInfo
{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true};
210 BC
->SegmentMapInfo
[0x22f20] =
211 SegmentInfo
{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false};
212 BC
->SegmentMapInfo
[0x33110] =
213 SegmentInfo
{0x33110, 0x89, 0x3110, 0x88, 0x10000, false};
215 std::optional
<uint64_t> BaseAddress
=
216 BC
->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
217 ASSERT_TRUE(BaseAddress
.has_value());
218 ASSERT_EQ(*BaseAddress
, 0xaaaaaaaa0000ULL
);