xref: /llvm-project/bolt/unittests/Core/BinaryContext.cpp (revision 2ccf7ed277df28651b94bbee9fccefdf22fb074f)
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 
15 using namespace llvm;
16 using namespace llvm::object;
17 using namespace llvm::ELF;
18 using namespace bolt;
19 
20 namespace {
21 struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
22   void SetUp() override {
23     initalizeLLVM();
24     prepareElf();
25     initializeBOLT();
26   }
27 
28 protected:
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(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));
46   }
47 
48   void initializeBOLT() {
49     Relocation::Arch = ObjFile->makeTriple().getArch();
50     BC = cantFail(BinaryContext::createBinaryContext(
51         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
52         ObjFile->getFileName(), nullptr, true,
53         DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
54     ASSERT_FALSE(!BC);
55   }
56 
57   char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
58   std::unique_ptr<ObjectFile> ObjFile;
59   std::unique_ptr<BinaryContext> BC;
60 };
61 } // namespace
62 
63 #ifdef X86_AVAILABLE
64 
65 INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
66                          ::testing::Values(Triple::x86_64));
67 
68 #endif
69 
70 #ifdef AARCH64_AVAILABLE
71 
72 INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
73                          ::testing::Values(Triple::aarch64));
74 
75 TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
76   if (GetParam() != Triple::aarch64)
77     GTEST_SKIP();
78 
79   // This test checks that encodeValueAArch64 used by flushPendingRelocations
80   // returns correctly encoded values for CALL26 relocation for both backward
81   // and forward branches.
82   //
83   // The offsets layout is:
84   // 4:  func1
85   // 8:  bl func1
86   // 12: bl func2
87   // 16: func2
88 
89   constexpr size_t DataSize = 20;
90   uint8_t *Data = new uint8_t[DataSize];
91   BinarySection &BS = BC->registerOrUpdateSection(
92       ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,
93       DataSize, 4);
94   MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
95   ASSERT_TRUE(RelSymbol1);
96   BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0, true);
97   MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
98   ASSERT_TRUE(RelSymbol2);
99   BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0, true);
100 
101   std::error_code EC;
102   SmallVector<char> Vect(DataSize);
103   raw_svector_ostream OS(Vect);
104 
105   BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
106     return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
107   });
108 
109   const uint8_t Func1Call[4] = {255, 255, 255, 151};
110   const uint8_t Func2Call[4] = {1, 0, 0, 148};
111 
112   EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
113   EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
114 }
115 
116 TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
117   if (GetParam() != Triple::aarch64)
118     GTEST_SKIP();
119 
120   // This test checks that encodeValueAArch64 used by flushPendingRelocations
121   // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
122   // backward and forward branches.
123   //
124   // The offsets layout is:
125   // 4:  func1
126   // 8:  b func1
127   // 12: b func2
128   // 16: func2
129 
130   const uint64_t Size = 20;
131   char *Data = new char[Size];
132   BinarySection &BS = BC->registerOrUpdateSection(
133       ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
134       (uint8_t *)Data, Size, 4);
135   MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
136   ASSERT_TRUE(RelSymbol1);
137   BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0, true);
138   MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
139   ASSERT_TRUE(RelSymbol2);
140   BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0, true);
141 
142   std::error_code EC;
143   SmallVector<char> Vect(Size);
144   raw_svector_ostream OS(Vect);
145 
146   BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
147     return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
148   });
149 
150   const uint8_t Func1Call[4] = {255, 255, 255, 23};
151   const uint8_t Func2Call[4] = {1, 0, 0, 20};
152 
153   EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4))
154       << "Wrong backward branch value\n";
155   EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4))
156       << "Wrong forward branch value\n";
157 }
158 
159 #endif
160 
161 TEST_P(BinaryContextTester, BaseAddress) {
162   // Check that  base address calculation is correct for a binary with the
163   // following segment layout:
164   BC->SegmentMapInfo[0] =
165       SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true};
166   BC->SegmentMapInfo[0x10e8d2b4] =
167       SegmentInfo{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true};
168   BC->SegmentMapInfo[0x4a3bddc0] =
169       SegmentInfo{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true};
170   BC->SegmentMapInfo[0x4b84d5e8] =
171       SegmentInfo{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true};
172 
173   std::optional<uint64_t> BaseAddress =
174       BC->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);
175   ASSERT_TRUE(BaseAddress.has_value());
176   ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
177 
178   BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
179   ASSERT_FALSE(BaseAddress.has_value());
180 }
181 
182 TEST_P(BinaryContextTester, BaseAddress2) {
183   // Check that base address calculation is correct for a binary if the
184   // alignment in ELF file are different from pagesize.
185   // The segment layout is as follows:
186   BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000, true};
187   BC->SegmentMapInfo[0x31860] =
188       SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000, true};
189   BC->SegmentMapInfo[0x41c20] =
190       SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true};
191   BC->SegmentMapInfo[0x54e18] =
192       SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true};
193 
194   std::optional<uint64_t> BaseAddress =
195       BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
196   ASSERT_TRUE(BaseAddress.has_value());
197   ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
198 
199   BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
200   ASSERT_FALSE(BaseAddress.has_value());
201 }
202 
203 TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
204   // Check that the correct segment is used to compute the base address
205   // when multiple segments are close together in the ELF file (closer
206   // than the required alignment in the process space).
207   // See https://github.com/llvm/llvm-project/issues/109384
208   BC->SegmentMapInfo[0] = SegmentInfo{0, 0x1d1c, 0, 0x1d1c, 0x10000, false};
209   BC->SegmentMapInfo[0x11d40] =
210       SegmentInfo{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true};
211   BC->SegmentMapInfo[0x22f20] =
212       SegmentInfo{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false};
213   BC->SegmentMapInfo[0x33110] =
214       SegmentInfo{0x33110, 0x89, 0x3110, 0x88, 0x10000, false};
215 
216   std::optional<uint64_t> BaseAddress =
217       BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
218   ASSERT_TRUE(BaseAddress.has_value());
219   ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
220 }
221