1 //===- bolt/unittest/Core/MCPlusBuilder.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 #ifdef AARCH64_AVAILABLE 10 #include "AArch64Subtarget.h" 11 #endif // AARCH64_AVAILABLE 12 13 #ifdef X86_AVAILABLE 14 #include "X86Subtarget.h" 15 #endif // X86_AVAILABLE 16 17 #include "bolt/Core/BinaryBasicBlock.h" 18 #include "bolt/Core/BinaryFunction.h" 19 #include "bolt/Rewrite/RewriteInstance.h" 20 #include "llvm/BinaryFormat/ELF.h" 21 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 22 #include "llvm/Support/TargetSelect.h" 23 #include "gtest/gtest.h" 24 25 using namespace llvm; 26 using namespace llvm::object; 27 using namespace llvm::ELF; 28 using namespace bolt; 29 30 namespace { 31 struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> { 32 void SetUp() override { 33 initalizeLLVM(); 34 prepareElf(); 35 initializeBolt(); 36 } 37 38 protected: 39 void initalizeLLVM() { 40 llvm::InitializeAllTargetInfos(); 41 llvm::InitializeAllTargetMCs(); 42 llvm::InitializeAllAsmParsers(); 43 llvm::InitializeAllDisassemblers(); 44 llvm::InitializeAllTargets(); 45 llvm::InitializeAllAsmPrinters(); 46 } 47 48 void prepareElf() { 49 memcpy(ElfBuf, "\177ELF", 4); 50 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf); 51 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64; 52 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB; 53 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64; 54 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF"); 55 ObjFile = cantFail(ObjectFile::createObjectFile(Source)); 56 } 57 58 void initializeBolt() { 59 Relocation::Arch = ObjFile->makeTriple().getArch(); 60 BC = cantFail(BinaryContext::createBinaryContext( 61 ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(), 62 ObjFile->getFileName(), nullptr, true, 63 DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()})); 64 ASSERT_FALSE(!BC); 65 BC->initializeTarget(std::unique_ptr<MCPlusBuilder>( 66 createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(), 67 BC->MRI.get(), BC->STI.get()))); 68 } 69 70 void testRegAliases(Triple::ArchType Arch, uint64_t Register, 71 uint64_t *Aliases, size_t Count, 72 bool OnlySmaller = false) { 73 if (GetParam() != Arch) 74 GTEST_SKIP(); 75 76 const BitVector &BV = BC->MIB->getAliases(Register, OnlySmaller); 77 ASSERT_EQ(BV.count(), Count); 78 for (size_t I = 0; I < Count; ++I) 79 ASSERT_TRUE(BV[Aliases[I]]); 80 } 81 82 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {}; 83 std::unique_ptr<ObjectFile> ObjFile; 84 std::unique_ptr<BinaryContext> BC; 85 }; 86 } // namespace 87 88 #ifdef AARCH64_AVAILABLE 89 90 INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester, 91 ::testing::Values(Triple::aarch64)); 92 93 TEST_P(MCPlusBuilderTester, AliasX0) { 94 uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, 95 AArch64::X0, AArch64::W0_W1, 96 AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7}; 97 size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0); 98 testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count); 99 } 100 101 TEST_P(MCPlusBuilderTester, AliasSmallerX0) { 102 uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, AArch64::X0}; 103 size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0); 104 testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true); 105 } 106 107 #endif // AARCH64_AVAILABLE 108 109 #ifdef X86_AVAILABLE 110 111 INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester, 112 ::testing::Values(Triple::x86_64)); 113 114 TEST_P(MCPlusBuilderTester, AliasAX) { 115 uint64_t AliasesAX[] = {X86::RAX, X86::EAX, X86::AX, X86::AL, X86::AH}; 116 size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX); 117 testRegAliases(Triple::x86_64, X86::AX, AliasesAX, AliasesAXCount); 118 } 119 120 TEST_P(MCPlusBuilderTester, AliasSmallerAX) { 121 uint64_t AliasesAX[] = {X86::AX, X86::AL, X86::AH}; 122 size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX); 123 testRegAliases(Triple::x86_64, X86::AX, AliasesAX, AliasesAXCount, true); 124 } 125 126 TEST_P(MCPlusBuilderTester, ReplaceRegWithImm) { 127 if (GetParam() != Triple::x86_64) 128 GTEST_SKIP(); 129 BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); 130 std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); 131 MCInst Inst; // cmpl %eax, %ebx 132 Inst.setOpcode(X86::CMP32rr); 133 Inst.addOperand(MCOperand::createReg(X86::EAX)); 134 Inst.addOperand(MCOperand::createReg(X86::EBX)); 135 auto II = BB->addInstruction(Inst); 136 bool Replaced = BC->MIB->replaceRegWithImm(*II, X86::EBX, 1); 137 ASSERT_TRUE(Replaced); 138 ASSERT_EQ(II->getOpcode(), X86::CMP32ri8); 139 ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX); 140 ASSERT_EQ(II->getOperand(1).getImm(), 1); 141 } 142 143 #endif // X86_AVAILABLE 144 145 TEST_P(MCPlusBuilderTester, Annotation) { 146 MCInst Inst; 147 BC->MIB->createTailCall(Inst, BC->Ctx->createNamedTempSymbol(), 148 BC->Ctx.get()); 149 MCSymbol *LPSymbol = BC->Ctx->createNamedTempSymbol("LP"); 150 uint64_t Value = INT32_MIN; 151 // Test encodeAnnotationImm using this indirect way 152 BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value)); 153 // Round-trip encoding-decoding check for negative values 154 std::optional<MCPlus::MCLandingPad> EHInfo = BC->MIB->getEHInfo(Inst); 155 ASSERT_TRUE(EHInfo.has_value()); 156 MCPlus::MCLandingPad LP = EHInfo.value(); 157 uint64_t DecodedValue = LP.second; 158 ASSERT_EQ(Value, DecodedValue); 159 160 // Large int64 should trigger an out of range assertion 161 Value = 0x1FF'FFFF'FFFF'FFFFULL; 162 Inst.clear(); 163 BC->MIB->createTailCall(Inst, BC->Ctx->createNamedTempSymbol(), 164 BC->Ctx.get()); 165 ASSERT_DEATH(BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value)), 166 "annotation value out of range"); 167 } 168