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 | |
8 | using namespace llvm; |
9 | using namespace llvm::object; |
10 | using namespace llvm::ELF; |
11 | using namespace bolt; |
12 | |
13 | namespace { |
14 | struct BinaryContextTester : public testing::TestWithParam<Triple::ArchType> { |
15 | void SetUp() override { |
16 | initalizeLLVM(); |
17 | prepareElf(); |
18 | initializeBOLT(); |
19 | } |
20 | |
21 | protected: |
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 | |
56 | INSTANTIATE_TEST_SUITE_P(X86, BinaryContextTester, |
57 | ::testing::Values(Triple::x86_64)); |
58 | |
59 | #endif |
60 | |
61 | #ifdef AARCH64_AVAILABLE |
62 | |
63 | INSTANTIATE_TEST_SUITE_P(AArch64, BinaryContextTester, |
64 | ::testing::Values(Triple::aarch64)); |
65 | |
66 | TEST_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 | |
108 | TEST_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 | |
128 | TEST_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 | |