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
18using namespace llvm;
19using namespace llvm::object;
20using namespace llvm::ELF;
21using namespace bolt;
22
23namespace {
24struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
25 void SetUp() override {
26 initalizeLLVM();
27 prepareElf();
28 initializeBolt();
29 }
30
31protected:
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
81INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
82 ::testing::Values(Triple::aarch64));
83
84TEST_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
91TEST_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
101INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester,
102 ::testing::Values(Triple::x86_64));
103
104TEST_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
110TEST_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
116TEST_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
135TEST_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

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