1#include "bolt/Core/BinaryContext.h"
2#include "llvm/BinaryFormat/ELF.h"
3#include "llvm/DebugInfo/DWARF/DWARFContext.h"
4#include "llvm/Object/ELFObjectFile.h"
5#include "llvm/Support/TargetSelect.h"
6#include "gtest/gtest.h"
7
8using namespace llvm;
9using namespace llvm::object;
10using namespace llvm::ELF;
11using namespace bolt;
12
13namespace {
14struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> {
15 void SetUp() override {
16 initalizeLLVM();
17 prepareElf();
18 initializeBOLT();
19 }
20
21protected:
22 void initalizeLLVM() {
23 llvm::InitializeAllTargetInfos();
24 llvm::InitializeAllTargetMCs();
25 llvm::InitializeAllAsmParsers();
26 llvm::InitializeAllDisassemblers();
27 llvm::InitializeAllTargets();
28 llvm::InitializeAllAsmPrinters();
29 }
30
31 void prepareElf() {
32 memcpy(dest: ElfBuf, src: "\177ELF", n: 4);
33 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
34 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
35 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
36 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
37 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
38 ObjFile = cantFail(ValOrErr: ObjectFile::createObjectFile(Object: Source));
39 }
40
41 void initializeBOLT() {
42 BC = cantFail(ValOrErr: BinaryContext::createBinaryContext(
43 File: ObjFile.get(), IsPIC: true, DwCtx: DWARFContext::create(Obj: *ObjFile.get()),
44 Logger: {.Out: llvm::outs(), .Err: llvm::errs()}));
45 ASSERT_FALSE(!BC);
46 }
47
48 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
49 std::unique_ptr<ObjectFile> ObjFile;
50 std::unique_ptr<BinaryContext> BC;
51};
52} // namespace
53
54#ifdef X86_AVAILABLE
55
56INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester,
57 ::testing::Values(Triple::x86_64));
58
59#endif
60
61#ifdef AARCH64_AVAILABLE
62
63INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester,
64 ::testing::Values(Triple::aarch64));
65
66TEST_P(BinaryContextTester, FlushPendingRelocCALL26) {
67 if (GetParam() != Triple::aarch64)
68 GTEST_SKIP();
69
70 // This test checks that encodeValueAArch64 used by flushPendingRelocations
71 // returns correctly encoded values for CALL26 relocation for both backward
72 // and forward branches.
73 //
74 // The offsets layout is:
75 // 4: func1
76 // 8: bl func1
77 // 12: bl func2
78 // 16: func2
79
80 char Data[20] = {};
81 BinarySection &BS = BC->registerOrUpdateSection(
82 Name: ".text", ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC,
83 Data: (uint8_t *)Data, Size: sizeof(Data), Alignment: 4);
84 MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1");
85 ASSERT_TRUE(RelSymbol1);
86 BS.addRelocation(Offset: 8, Symbol: RelSymbol1, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true);
87 MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2");
88 ASSERT_TRUE(RelSymbol2);
89 BS.addRelocation(Offset: 12, Symbol: RelSymbol2, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true);
90
91 std::error_code EC;
92 SmallVector<char> Vect(sizeof(Data));
93 raw_svector_ostream OS(Vect);
94
95 BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) {
96 return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0;
97 });
98
99 const uint8_t Func1Call[4] = {255, 255, 255, 151};
100 const uint8_t Func2Call[4] = {1, 0, 0, 148};
101
102 EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n";
103 EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n";
104}
105
106#endif
107
108TEST_P(BinaryContextTester, BaseAddress) {
109 // Check that base address calculation is correct for a binary with the
110 // following segment layout:
111 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x10e8c2b4, .FileOffset: 0, .FileSize: 0x10e8c2b4, .Alignment: 0x1000};
112 BC->SegmentMapInfo[0x10e8d2b4] =
113 SegmentInfo{.Address: 0x10e8d2b4, .Size: 0x3952faec, .FileOffset: 0x10e8c2b4, .FileSize: 0x3952faec, .Alignment: 0x1000};
114 BC->SegmentMapInfo[0x4a3bddc0] =
115 SegmentInfo{.Address: 0x4a3bddc0, .Size: 0x148e828, .FileOffset: 0x4a3bbdc0, .FileSize: 0x148e828, .Alignment: 0x1000};
116 BC->SegmentMapInfo[0x4b84d5e8] =
117 SegmentInfo{.Address: 0x4b84d5e8, .Size: 0x294f830, .FileOffset: 0x4b84a5e8, .FileSize: 0x3d3820, .Alignment: 0x1000};
118
119 std::optional<uint64_t> BaseAddress =
120 BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x10e8c000);
121 ASSERT_TRUE(BaseAddress.has_value());
122 ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL);
123
124 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x137a000);
125 ASSERT_FALSE(BaseAddress.has_value());
126}
127
128TEST_P(BinaryContextTester, BaseAddress2) {
129 // Check that base address calculation is correct for a binary if the
130 // alignment in ELF file are different from pagesize.
131 // The segment layout is as follows:
132 BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x2177c, .FileOffset: 0, .FileSize: 0x2177c, .Alignment: 0x10000};
133 BC->SegmentMapInfo[0x31860] =
134 SegmentInfo{.Address: 0x31860, .Size: 0x370, .FileOffset: 0x21860, .FileSize: 0x370, .Alignment: 0x10000};
135 BC->SegmentMapInfo[0x41c20] =
136 SegmentInfo{.Address: 0x41c20, .Size: 0x1f8, .FileOffset: 0x21c20, .FileSize: 0x1f8, .Alignment: 0x10000};
137 BC->SegmentMapInfo[0x54e18] =
138 SegmentInfo{.Address: 0x54e18, .Size: 0x51, .FileOffset: 0x24e18, .FileSize: 0x51, .Alignment: 0x10000};
139
140 std::optional<uint64_t> BaseAddress =
141 BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x21000);
142 ASSERT_TRUE(BaseAddress.has_value());
143 ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL);
144
145 BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x11000);
146 ASSERT_FALSE(BaseAddress.has_value());
147}
148

source code of bolt/unittests/Core/BinaryContext.cpp