xref: /llvm-project/bolt/unittests/Core/BinaryContext.cpp (revision 2ccf7ed277df28651b94bbee9fccefdf22fb074f)
1fd38366eSAmir Ayupov //===- bolt/unittest/Core/BinaryContext.cpp -------------------------------===//
2fd38366eSAmir Ayupov //
3fd38366eSAmir Ayupov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fd38366eSAmir Ayupov // See https://llvm.org/LICENSE.txt for license information.
5fd38366eSAmir Ayupov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fd38366eSAmir Ayupov //
7fd38366eSAmir Ayupov //===----------------------------------------------------------------------===//
8fd38366eSAmir Ayupov 
977b75ca5SMaksim Panchenko #include "bolt/Core/BinaryContext.h"
1077b75ca5SMaksim Panchenko #include "llvm/BinaryFormat/ELF.h"
1177b75ca5SMaksim Panchenko #include "llvm/DebugInfo/DWARF/DWARFContext.h"
1277b75ca5SMaksim Panchenko #include "llvm/Support/TargetSelect.h"
1377b75ca5SMaksim Panchenko #include "gtest/gtest.h"
1477b75ca5SMaksim Panchenko 
1577b75ca5SMaksim Panchenko using namespace llvm;
1677b75ca5SMaksim Panchenko using namespace llvm::object;
1777b75ca5SMaksim Panchenko using namespace llvm::ELF;
1877b75ca5SMaksim Panchenko using namespace bolt;
1977b75ca5SMaksim Panchenko 
2077b75ca5SMaksim Panchenko namespace {
2177b75ca5SMaksim Panchenko struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
2277b75ca5SMaksim Panchenko   void SetUp() override {
2377b75ca5SMaksim Panchenko     initalizeLLVM();
2477b75ca5SMaksim Panchenko     prepareElf();
2577b75ca5SMaksim Panchenko     initializeBOLT();
2677b75ca5SMaksim Panchenko   }
2777b75ca5SMaksim Panchenko 
2877b75ca5SMaksim Panchenko protected:
2977b75ca5SMaksim Panchenko   void initalizeLLVM() {
3077b75ca5SMaksim Panchenko     llvm::InitializeAllTargetInfos();
3177b75ca5SMaksim Panchenko     llvm::InitializeAllTargetMCs();
3277b75ca5SMaksim Panchenko     llvm::InitializeAllAsmParsers();
3377b75ca5SMaksim Panchenko     llvm::InitializeAllDisassemblers();
3477b75ca5SMaksim Panchenko     llvm::InitializeAllTargets();
3577b75ca5SMaksim Panchenko     llvm::InitializeAllAsmPrinters();
3677b75ca5SMaksim Panchenko   }
3777b75ca5SMaksim Panchenko 
3877b75ca5SMaksim Panchenko   void prepareElf() {
3977b75ca5SMaksim Panchenko     memcpy(ElfBuf, "\177ELF", 4);
4077b75ca5SMaksim Panchenko     ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
4177b75ca5SMaksim Panchenko     EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
4277b75ca5SMaksim Panchenko     EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
4377b75ca5SMaksim Panchenko     EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
4477b75ca5SMaksim Panchenko     MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
4577b75ca5SMaksim Panchenko     ObjFile = cantFail(ObjectFile::createObjectFile(Source));
4677b75ca5SMaksim Panchenko   }
4777b75ca5SMaksim Panchenko 
4877b75ca5SMaksim Panchenko   void initializeBOLT() {
4962e894e0SSayhaan Siddiqui     Relocation::Arch = ObjFile->makeTriple().getArch();
5077b75ca5SMaksim Panchenko     BC = cantFail(BinaryContext::createBinaryContext(
51*2ccf7ed2SJared Wyles         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
52*2ccf7ed2SJared Wyles         ObjFile->getFileName(), nullptr, true,
53c0febca3SAmir Ayupov         DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
5477b75ca5SMaksim Panchenko     ASSERT_FALSE(!BC);
5577b75ca5SMaksim Panchenko   }
5677b75ca5SMaksim Panchenko 
5777b75ca5SMaksim Panchenko   char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
5877b75ca5SMaksim Panchenko   std::unique_ptr<ObjectFile> ObjFile;
5977b75ca5SMaksim Panchenko   std::unique_ptr<BinaryContext> BC;
6077b75ca5SMaksim Panchenko };
6177b75ca5SMaksim Panchenko } // namespace
6277b75ca5SMaksim Panchenko 
6377b75ca5SMaksim Panchenko #ifdef X86_AVAILABLE
6477b75ca5SMaksim Panchenko 
6577b75ca5SMaksim Panchenko INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
6677b75ca5SMaksim Panchenko                          ::testing::Values(Triple::x86_64));
6777b75ca5SMaksim Panchenko 
6877b75ca5SMaksim Panchenko #endif
6977b75ca5SMaksim Panchenko 
7077b75ca5SMaksim Panchenko #ifdef AARCH64_AVAILABLE
7177b75ca5SMaksim Panchenko 
7277b75ca5SMaksim Panchenko INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
7377b75ca5SMaksim Panchenko                          ::testing::Values(Triple::aarch64));
7477b75ca5SMaksim Panchenko 
75485075c0SVladislav Khmelevsky TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
76485075c0SVladislav Khmelevsky   if (GetParam() != Triple::aarch64)
77485075c0SVladislav Khmelevsky     GTEST_SKIP();
78485075c0SVladislav Khmelevsky 
79485075c0SVladislav Khmelevsky   // This test checks that encodeValueAArch64 used by flushPendingRelocations
80485075c0SVladislav Khmelevsky   // returns correctly encoded values for CALL26 relocation for both backward
81485075c0SVladislav Khmelevsky   // and forward branches.
82485075c0SVladislav Khmelevsky   //
83485075c0SVladislav Khmelevsky   // The offsets layout is:
84485075c0SVladislav Khmelevsky   // 4:  func1
85485075c0SVladislav Khmelevsky   // 8:  bl func1
86485075c0SVladislav Khmelevsky   // 12: bl func2
87485075c0SVladislav Khmelevsky   // 16: func2
88485075c0SVladislav Khmelevsky 
895daf2001SMaksim Panchenko   constexpr size_t DataSize = 20;
905daf2001SMaksim Panchenko   uint8_t *Data = new uint8_t[DataSize];
91485075c0SVladislav Khmelevsky   BinarySection &BS = BC->registerOrUpdateSection(
925daf2001SMaksim Panchenko       ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data,
935daf2001SMaksim Panchenko       DataSize, 4);
94485075c0SVladislav Khmelevsky   MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
95485075c0SVladislav Khmelevsky   ASSERT_TRUE(RelSymbol1);
96485075c0SVladislav Khmelevsky   BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_CALL26, 0, 0, true);
97485075c0SVladislav Khmelevsky   MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
98485075c0SVladislav Khmelevsky   ASSERT_TRUE(RelSymbol2);
99485075c0SVladislav Khmelevsky   BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_CALL26, 0, 0, true);
100485075c0SVladislav Khmelevsky 
101485075c0SVladislav Khmelevsky   std::error_code EC;
1025daf2001SMaksim Panchenko   SmallVector<char> Vect(DataSize);
103485075c0SVladislav Khmelevsky   raw_svector_ostream OS(Vect);
104485075c0SVladislav Khmelevsky 
105485075c0SVladislav Khmelevsky   BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
106485075c0SVladislav Khmelevsky     return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
107485075c0SVladislav Khmelevsky   });
108485075c0SVladislav Khmelevsky 
109485075c0SVladislav Khmelevsky   const uint8_t Func1Call[4] = {255, 255, 255, 151};
110485075c0SVladislav Khmelevsky   const uint8_t Func2Call[4] = {1, 0, 0, 148};
111485075c0SVladislav Khmelevsky 
112485075c0SVladislav Khmelevsky   EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
113485075c0SVladislav Khmelevsky   EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
114485075c0SVladislav Khmelevsky }
115485075c0SVladislav Khmelevsky 
11671c2a132Ssinan TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) {
11771c2a132Ssinan   if (GetParam() != Triple::aarch64)
11871c2a132Ssinan     GTEST_SKIP();
11971c2a132Ssinan 
12071c2a132Ssinan   // This test checks that encodeValueAArch64 used by flushPendingRelocations
12171c2a132Ssinan   // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both
12271c2a132Ssinan   // backward and forward branches.
12371c2a132Ssinan   //
12471c2a132Ssinan   // The offsets layout is:
12571c2a132Ssinan   // 4:  func1
12671c2a132Ssinan   // 8:  b func1
12771c2a132Ssinan   // 12: b func2
12871c2a132Ssinan   // 16: func2
12971c2a132Ssinan 
13071c2a132Ssinan   const uint64_t Size = 20;
13171c2a132Ssinan   char *Data = new char[Size];
13271c2a132Ssinan   BinarySection &BS = BC->registerOrUpdateSection(
13371c2a132Ssinan       ".text", ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
13471c2a132Ssinan       (uint8_t *)Data, Size, 4);
13571c2a132Ssinan   MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(4, "Func1");
13671c2a132Ssinan   ASSERT_TRUE(RelSymbol1);
13771c2a132Ssinan   BS.addRelocation(8, RelSymbol1, ELF::R_AARCH64_JUMP26, 0, 0, true);
13871c2a132Ssinan   MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(16, "Func2");
13971c2a132Ssinan   ASSERT_TRUE(RelSymbol2);
14071c2a132Ssinan   BS.addRelocation(12, RelSymbol2, ELF::R_AARCH64_JUMP26, 0, 0, true);
14171c2a132Ssinan 
14271c2a132Ssinan   std::error_code EC;
14371c2a132Ssinan   SmallVector<char> Vect(Size);
14471c2a132Ssinan   raw_svector_ostream OS(Vect);
14571c2a132Ssinan 
14671c2a132Ssinan   BS.flushPendingRelocations(OS, [&](const MCSymbol *S) {
14771c2a132Ssinan     return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
14871c2a132Ssinan   });
14971c2a132Ssinan 
15071c2a132Ssinan   const uint8_t Func1Call[4] = {255, 255, 255, 23};
15171c2a132Ssinan   const uint8_t Func2Call[4] = {1, 0, 0, 20};
15271c2a132Ssinan 
15371c2a132Ssinan   EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4))
15471c2a132Ssinan       << "Wrong backward branch value\n";
15571c2a132Ssinan   EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4))
15671c2a132Ssinan       << "Wrong forward branch value\n";
15771c2a132Ssinan }
15871c2a132Ssinan 
15977b75ca5SMaksim Panchenko #endif
16077b75ca5SMaksim Panchenko 
16177b75ca5SMaksim Panchenko TEST_P(BinaryContextTester, BaseAddress) {
16277b75ca5SMaksim Panchenko   // Check that  base address calculation is correct for a binary with the
16377b75ca5SMaksim Panchenko   // following segment layout:
1646d216fb7SKristof Beyls   BC->SegmentMapInfo[0] =
1656d216fb7SKristof Beyls       SegmentInfo{0, 0x10e8c2b4, 0, 0x10e8c2b4, 0x1000, true};
16677b75ca5SMaksim Panchenko   BC->SegmentMapInfo[0x10e8d2b4] =
1676d216fb7SKristof Beyls       SegmentInfo{0x10e8d2b4, 0x3952faec, 0x10e8c2b4, 0x3952faec, 0x1000, true};
16877b75ca5SMaksim Panchenko   BC->SegmentMapInfo[0x4a3bddc0] =
1696d216fb7SKristof Beyls       SegmentInfo{0x4a3bddc0, 0x148e828, 0x4a3bbdc0, 0x148e828, 0x1000, true};
17077b75ca5SMaksim Panchenko   BC->SegmentMapInfo[0x4b84d5e8] =
1716d216fb7SKristof Beyls       SegmentInfo{0x4b84d5e8, 0x294f830, 0x4b84a5e8, 0x3d3820, 0x1000, true};
17277b75ca5SMaksim Panchenko 
1739cbd2959SAmir Ayupov   std::optional<uint64_t> BaseAddress =
17477b75ca5SMaksim Panchenko       BC->getBaseAddressForMapping(0x7f13f5556000, 0x10e8c000);
1757430894aSFangrui Song   ASSERT_TRUE(BaseAddress.has_value());
17677b75ca5SMaksim Panchenko   ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
17777b75ca5SMaksim Panchenko 
17877b75ca5SMaksim Panchenko   BaseAddress = BC->getBaseAddressForMapping(0x7f13f5556000, 0x137a000);
1797430894aSFangrui Song   ASSERT_FALSE(BaseAddress.has_value());
18077b75ca5SMaksim Panchenko }
181ae51ec84SJohnLee1243 
182ae51ec84SJohnLee1243 TEST_P(BinaryContextTester, BaseAddress2) {
183ae51ec84SJohnLee1243   // Check that base address calculation is correct for a binary if the
184ae51ec84SJohnLee1243   // alignment in ELF file are different from pagesize.
185ae51ec84SJohnLee1243   // The segment layout is as follows:
1866d216fb7SKristof Beyls   BC->SegmentMapInfo[0] = SegmentInfo{0, 0x2177c, 0, 0x2177c, 0x10000, true};
187ae51ec84SJohnLee1243   BC->SegmentMapInfo[0x31860] =
1886d216fb7SKristof Beyls       SegmentInfo{0x31860, 0x370, 0x21860, 0x370, 0x10000, true};
189ae51ec84SJohnLee1243   BC->SegmentMapInfo[0x41c20] =
1906d216fb7SKristof Beyls       SegmentInfo{0x41c20, 0x1f8, 0x21c20, 0x1f8, 0x10000, true};
191ae51ec84SJohnLee1243   BC->SegmentMapInfo[0x54e18] =
1926d216fb7SKristof Beyls       SegmentInfo{0x54e18, 0x51, 0x24e18, 0x51, 0x10000, true};
193ae51ec84SJohnLee1243 
194ae51ec84SJohnLee1243   std::optional<uint64_t> BaseAddress =
195ae51ec84SJohnLee1243       BC->getBaseAddressForMapping(0xaaaaea444000, 0x21000);
196ae51ec84SJohnLee1243   ASSERT_TRUE(BaseAddress.has_value());
197ae51ec84SJohnLee1243   ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
198ae51ec84SJohnLee1243 
199ae51ec84SJohnLee1243   BaseAddress = BC->getBaseAddressForMapping(0xaaaaea444000, 0x11000);
200ae51ec84SJohnLee1243   ASSERT_FALSE(BaseAddress.has_value());
201ae51ec84SJohnLee1243 }
2026d216fb7SKristof Beyls 
2036d216fb7SKristof Beyls TEST_P(BinaryContextTester, BaseAddressSegmentsSmallerThanAlignment) {
2046d216fb7SKristof Beyls   // Check that the correct segment is used to compute the base address
2056d216fb7SKristof Beyls   // when multiple segments are close together in the ELF file (closer
2066d216fb7SKristof Beyls   // than the required alignment in the process space).
2076d216fb7SKristof Beyls   // See https://github.com/llvm/llvm-project/issues/109384
2086d216fb7SKristof Beyls   BC->SegmentMapInfo[0] = SegmentInfo{0, 0x1d1c, 0, 0x1d1c, 0x10000, false};
2096d216fb7SKristof Beyls   BC->SegmentMapInfo[0x11d40] =
2106d216fb7SKristof Beyls       SegmentInfo{0x11d40, 0x11e0, 0x1d40, 0x11e0, 0x10000, true};
2116d216fb7SKristof Beyls   BC->SegmentMapInfo[0x22f20] =
2126d216fb7SKristof Beyls       SegmentInfo{0x22f20, 0x10e0, 0x2f20, 0x1f0, 0x10000, false};
2136d216fb7SKristof Beyls   BC->SegmentMapInfo[0x33110] =
2146d216fb7SKristof Beyls       SegmentInfo{0x33110, 0x89, 0x3110, 0x88, 0x10000, false};
2156d216fb7SKristof Beyls 
2166d216fb7SKristof Beyls   std::optional<uint64_t> BaseAddress =
2176d216fb7SKristof Beyls       BC->getBaseAddressForMapping(0xaaaaaaab1000, 0x1000);
2186d216fb7SKristof Beyls   ASSERT_TRUE(BaseAddress.has_value());
2196d216fb7SKristof Beyls   ASSERT_EQ(*BaseAddress, 0xaaaaaaaa0000ULL);
2206d216fb7SKristof Beyls }
221