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(dest: ElfBuf, src: "\177ELF" , n: 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(ValOrErr: ObjectFile::createObjectFile(Object: Source)); |
46 | } |
47 | |
48 | void initializeBOLT() { |
49 | BC = cantFail(ValOrErr: BinaryContext::createBinaryContext( |
50 | TheTriple: ObjFile->makeTriple(), InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true, |
51 | DwCtx: DWARFContext::create(Obj: *ObjFile.get()), Logger: {.Out: llvm::outs(), .Err: llvm::errs()})); |
52 | ASSERT_FALSE(!BC); |
53 | } |
54 | |
55 | char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {}; |
56 | std::unique_ptr<ObjectFile> ObjFile; |
57 | std::unique_ptr<BinaryContext> BC; |
58 | }; |
59 | } // namespace |
60 | |
61 | #ifdef X86_AVAILABLE |
62 | |
63 | INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester, |
64 | ::testing::Values(Triple::x86_64)); |
65 | |
66 | #endif |
67 | |
68 | #ifdef AARCH64_AVAILABLE |
69 | |
70 | INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester, |
71 | ::testing::Values(Triple::aarch64)); |
72 | |
73 | TEST_P(BinaryContextTester, FlushPendingRelocCALL26) { |
74 | if (GetParam() != Triple::aarch64) |
75 | GTEST_SKIP(); |
76 | |
77 | // This test checks that encodeValueAArch64 used by flushPendingRelocations |
78 | // returns correctly encoded values for CALL26 relocation for both backward |
79 | // and forward branches. |
80 | // |
81 | // The offsets layout is: |
82 | // 4: func1 |
83 | // 8: bl func1 |
84 | // 12: bl func2 |
85 | // 16: func2 |
86 | |
87 | constexpr size_t DataSize = 20; |
88 | uint8_t *Data = new uint8_t[DataSize]; |
89 | BinarySection &BS = BC->registerOrUpdateSection( |
90 | Name: ".text" , ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, Data, |
91 | Size: DataSize, Alignment: 4); |
92 | MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1" ); |
93 | ASSERT_TRUE(RelSymbol1); |
94 | BS.addRelocation(Offset: 8, Symbol: RelSymbol1, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true); |
95 | MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2" ); |
96 | ASSERT_TRUE(RelSymbol2); |
97 | BS.addRelocation(Offset: 12, Symbol: RelSymbol2, Type: ELF::R_AARCH64_CALL26, Addend: 0, Value: 0, Pending: true); |
98 | |
99 | std::error_code EC; |
100 | SmallVector<char> Vect(DataSize); |
101 | raw_svector_ostream OS(Vect); |
102 | |
103 | BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) { |
104 | return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0; |
105 | }); |
106 | |
107 | const uint8_t Func1Call[4] = {255, 255, 255, 151}; |
108 | const uint8_t Func2Call[4] = {1, 0, 0, 148}; |
109 | |
110 | EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) << "Wrong backward call value\n" ; |
111 | EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) << "Wrong forward call value\n" ; |
112 | } |
113 | |
114 | TEST_P(BinaryContextTester, FlushPendingRelocJUMP26) { |
115 | if (GetParam() != Triple::aarch64) |
116 | GTEST_SKIP(); |
117 | |
118 | // This test checks that encodeValueAArch64 used by flushPendingRelocations |
119 | // returns correctly encoded values for R_AARCH64_JUMP26 relocation for both |
120 | // backward and forward branches. |
121 | // |
122 | // The offsets layout is: |
123 | // 4: func1 |
124 | // 8: b func1 |
125 | // 12: b func2 |
126 | // 16: func2 |
127 | |
128 | const uint64_t Size = 20; |
129 | char *Data = new char[Size]; |
130 | BinarySection &BS = BC->registerOrUpdateSection( |
131 | Name: ".text" , ELFType: ELF::SHT_PROGBITS, ELFFlags: ELF::SHF_EXECINSTR | ELF::SHF_ALLOC, |
132 | Data: (uint8_t *)Data, Size, Alignment: 4); |
133 | MCSymbol *RelSymbol1 = BC->getOrCreateGlobalSymbol(Address: 4, Prefix: "Func1" ); |
134 | ASSERT_TRUE(RelSymbol1); |
135 | BS.addRelocation(Offset: 8, Symbol: RelSymbol1, Type: ELF::R_AARCH64_JUMP26, Addend: 0, Value: 0, Pending: true); |
136 | MCSymbol *RelSymbol2 = BC->getOrCreateGlobalSymbol(Address: 16, Prefix: "Func2" ); |
137 | ASSERT_TRUE(RelSymbol2); |
138 | BS.addRelocation(Offset: 12, Symbol: RelSymbol2, Type: ELF::R_AARCH64_JUMP26, Addend: 0, Value: 0, Pending: true); |
139 | |
140 | std::error_code EC; |
141 | SmallVector<char> Vect(Size); |
142 | raw_svector_ostream OS(Vect); |
143 | |
144 | BS.flushPendingRelocations(OS, Resolver: [&](const MCSymbol *S) { |
145 | return S == RelSymbol1 ? 4 : S == RelSymbol2 ? 16 : 0; |
146 | }); |
147 | |
148 | const uint8_t Func1Call[4] = {255, 255, 255, 23}; |
149 | const uint8_t Func2Call[4] = {1, 0, 0, 20}; |
150 | |
151 | EXPECT_FALSE(memcmp(Func1Call, &Vect[8], 4)) |
152 | << "Wrong backward branch value\n" ; |
153 | EXPECT_FALSE(memcmp(Func2Call, &Vect[12], 4)) |
154 | << "Wrong forward branch value\n" ; |
155 | } |
156 | |
157 | #endif |
158 | |
159 | TEST_P(BinaryContextTester, BaseAddress) { |
160 | // Check that base address calculation is correct for a binary with the |
161 | // following segment layout: |
162 | BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x10e8c2b4, .FileOffset: 0, .FileSize: 0x10e8c2b4, .Alignment: 0x1000}; |
163 | BC->SegmentMapInfo[0x10e8d2b4] = |
164 | SegmentInfo{.Address: 0x10e8d2b4, .Size: 0x3952faec, .FileOffset: 0x10e8c2b4, .FileSize: 0x3952faec, .Alignment: 0x1000}; |
165 | BC->SegmentMapInfo[0x4a3bddc0] = |
166 | SegmentInfo{.Address: 0x4a3bddc0, .Size: 0x148e828, .FileOffset: 0x4a3bbdc0, .FileSize: 0x148e828, .Alignment: 0x1000}; |
167 | BC->SegmentMapInfo[0x4b84d5e8] = |
168 | SegmentInfo{.Address: 0x4b84d5e8, .Size: 0x294f830, .FileOffset: 0x4b84a5e8, .FileSize: 0x3d3820, .Alignment: 0x1000}; |
169 | |
170 | std::optional<uint64_t> BaseAddress = |
171 | BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x10e8c000); |
172 | ASSERT_TRUE(BaseAddress.has_value()); |
173 | ASSERT_EQ(*BaseAddress, 0x7f13e46c9000ULL); |
174 | |
175 | BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0x7f13f5556000, FileOffset: 0x137a000); |
176 | ASSERT_FALSE(BaseAddress.has_value()); |
177 | } |
178 | |
179 | TEST_P(BinaryContextTester, BaseAddress2) { |
180 | // Check that base address calculation is correct for a binary if the |
181 | // alignment in ELF file are different from pagesize. |
182 | // The segment layout is as follows: |
183 | BC->SegmentMapInfo[0] = SegmentInfo{.Address: 0, .Size: 0x2177c, .FileOffset: 0, .FileSize: 0x2177c, .Alignment: 0x10000}; |
184 | BC->SegmentMapInfo[0x31860] = |
185 | SegmentInfo{.Address: 0x31860, .Size: 0x370, .FileOffset: 0x21860, .FileSize: 0x370, .Alignment: 0x10000}; |
186 | BC->SegmentMapInfo[0x41c20] = |
187 | SegmentInfo{.Address: 0x41c20, .Size: 0x1f8, .FileOffset: 0x21c20, .FileSize: 0x1f8, .Alignment: 0x10000}; |
188 | BC->SegmentMapInfo[0x54e18] = |
189 | SegmentInfo{.Address: 0x54e18, .Size: 0x51, .FileOffset: 0x24e18, .FileSize: 0x51, .Alignment: 0x10000}; |
190 | |
191 | std::optional<uint64_t> BaseAddress = |
192 | BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x21000); |
193 | ASSERT_TRUE(BaseAddress.has_value()); |
194 | ASSERT_EQ(*BaseAddress, 0xaaaaea413000ULL); |
195 | |
196 | BaseAddress = BC->getBaseAddressForMapping(MMapAddress: 0xaaaaea444000, FileOffset: 0x11000); |
197 | ASSERT_FALSE(BaseAddress.has_value()); |
198 | } |
199 | |