//==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief This file is part of the WebAssembly Disassembler. /// /// It contains code to translate the data produced by the decoder into /// MCInsts. /// //===----------------------------------------------------------------------===// #include "WebAssembly.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Endian.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; #define DEBUG_TYPE "wasm-disassembler" namespace { class WebAssemblyDisassembler final : public MCDisassembler { std::unique_ptr<const MCInstrInfo> MCII; DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address, raw_ostream &VStream, raw_ostream &CStream) const override; public: WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, std::unique_ptr<const MCInstrInfo> MCII) : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {} }; } // end anonymous namespace static MCDisassembler *createWebAssemblyDisassembler(const Target &T, const MCSubtargetInfo &STI, MCContext &Ctx) { std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo()); return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII)); } extern "C" void LLVMInitializeWebAssemblyDisassembler() { // Register the disassembler for each target. TargetRegistry::RegisterMCDisassembler(TheWebAssemblyTarget32, createWebAssemblyDisassembler); TargetRegistry::RegisterMCDisassembler(TheWebAssemblyTarget64, createWebAssemblyDisassembler); } MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/, raw_ostream &OS, raw_ostream &CS) const { Size = 0; uint64_t Pos = 0; // Read the opcode. if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; uint64_t Opcode = support::endian::read64le(Bytes.data() + Pos); Pos += sizeof(uint64_t); if (Opcode >= WebAssembly::INSTRUCTION_LIST_END) return MCDisassembler::Fail; MI.setOpcode(Opcode); const MCInstrDesc &Desc = MCII->get(Opcode); unsigned NumFixedOperands = Desc.NumOperands; // If it's variadic, read the number of extra operands. unsigned NumExtraOperands = 0; if (Desc.isVariadic()) { if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; NumExtraOperands = support::endian::read64le(Bytes.data() + Pos); Pos += sizeof(uint64_t); } // Read the fixed operands. These are described by the MCInstrDesc. for (unsigned i = 0; i < NumFixedOperands; ++i) { const MCOperandInfo &Info = Desc.OpInfo[i]; switch (Info.OperandType) { case MCOI::OPERAND_IMMEDIATE: case WebAssembly::OPERAND_P2ALIGN: case WebAssembly::OPERAND_BASIC_BLOCK: { if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; uint64_t Imm = support::endian::read64le(Bytes.data() + Pos); Pos += sizeof(uint64_t); MI.addOperand(MCOperand::createImm(Imm)); break; } case MCOI::OPERAND_REGISTER: { if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; uint64_t Reg = support::endian::read64le(Bytes.data() + Pos); Pos += sizeof(uint64_t); MI.addOperand(MCOperand::createReg(Reg)); break; } case WebAssembly::OPERAND_FP32IMM: case WebAssembly::OPERAND_FP64IMM: { // TODO: MC converts all floating point immediate operands to double. // This is fine for numeric values, but may cause NaNs to change bits. if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; uint64_t Bits = support::endian::read64le(Bytes.data() + Pos); Pos += sizeof(uint64_t); double Imm; memcpy(&Imm, &Bits, sizeof(Imm)); MI.addOperand(MCOperand::createFPImm(Imm)); break; } default: llvm_unreachable("unimplemented operand kind"); } } // Read the extra operands. assert(NumExtraOperands == 0 || Desc.isVariadic()); for (unsigned i = 0; i < NumExtraOperands; ++i) { if (Pos + sizeof(uint64_t) > Bytes.size()) return MCDisassembler::Fail; if (Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate) { // Decode extra immediate operands. uint64_t Imm = support::endian::read64le(Bytes.data() + Pos); MI.addOperand(MCOperand::createImm(Imm)); } else { // Decode extra register operands. uint64_t Reg = support::endian::read64le(Bytes.data() + Pos); MI.addOperand(MCOperand::createReg(Reg)); } Pos += sizeof(uint64_t); } Size = Pos; return MCDisassembler::Success; }