260 lines · cpp
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-exception6//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(ElfBuf, "\177ELF", 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(ObjectFile::createObjectFile(Source));50 }51 52 void initializeBOLT() {53 Relocation::Arch = ObjFile->makeTriple().getArch();54 BC = cantFail(BinaryContext::createBinaryContext(55 ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),56 ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),57 {llvm::outs(), 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} // namespace66 67#ifdef X86_AVAILABLE68 69INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,70 ::testing::Values(Triple::x86_64));71 72#endif73 74#ifdef AARCH64_AVAILABLE75 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 flushPendingRelocations84 // returns correctly encoded values for CALL26 relocation for both backward85 // and forward branches.86 //87 // The offsets layout is:88 // 4: func189 // 8: bl func190 // 12: bl func291 // 16: func292 93 constexpr size_t DataSize = 20;94 uint8_t *Data = new uint8_t[DataSize];95 BinarySection &BS = BC->registerOrUpdateSection(96 ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,97 DataSize, 4);98 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");99 ASSERT_TRUE(RelSymbol1);100 BS.addPendingRelocation(101 Relocation{8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0});102 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");103 ASSERT_TRUE(RelSymbol2);104 BS.addPendingRelocation(105 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, [&](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 flushPendingRelocations126 // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both127 // backward and forward branches.128 //129 // The offsets layout is:130 // 4: func1131 // 8: b func1132 // 12: b func2133 // 16: func2134 135 const uint64_t Size = 20;136 char *Data = new char[Size];137 BinarySection &BS = BC->registerOrUpdateSection(138 ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,139 (uint8_t *)Data, Size, 4);140 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");141 ASSERT_TRUE(RelSymbol1);142 BS.addPendingRelocation(143 Relocation{8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0});144 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");145 ASSERT_TRUE(RelSymbol2);146 BS.addPendingRelocation(147 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, [&](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 pending171 // 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 ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);179 MCSymbol *RelSymbol = BC->getOrCreateGlobalSymbol(4, "Func");180 ASSERT_TRUE(RelSymbol);181 Relocation Reloc{8, RelSymbol, ELF::R_AARCH64_CALL26, 0, 0};182 Reloc.setOptional();183 BS.addPendingRelocation(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, [&](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#endif197 198TEST_P(BinaryContextTester, BaseAddress) {199 // Check that base address calculation is correct for a binary with the200 // following segment layout:201 BC->SegmentMapInfo[0] =202 SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true, false};203 BC->SegmentMapInfo[0x10e8d2b4] = SegmentInfo{204 0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true, false};205 BC->SegmentMapInfo[0x4a3bddc0] = SegmentInfo{206 0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true, false};207 BC->SegmentMapInfo[0x4b84d5e8] = SegmentInfo{208 0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true, false};209 210 std::optional<uint64_t> BaseAddress =211 BC->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);212 ASSERT_TRUE(BaseAddress.has_value());213 ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);214 215 BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 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 the221 // alignment in ELF file are different from pagesize.222 // The segment layout is as follows:223 BC->SegmentMapInfo[0] =224 SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000, true, false};225 BC->SegmentMapInfo[0x31860] =226 SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000, true, false};227 BC->SegmentMapInfo[0x41c20] =228 SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true, false};229 BC->SegmentMapInfo[0x54e18] =230 SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true, false};231 232 std::optional<uint64_t> BaseAddress =233 BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);234 ASSERT_TRUE(BaseAddress.has_value());235 ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);236 237 BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);238 ASSERT_FALSE(BaseAddress.has_value());239}240 241TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {242 // Check that the correct segment is used to compute the base address243 // when multiple segments are close together in the ELF file (closer244 // than the required alignment in the process space).245 // See https://github.com/llvm/llvm-project/issues/109384246 BC->SegmentMapInfo[0] =247 SegmentInfo{0, 0x1d1c, 0, 0x1d1c, 0x10000, false, false};248 BC->SegmentMapInfo[0x11d40] =249 SegmentInfo{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true, false};250 BC->SegmentMapInfo[0x22f20] =251 SegmentInfo{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false, false};252 BC->SegmentMapInfo[0x33110] =253 SegmentInfo{0x33110, 0x89, 0x3110, 0x88, 0x10000, false, false};254 255 std::optional<uint64_t> BaseAddress =256 BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);257 ASSERT_TRUE(BaseAddress.has_value());258 ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);259}260