1 //===- X86MCDisassemblerTest.cpp - Tests for X86 MCDisassembler -----------===// 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 "llvm/MC/MCAsmInfo.h" 10 #include "llvm/MC/MCContext.h" 11 #include "llvm/MC/MCDisassembler/MCDisassembler.h" 12 #include "llvm/MC/MCDisassembler/MCSymbolizer.h" 13 #include "llvm/MC/MCInst.h" 14 #include "llvm/MC/MCRegisterInfo.h" 15 #include "llvm/MC/MCSubtargetInfo.h" 16 #include "llvm/MC/MCTargetOptions.h" 17 #include "llvm/MC/TargetRegistry.h" 18 #include "llvm/Support/TargetSelect.h" 19 #include "gtest/gtest.h" 20 21 using namespace llvm; 22 23 namespace { 24 25 struct Context { 26 const char *TripleName = "x86_64-unknown-elf"; 27 std::unique_ptr<MCRegisterInfo> MRI; 28 std::unique_ptr<MCAsmInfo> MAI; 29 std::unique_ptr<MCContext> Ctx; 30 std::unique_ptr<MCSubtargetInfo> STI; 31 std::unique_ptr<MCDisassembler> DisAsm; 32 33 Context() { 34 LLVMInitializeX86TargetInfo(); 35 LLVMInitializeX86TargetMC(); 36 LLVMInitializeX86Disassembler(); 37 38 // If we didn't build x86, do not run the test. 39 std::string Error; 40 const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); 41 if (!TheTarget) 42 return; 43 44 MRI.reset(TheTarget->createMCRegInfo(TripleName)); 45 MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions())); 46 STI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); 47 Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(), MRI.get(), 48 STI.get()); 49 50 DisAsm.reset(TheTarget->createMCDisassembler(*STI, *Ctx)); 51 } 52 53 operator MCContext &() { return *Ctx; }; 54 }; 55 56 Context &getContext() { 57 static Context Ctxt; 58 return Ctxt; 59 } 60 61 class X86MCSymbolizerTest : public MCSymbolizer { 62 public: 63 X86MCSymbolizerTest(MCContext &MC) : MCSymbolizer(MC, nullptr) {} 64 ~X86MCSymbolizerTest() {} 65 66 struct OpInfo { 67 int64_t Value = 0; 68 uint64_t Offset = 0; 69 uint64_t Size; 70 }; 71 std::vector<OpInfo> Operands; 72 uint64_t InstructionSize = 0; 73 74 void reset() { 75 Operands.clear(); 76 InstructionSize = 0; 77 } 78 79 bool tryAddingSymbolicOperand(MCInst &Inst, raw_ostream &CStream, 80 int64_t Value, uint64_t Address, bool IsBranch, 81 uint64_t Offset, uint64_t OpSize, 82 uint64_t InstSize) override { 83 Operands.push_back({Value, Offset, OpSize}); 84 InstructionSize = InstSize; 85 return false; 86 } 87 88 void tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value, 89 uint64_t Address) override {} 90 }; 91 92 } // namespace 93 94 TEST(X86Disassembler, X86MCSymbolizerTest) { 95 X86MCSymbolizerTest *TestSymbolizer = new X86MCSymbolizerTest(getContext()); 96 getContext().DisAsm->setSymbolizer( 97 std::unique_ptr<MCSymbolizer>(TestSymbolizer)); 98 99 MCDisassembler::DecodeStatus Status; 100 MCInst Inst; 101 uint64_t InstSize; 102 103 auto checkBytes = [&](ArrayRef<uint8_t> Bytes) { 104 TestSymbolizer->reset(); 105 Status = 106 getContext().DisAsm->getInstruction(Inst, InstSize, Bytes, 0, nulls()); 107 ASSERT_TRUE(Status == MCDisassembler::Success); 108 EXPECT_EQ(TestSymbolizer->InstructionSize, InstSize); 109 }; 110 111 auto checkOperand = [&](size_t OpNo, int64_t Value, uint64_t Offset, 112 uint64_t Size) { 113 ASSERT_TRUE(TestSymbolizer->Operands.size() > OpNo); 114 EXPECT_EQ(TestSymbolizer->Operands[OpNo].Value, Value); 115 EXPECT_EQ(TestSymbolizer->Operands[OpNo].Offset, Offset); 116 EXPECT_EQ(TestSymbolizer->Operands[OpNo].Size, Size); 117 }; 118 119 // movq $0x80000, 0x80000 120 checkBytes( 121 {0x48, 0xc7, 0x04, 0x25, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00}); 122 checkOperand(0, 0x80000, 4, 4); 123 checkOperand(1, 0x80000, 8, 4); 124 125 // movq $0x2a, 0x123(%rax,%r14,8) 126 checkBytes( 127 {0x4a, 0xc7, 0x84, 0xf0, 0x23, 0x01, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00}); 128 checkOperand(0, 291, 4, 4); 129 checkOperand(1, 42, 8, 4); 130 131 // movq $0xffffffffffffefe8, -0x1(%rip) 132 // Test that the value of the rip-relative operand is set correctly. 133 // The instruction address is 0 and the size is 12 bytes. 134 checkBytes( 135 {0x48, 0xc7, 0x05, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xef, 0xff, 0xff}); 136 checkOperand(0, /*next instr address*/ 11 - /*disp*/ 1, 3, 4); 137 checkOperand(1, 0xffffffffffffefe8, 7, 4); 138 139 // movq $0xfffffffffffffef5, (%r12) 140 // Test that the displacement operand has a size of 0, since it is not 141 // explicitly specified in the instruction. 142 checkBytes({0x49, 0xc7, 0x04, 0x24, 0xf5, 0xfe, 0xff, 0xff}); 143 checkOperand(0, 0, 4, 0); 144 checkOperand(1, 0xfffffffffffffef5, 4, 4); 145 } 146