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