322 lines · cpp
1//==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==//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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file is part of the WebAssembly Disassembler.11///12/// It contains code to translate the data produced by the decoder into13/// MCInsts.14///15//===----------------------------------------------------------------------===//16 17#include "MCTargetDesc/WebAssemblyMCAsmInfo.h"18#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"19#include "TargetInfo/WebAssemblyTargetInfo.h"20#include "llvm/BinaryFormat/Wasm.h"21#include "llvm/MC/MCContext.h"22#include "llvm/MC/MCDisassembler/MCDisassembler.h"23#include "llvm/MC/MCInst.h"24#include "llvm/MC/MCInstrInfo.h"25#include "llvm/MC/MCSubtargetInfo.h"26#include "llvm/MC/MCSymbol.h"27#include "llvm/MC/MCSymbolWasm.h"28#include "llvm/MC/TargetRegistry.h"29#include "llvm/Support/Compiler.h"30#include "llvm/Support/Endian.h"31#include "llvm/Support/LEB128.h"32 33using namespace llvm;34 35#define DEBUG_TYPE "wasm-disassembler"36 37using DecodeStatus = MCDisassembler::DecodeStatus;38 39#include "WebAssemblyGenDisassemblerTables.inc"40 41static constexpr int WebAssemblyInstructionTableSize = 256;42 43namespace {44class WebAssemblyDisassembler final : public MCDisassembler {45 std::unique_ptr<const MCInstrInfo> MCII;46 47 DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,48 ArrayRef<uint8_t> Bytes, uint64_t Address,49 raw_ostream &CStream) const override;50 51 Expected<bool> onSymbolStart(SymbolInfoTy &Symbol, uint64_t &Size,52 ArrayRef<uint8_t> Bytes,53 uint64_t Address) const override;54 55public:56 WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,57 std::unique_ptr<const MCInstrInfo> MCII)58 : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {}59};60} // end anonymous namespace61 62static MCDisassembler *createWebAssemblyDisassembler(const Target &T,63 const MCSubtargetInfo &STI,64 MCContext &Ctx) {65 std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo());66 return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII));67}68 69extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void70LLVMInitializeWebAssemblyDisassembler() {71 // Register the disassembler for each target.72 TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(),73 createWebAssemblyDisassembler);74 TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(),75 createWebAssemblyDisassembler);76}77 78static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) {79 if (Size >= Bytes.size())80 return -1;81 auto V = Bytes[Size];82 Size++;83 return V;84}85 86static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size,87 bool Signed) {88 unsigned N = 0;89 const char *Error = nullptr;90 Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,91 Bytes.data() + Bytes.size(), &Error)92 : static_cast<int64_t>(decodeULEB128(Bytes.data() + Size, &N,93 Bytes.data() + Bytes.size(),94 &Error));95 if (Error)96 return false;97 Size += N;98 return true;99}100 101static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,102 ArrayRef<uint8_t> Bytes, bool Signed) {103 int64_t Val;104 if (!nextLEB(Val, Bytes, Size, Signed))105 return false;106 MI.addOperand(MCOperand::createImm(Val));107 return true;108}109 110template <typename T>111bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {112 if (Size + sizeof(T) > Bytes.size())113 return false;114 T Val =115 support::endian::read<T, llvm::endianness::little>(Bytes.data() + Size);116 Size += sizeof(T);117 if (std::is_floating_point<T>::value) {118 MI.addOperand(119 MCOperand::createDFPImm(bit_cast<uint64_t>(static_cast<double>(Val))));120 } else {121 MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));122 }123 return true;124}125 126Expected<bool> WebAssemblyDisassembler::onSymbolStart(SymbolInfoTy &Symbol,127 uint64_t &Size,128 ArrayRef<uint8_t> Bytes,129 uint64_t Address) const {130 Size = 0;131 if (Symbol.Type == wasm::WASM_SYMBOL_TYPE_SECTION) {132 // Start of a code section: we're parsing only the function count.133 int64_t FunctionCount;134 if (!nextLEB(FunctionCount, Bytes, Size, false))135 return false;136 outs() << " # " << FunctionCount << " functions in section.";137 } else {138 // Parse the start of a single function.139 int64_t BodySize, LocalEntryCount;140 if (!nextLEB(BodySize, Bytes, Size, false) ||141 !nextLEB(LocalEntryCount, Bytes, Size, false))142 return false;143 if (LocalEntryCount) {144 outs() << " .local ";145 for (int64_t I = 0; I < LocalEntryCount; I++) {146 int64_t Count, Type;147 if (!nextLEB(Count, Bytes, Size, false) ||148 !nextLEB(Type, Bytes, Size, false))149 return false;150 for (int64_t J = 0; J < Count; J++) {151 if (I || J)152 outs() << ", ";153 outs() << WebAssembly::anyTypeToString(Type);154 }155 }156 }157 }158 outs() << "\n";159 return true;160}161 162MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(163 MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/,164 raw_ostream &CS) const {165 CommentStream = &CS;166 Size = 0;167 int Opc = nextByte(Bytes, Size);168 if (Opc < 0)169 return MCDisassembler::Fail;170 const auto *WasmInst = &InstructionTable0[Opc];171 // If this is a prefix byte, indirect to another table.172 if (WasmInst->ET == ET_Prefix) {173 WasmInst = nullptr;174 // Linear search, so far only 4 entries.175 for (const auto &[Prefix, Table] : PrefixTable) {176 if (Prefix == Opc) {177 WasmInst = Table;178 break;179 }180 }181 if (!WasmInst)182 return MCDisassembler::Fail;183 int64_t PrefixedOpc;184 if (!nextLEB(PrefixedOpc, Bytes, Size, false))185 return MCDisassembler::Fail;186 if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize)187 return MCDisassembler::Fail;188 WasmInst += PrefixedOpc;189 }190 if (WasmInst->ET == ET_Unused)191 return MCDisassembler::Fail;192 // At this point we must have a valid instruction to decode.193 assert(WasmInst->ET == ET_Instruction);194 MI.setOpcode(WasmInst->Opcode);195 // Parse any operands.196 for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) {197 auto OT = OperandTable[WasmInst->OperandStart + OPI];198 switch (OT) {199 // ULEB operands:200 case WebAssembly::OPERAND_BASIC_BLOCK:201 case WebAssembly::OPERAND_LOCAL:202 case WebAssembly::OPERAND_GLOBAL:203 case WebAssembly::OPERAND_FUNCTION32:204 case WebAssembly::OPERAND_TABLE:205 case WebAssembly::OPERAND_OFFSET32:206 case WebAssembly::OPERAND_OFFSET64:207 case WebAssembly::OPERAND_P2ALIGN:208 case WebAssembly::OPERAND_TYPEINDEX:209 case WebAssembly::OPERAND_TAG:210 case MCOI::OPERAND_IMMEDIATE: {211 if (!parseLEBImmediate(MI, Size, Bytes, false))212 return MCDisassembler::Fail;213 break;214 }215 // SLEB operands:216 case WebAssembly::OPERAND_I32IMM:217 case WebAssembly::OPERAND_I64IMM: {218 if (!parseLEBImmediate(MI, Size, Bytes, true))219 return MCDisassembler::Fail;220 break;221 }222 // block_type operands:223 case WebAssembly::OPERAND_SIGNATURE: {224 int64_t Val;225 uint64_t PrevSize = Size;226 if (!nextLEB(Val, Bytes, Size, true))227 return MCDisassembler::Fail;228 if (Val < 0) {229 // Negative values are single septet value types or empty types230 if (Size != PrevSize + 1) {231 MI.addOperand(232 MCOperand::createImm(int64_t(WebAssembly::BlockType::Invalid)));233 } else {234 MI.addOperand(MCOperand::createImm(Val & 0x7f));235 }236 } else {237 // We don't have access to the signature, so create a symbol without one238 MCSymbol *Sym = getContext().createTempSymbol("typeindex", true);239 auto *WasmSym = static_cast<MCSymbolWasm *>(Sym);240 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);241 const MCExpr *Expr = MCSymbolRefExpr::create(242 WasmSym, WebAssembly::S_TYPEINDEX, getContext());243 MI.addOperand(MCOperand::createExpr(Expr));244 }245 break;246 }247 // FP operands.248 case WebAssembly::OPERAND_F32IMM: {249 if (!parseImmediate<float>(MI, Size, Bytes))250 return MCDisassembler::Fail;251 break;252 }253 case WebAssembly::OPERAND_F64IMM: {254 if (!parseImmediate<double>(MI, Size, Bytes))255 return MCDisassembler::Fail;256 break;257 }258 // Vector lane operands (not LEB encoded).259 case WebAssembly::OPERAND_VEC_I8IMM: {260 if (!parseImmediate<uint8_t>(MI, Size, Bytes))261 return MCDisassembler::Fail;262 break;263 }264 case WebAssembly::OPERAND_VEC_I16IMM: {265 if (!parseImmediate<uint16_t>(MI, Size, Bytes))266 return MCDisassembler::Fail;267 break;268 }269 case WebAssembly::OPERAND_VEC_I32IMM: {270 if (!parseImmediate<uint32_t>(MI, Size, Bytes))271 return MCDisassembler::Fail;272 break;273 }274 case WebAssembly::OPERAND_VEC_I64IMM: {275 if (!parseImmediate<uint64_t>(MI, Size, Bytes))276 return MCDisassembler::Fail;277 break;278 }279 case WebAssembly::OPERAND_BRLIST: {280 int64_t TargetTableLen;281 if (!nextLEB(TargetTableLen, Bytes, Size, false))282 return MCDisassembler::Fail;283 for (int64_t I = 0; I < TargetTableLen; I++) {284 if (!parseLEBImmediate(MI, Size, Bytes, false))285 return MCDisassembler::Fail;286 }287 // Default case.288 if (!parseLEBImmediate(MI, Size, Bytes, false))289 return MCDisassembler::Fail;290 break;291 }292 case WebAssembly::OPERAND_CATCH_LIST: {293 if (!parseLEBImmediate(MI, Size, Bytes, false))294 return MCDisassembler::Fail;295 int64_t NumCatches = MI.getOperand(MI.getNumOperands() - 1).getImm();296 for (int64_t I = 0; I < NumCatches; I++) {297 if (!parseImmediate<uint8_t>(MI, Size, Bytes))298 return MCDisassembler::Fail;299 int64_t CatchOpcode = MI.getOperand(MI.getNumOperands() - 1).getImm();300 if (CatchOpcode == wasm::WASM_OPCODE_CATCH ||301 CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) {302 if (!parseLEBImmediate(MI, Size, Bytes, false)) // tag index303 return MCDisassembler::Fail;304 }305 if (!parseLEBImmediate(MI, Size, Bytes, false)) // destination306 return MCDisassembler::Fail;307 }308 break;309 }310 case MCOI::OPERAND_REGISTER:311 // The tablegen header currently does not have any register operands since312 // we use only the stack (_S) instructions.313 // If you hit this that probably means a bad instruction definition in314 // tablegen.315 llvm_unreachable("Register operand in WebAssemblyDisassembler");316 default:317 llvm_unreachable("Unknown operand type in WebAssemblyDisassembler");318 }319 }320 return MCDisassembler::Success;321}322