//===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements the Wasm-specific dumper for llvm-readobj. // //===----------------------------------------------------------------------===// #include "Error.h" #include "ObjDumper.h" #include "llvm-readobj.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/ScopedPrinter.h" using namespace llvm; using namespace object; namespace { static const EnumEntry WasmSymbolTypes[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_TYPE_##X } ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), #undef ENUM_ENTRY }; static const EnumEntry WasmSectionTypes[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SEC_##X } ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), #undef ENUM_ENTRY }; static const EnumEntry WasmSymbolFlags[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_##X } ENUM_ENTRY(BINDING_GLOBAL), ENUM_ENTRY(BINDING_WEAK), ENUM_ENTRY(BINDING_LOCAL), ENUM_ENTRY(VISIBILITY_DEFAULT), ENUM_ENTRY(VISIBILITY_HIDDEN), ENUM_ENTRY(UNDEFINED), ENUM_ENTRY(EXPORTED), ENUM_ENTRY(EXPLICIT_NAME), ENUM_ENTRY(NO_STRIP), #undef ENUM_ENTRY }; class WasmDumper : public ObjDumper { public: WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer) : ObjDumper(Writer), Obj(Obj) {} void printFileHeaders() override; void printSectionHeaders() override; void printRelocations() override; void printUnwindInfo() override { llvm_unreachable("unimplemented"); } void printStackMap() const override { llvm_unreachable("unimplemented"); } protected: void printSymbol(const SymbolRef &Sym); void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); private: void printSymbols() override; void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } const WasmObjectFile *Obj; }; void WasmDumper::printFileHeaders() { W.printHex("Version", Obj->getHeader().Version); } void WasmDumper::printRelocation(const SectionRef &Section, const RelocationRef &Reloc) { SmallString<64> RelocTypeName; uint64_t RelocType = Reloc.getType(); Reloc.getTypeName(RelocTypeName); const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc); StringRef SymName; symbol_iterator SI = Reloc.getSymbol(); if (SI != Obj->symbol_end()) SymName = unwrapOrError(Obj->getFileName(), SI->getName()); bool HasAddend = wasm::relocTypeHasAddend(static_cast(RelocType)); if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printNumber("Type", RelocTypeName, RelocType); W.printHex("Offset", Reloc.getOffset()); if (!SymName.empty()) W.printString("Symbol", SymName); else W.printHex("Index", WasmReloc.Index); if (HasAddend) W.printNumber("Addend", WasmReloc.Addend); } else { raw_ostream &OS = W.startLine(); OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " "; if (!SymName.empty()) OS << SymName; else OS << WasmReloc.Index; if (HasAddend) OS << " " << WasmReloc.Addend; OS << "\n"; } } void WasmDumper::printRelocations() { ListScope D(W, "Relocations"); int SectionNumber = 0; for (const SectionRef &Section : Obj->sections()) { bool PrintedGroup = false; StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); ++SectionNumber; for (const RelocationRef &Reloc : Section.relocations()) { if (!PrintedGroup) { W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; W.indent(); PrintedGroup = true; } printRelocation(Section, Reloc); } if (PrintedGroup) { W.unindent(); W.startLine() << "}\n"; } } } void WasmDumper::printSymbols() { ListScope Group(W, "Symbols"); for (const SymbolRef &Symbol : Obj->symbols()) printSymbol(Symbol); } void WasmDumper::printSectionHeaders() { ListScope Group(W, "Sections"); for (const SectionRef &Section : Obj->sections()) { const WasmSection &WasmSec = Obj->getWasmSection(Section); DictScope SectionD(W, "Section"); W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes)); W.printNumber("Size", static_cast(WasmSec.Content.size())); W.printNumber("Offset", WasmSec.Offset); switch (WasmSec.Type) { case wasm::WASM_SEC_CUSTOM: W.printString("Name", WasmSec.Name); if (WasmSec.Name == "linking") { const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); if (!LinkingData.InitFunctions.empty()) { ListScope Group(W, "InitFunctions"); for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions) W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n"; } } break; case wasm::WASM_SEC_DATA: { ListScope Group(W, "Segments"); for (const WasmSegment &Segment : Obj->dataSegments()) { const wasm::WasmDataSegment &Seg = Segment.Data; DictScope Group(W, "Segment"); if (!Seg.Name.empty()) W.printString("Name", Seg.Name); W.printNumber("Size", static_cast(Seg.Content.size())); if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) W.printNumber("Offset", Seg.Offset.Value.Int32); else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST) W.printNumber("Offset", Seg.Offset.Value.Int64); else llvm_unreachable("unknown init expr opcode"); } break; } case wasm::WASM_SEC_MEMORY: ListScope Group(W, "Memories"); for (const wasm::WasmLimits &Memory : Obj->memories()) { DictScope Group(W, "Memory"); W.printNumber("InitialPages", Memory.Initial); if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { W.printNumber("MaxPages", WasmSec.Offset); } } break; } if (opts::SectionRelocations) { ListScope D(W, "Relocations"); for (const RelocationRef &Reloc : Section.relocations()) printRelocation(Section, Reloc); } if (opts::SectionData) { W.printBinaryBlock("SectionData", WasmSec.Content); } } } void WasmDumper::printSymbol(const SymbolRef &Sym) { DictScope D(W, "Symbol"); WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); W.printString("Name", Symbol.Info.Name); W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes)); W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags)); if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { if (Symbol.Info.ImportName) { W.printString("ImportName", *Symbol.Info.ImportName); } if (Symbol.Info.ImportModule) { W.printString("ImportModule", *Symbol.Info.ImportModule); } } if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { W.printHex("ElementIndex", Symbol.Info.ElementIndex); } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { W.printHex("Offset", Symbol.Info.DataRef.Offset); W.printHex("Segment", Symbol.Info.DataRef.Segment); W.printHex("Size", Symbol.Info.DataRef.Size); } } } // namespace namespace llvm { std::error_code createWasmDumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr &Result) { const auto *WasmObj = dyn_cast(Obj); assert(WasmObj && "createWasmDumper called with non-wasm object"); Result.reset(new WasmDumper(WasmObj, Writer)); return readobj_error::success; } } // namespace llvm