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