//==- 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 /// This file is part of the WebAssembly Disassembler. /// /// It contains code to translate the data produced by the decoder into /// MCInsts. /// //===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCFixedLenDisassembler.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/LEB128.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; #define DEBUG_TYPE "wasm-disassembler" using DecodeStatus = MCDisassembler::DecodeStatus; #include "WebAssemblyGenDisassemblerTables.inc" namespace { class WebAssemblyDisassembler final : public MCDisassembler { std::unique_ptr MCII; DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &VStream, raw_ostream &CStream) const override; public: WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, std::unique_ptr 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 MCII(T.createMCInstrInfo()); return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII)); } extern "C" void LLVMInitializeWebAssemblyDisassembler() { // Register the disassembler for each target. TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(), createWebAssemblyDisassembler); TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(), createWebAssemblyDisassembler); } static int nextByte(ArrayRef Bytes, uint64_t &Size) { if (Size >= Bytes.size()) return -1; auto V = Bytes[Size]; Size++; return V; } static bool parseLEBImmediate(MCInst &MI, uint64_t &Size, ArrayRef Bytes, bool Signed) { unsigned N = 0; const char *Error = nullptr; auto Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N, Bytes.data() + Bytes.size(), &Error) : static_cast( decodeULEB128(Bytes.data() + Size, &N, Bytes.data() + Bytes.size(), &Error)); if (Error) return false; Size += N; MI.addOperand(MCOperand::createImm(Val)); return true; } template bool parseFPImmediate(MCInst &MI, uint64_t &Size, ArrayRef Bytes) { if (Size + sizeof(T) > Bytes.size()) return false; T Val; memcpy(&Val, Bytes.data() + Size, sizeof(T)); support::endian::byte_swap(Val); Size += sizeof(T); MI.addOperand(MCOperand::createFPImm(static_cast(Val))); return true; } MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( MCInst &MI, uint64_t &Size, ArrayRef Bytes, uint64_t /*Address*/, raw_ostream & /*OS*/, raw_ostream &CS) const { CommentStream = &CS; Size = 0; auto Opc = nextByte(Bytes, Size); if (Opc < 0) return MCDisassembler::Fail; const auto *WasmInst = &InstructionTable0[Opc]; // If this is a prefix byte, indirect to another table. if (WasmInst->ET == ET_Prefix) { WasmInst = nullptr; // Linear search, so far only 2 entries. for (auto PT = PrefixTable; PT->Table; PT++) { if (PT->Prefix == Opc) { WasmInst = PT->Table; break; } } if (!WasmInst) return MCDisassembler::Fail; Opc = nextByte(Bytes, Size); if (Opc < 0) return MCDisassembler::Fail; WasmInst += Opc; } if (WasmInst->ET == ET_Unused) return MCDisassembler::Fail; // At this point we must have a valid instruction to decode. assert(WasmInst->ET == ET_Instruction); MI.setOpcode(WasmInst->Opcode); // Parse any operands. for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) { switch (WasmInst->Operands[OPI]) { // ULEB operands: case WebAssembly::OPERAND_BASIC_BLOCK: case WebAssembly::OPERAND_LOCAL: case WebAssembly::OPERAND_GLOBAL: case WebAssembly::OPERAND_FUNCTION32: case WebAssembly::OPERAND_OFFSET32: case WebAssembly::OPERAND_P2ALIGN: case WebAssembly::OPERAND_TYPEINDEX: case MCOI::OPERAND_IMMEDIATE: { if (!parseLEBImmediate(MI, Size, Bytes, false)) return MCDisassembler::Fail; break; } // SLEB operands: case WebAssembly::OPERAND_I32IMM: case WebAssembly::OPERAND_I64IMM: case WebAssembly::OPERAND_SIGNATURE: { if (!parseLEBImmediate(MI, Size, Bytes, true)) return MCDisassembler::Fail; break; } // FP operands. case WebAssembly::OPERAND_F32IMM: { if (!parseFPImmediate(MI, Size, Bytes)) return MCDisassembler::Fail; break; } case WebAssembly::OPERAND_F64IMM: { if (!parseFPImmediate(MI, Size, Bytes)) return MCDisassembler::Fail; break; } case MCOI::OPERAND_REGISTER: { // These are NOT actually in the instruction stream, but MC is going to // expect operands to be present for them! // FIXME: can MC re-generate register assignments or do we have to // do this? Since this function decodes a single instruction, we don't // have the proper context for tracking an operand stack here. MI.addOperand(MCOperand::createReg(0)); break; } default: llvm_unreachable("Unknown operand type in WebAssemblyDisassembler"); } } return MCDisassembler::Success; }