| //==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// This file is part of the WebAssembly Disassembler. | 
 | /// | 
 | /// It contains code to translate the data produced by the decoder into | 
 | /// MCInsts. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "MCTargetDesc/WebAssemblyMCExpr.h" | 
 | #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" | 
 | #include "TargetInfo/WebAssemblyTargetInfo.h" | 
 | #include "llvm/BinaryFormat/Wasm.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/MC/MCSymbolWasm.h" | 
 | #include "llvm/MC/TargetRegistry.h" | 
 | #include "llvm/Support/Casting.h" | 
 | #include "llvm/Support/Endian.h" | 
 | #include "llvm/Support/LEB128.h" | 
 |  | 
 | using namespace llvm; | 
 |  | 
 | #define DEBUG_TYPE "wasm-disassembler" | 
 |  | 
 | using DecodeStatus = MCDisassembler::DecodeStatus; | 
 |  | 
 | #include "WebAssemblyGenDisassemblerTables.inc" | 
 |  | 
 | static constexpr int WebAssemblyInstructionTableSize = 256; | 
 |  | 
 | 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 &CStream) const override; | 
 |  | 
 |   Expected<bool> onSymbolStart(SymbolInfoTy &Symbol, uint64_t &Size, | 
 |                                ArrayRef<uint8_t> Bytes, | 
 |                                uint64_t Address) 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" LLVM_EXTERNAL_VISIBILITY void | 
 | LLVMInitializeWebAssemblyDisassembler() { | 
 |   // Register the disassembler for each target. | 
 |   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(), | 
 |                                          createWebAssemblyDisassembler); | 
 |   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(), | 
 |                                          createWebAssemblyDisassembler); | 
 | } | 
 |  | 
 | static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) { | 
 |   if (Size >= Bytes.size()) | 
 |     return -1; | 
 |   auto V = Bytes[Size]; | 
 |   Size++; | 
 |   return V; | 
 | } | 
 |  | 
 | static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size, | 
 |                     bool Signed) { | 
 |   unsigned N = 0; | 
 |   const char *Error = nullptr; | 
 |   Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N, | 
 |                                Bytes.data() + Bytes.size(), &Error) | 
 |                : static_cast<int64_t>(decodeULEB128(Bytes.data() + Size, &N, | 
 |                                                     Bytes.data() + Bytes.size(), | 
 |                                                     &Error)); | 
 |   if (Error) | 
 |     return false; | 
 |   Size += N; | 
 |   return true; | 
 | } | 
 |  | 
 | static bool parseLEBImmediate(MCInst &MI, uint64_t &Size, | 
 |                               ArrayRef<uint8_t> Bytes, bool Signed) { | 
 |   int64_t Val; | 
 |   if (!nextLEB(Val, Bytes, Size, Signed)) | 
 |     return false; | 
 |   MI.addOperand(MCOperand::createImm(Val)); | 
 |   return true; | 
 | } | 
 |  | 
 | template <typename T> | 
 | bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) { | 
 |   if (Size + sizeof(T) > Bytes.size()) | 
 |     return false; | 
 |   T Val = | 
 |       support::endian::read<T, llvm::endianness::little>(Bytes.data() + Size); | 
 |   Size += sizeof(T); | 
 |   if (std::is_floating_point<T>::value) { | 
 |     MI.addOperand( | 
 |         MCOperand::createDFPImm(bit_cast<uint64_t>(static_cast<double>(Val)))); | 
 |   } else { | 
 |     MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val))); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | Expected<bool> WebAssemblyDisassembler::onSymbolStart(SymbolInfoTy &Symbol, | 
 |                                                       uint64_t &Size, | 
 |                                                       ArrayRef<uint8_t> Bytes, | 
 |                                                       uint64_t Address) const { | 
 |   Size = 0; | 
 |   if (Symbol.Type == wasm::WASM_SYMBOL_TYPE_SECTION) { | 
 |     // Start of a code section: we're parsing only the function count. | 
 |     int64_t FunctionCount; | 
 |     if (!nextLEB(FunctionCount, Bytes, Size, false)) | 
 |       return false; | 
 |     outs() << "        # " << FunctionCount << " functions in section."; | 
 |   } else { | 
 |     // Parse the start of a single function. | 
 |     int64_t BodySize, LocalEntryCount; | 
 |     if (!nextLEB(BodySize, Bytes, Size, false) || | 
 |         !nextLEB(LocalEntryCount, Bytes, Size, false)) | 
 |       return false; | 
 |     if (LocalEntryCount) { | 
 |       outs() << "        .local "; | 
 |       for (int64_t I = 0; I < LocalEntryCount; I++) { | 
 |         int64_t Count, Type; | 
 |         if (!nextLEB(Count, Bytes, Size, false) || | 
 |             !nextLEB(Type, Bytes, Size, false)) | 
 |           return false; | 
 |         for (int64_t J = 0; J < Count; J++) { | 
 |           if (I || J) | 
 |             outs() << ", "; | 
 |           outs() << WebAssembly::anyTypeToString(Type); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   outs() << "\n"; | 
 |   return true; | 
 | } | 
 |  | 
 | MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( | 
 |     MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/, | 
 |     raw_ostream &CS) const { | 
 |   CommentStream = &CS; | 
 |   Size = 0; | 
 |   int 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 4 entries. | 
 |     for (const auto &[Prefix, Table] : PrefixTable) { | 
 |       if (Prefix == Opc) { | 
 |         WasmInst = Table; | 
 |         break; | 
 |       } | 
 |     } | 
 |     if (!WasmInst) | 
 |       return MCDisassembler::Fail; | 
 |     int64_t PrefixedOpc; | 
 |     if (!nextLEB(PrefixedOpc, Bytes, Size, false)) | 
 |       return MCDisassembler::Fail; | 
 |     if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize) | 
 |       return MCDisassembler::Fail; | 
 |     WasmInst += PrefixedOpc; | 
 |   } | 
 |   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++) { | 
 |     auto OT = OperandTable[WasmInst->OperandStart + OPI]; | 
 |     switch (OT) { | 
 |     // ULEB operands: | 
 |     case WebAssembly::OPERAND_BASIC_BLOCK: | 
 |     case WebAssembly::OPERAND_LOCAL: | 
 |     case WebAssembly::OPERAND_GLOBAL: | 
 |     case WebAssembly::OPERAND_FUNCTION32: | 
 |     case WebAssembly::OPERAND_TABLE: | 
 |     case WebAssembly::OPERAND_OFFSET32: | 
 |     case WebAssembly::OPERAND_OFFSET64: | 
 |     case WebAssembly::OPERAND_P2ALIGN: | 
 |     case WebAssembly::OPERAND_TYPEINDEX: | 
 |     case WebAssembly::OPERAND_TAG: | 
 |     case MCOI::OPERAND_IMMEDIATE: { | 
 |       if (!parseLEBImmediate(MI, Size, Bytes, false)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     // SLEB operands: | 
 |     case WebAssembly::OPERAND_I32IMM: | 
 |     case WebAssembly::OPERAND_I64IMM: { | 
 |       if (!parseLEBImmediate(MI, Size, Bytes, true)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     // block_type operands: | 
 |     case WebAssembly::OPERAND_SIGNATURE: { | 
 |       int64_t Val; | 
 |       uint64_t PrevSize = Size; | 
 |       if (!nextLEB(Val, Bytes, Size, true)) | 
 |         return MCDisassembler::Fail; | 
 |       if (Val < 0) { | 
 |         // Negative values are single septet value types or empty types | 
 |         if (Size != PrevSize + 1) { | 
 |           MI.addOperand( | 
 |               MCOperand::createImm(int64_t(WebAssembly::BlockType::Invalid))); | 
 |         } else { | 
 |           MI.addOperand(MCOperand::createImm(Val & 0x7f)); | 
 |         } | 
 |       } else { | 
 |         // We don't have access to the signature, so create a symbol without one | 
 |         MCSymbol *Sym = getContext().createTempSymbol("typeindex", true); | 
 |         auto *WasmSym = cast<MCSymbolWasm>(Sym); | 
 |         WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); | 
 |         const MCExpr *Expr = MCSymbolRefExpr::create( | 
 |             WasmSym, WebAssembly::S_TYPEINDEX, getContext()); | 
 |         MI.addOperand(MCOperand::createExpr(Expr)); | 
 |       } | 
 |       break; | 
 |     } | 
 |     // FP operands. | 
 |     case WebAssembly::OPERAND_F32IMM: { | 
 |       if (!parseImmediate<float>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_F64IMM: { | 
 |       if (!parseImmediate<double>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     // Vector lane operands (not LEB encoded). | 
 |     case WebAssembly::OPERAND_VEC_I8IMM: { | 
 |       if (!parseImmediate<uint8_t>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_VEC_I16IMM: { | 
 |       if (!parseImmediate<uint16_t>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_VEC_I32IMM: { | 
 |       if (!parseImmediate<uint32_t>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_VEC_I64IMM: { | 
 |       if (!parseImmediate<uint64_t>(MI, Size, Bytes)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_BRLIST: { | 
 |       int64_t TargetTableLen; | 
 |       if (!nextLEB(TargetTableLen, Bytes, Size, false)) | 
 |         return MCDisassembler::Fail; | 
 |       for (int64_t I = 0; I < TargetTableLen; I++) { | 
 |         if (!parseLEBImmediate(MI, Size, Bytes, false)) | 
 |           return MCDisassembler::Fail; | 
 |       } | 
 |       // Default case. | 
 |       if (!parseLEBImmediate(MI, Size, Bytes, false)) | 
 |         return MCDisassembler::Fail; | 
 |       break; | 
 |     } | 
 |     case WebAssembly::OPERAND_CATCH_LIST: { | 
 |       if (!parseLEBImmediate(MI, Size, Bytes, false)) | 
 |         return MCDisassembler::Fail; | 
 |       int64_t NumCatches = MI.getOperand(MI.getNumOperands() - 1).getImm(); | 
 |       for (int64_t I = 0; I < NumCatches; I++) { | 
 |         if (!parseImmediate<uint8_t>(MI, Size, Bytes)) | 
 |           return MCDisassembler::Fail; | 
 |         int64_t CatchOpcode = MI.getOperand(MI.getNumOperands() - 1).getImm(); | 
 |         if (CatchOpcode == wasm::WASM_OPCODE_CATCH || | 
 |             CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) { | 
 |           if (!parseLEBImmediate(MI, Size, Bytes, false)) // tag index | 
 |             return MCDisassembler::Fail; | 
 |         } | 
 |         if (!parseLEBImmediate(MI, Size, Bytes, false)) // destination | 
 |           return MCDisassembler::Fail; | 
 |       } | 
 |       break; | 
 |     } | 
 |     case MCOI::OPERAND_REGISTER: | 
 |       // The tablegen header currently does not have any register operands since | 
 |       // we use only the stack (_S) instructions. | 
 |       // If you hit this that probably means a bad instruction definition in | 
 |       // tablegen. | 
 |       llvm_unreachable("Register operand in WebAssemblyDisassembler"); | 
 |     default: | 
 |       llvm_unreachable("Unknown operand type in WebAssemblyDisassembler"); | 
 |     } | 
 |   } | 
 |   return MCDisassembler::Success; | 
 | } |