brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.3 KiB · d7374b3 Raw
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