1 //===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the Wasm-specific dumper for llvm-readobj. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "ObjDumper.h" 14 #include "llvm-readobj.h" 15 #include "llvm/Object/Wasm.h" 16 #include "llvm/Support/ScopedPrinter.h" 17 18 using namespace llvm; 19 using namespace object; 20 21 namespace { 22 23 static const EnumEntry<unsigned> WasmSymbolTypes[] = { 24 #define ENUM_ENTRY(X) \ 25 { #X, wasm::WASM_SYMBOL_TYPE_##X } 26 ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), 27 ENUM_ENTRY(SECTION), ENUM_ENTRY(TAG), ENUM_ENTRY(TABLE), 28 #undef ENUM_ENTRY 29 }; 30 31 static const EnumEntry<uint32_t> WasmSectionTypes[] = { 32 #define ENUM_ENTRY(X) \ 33 { #X, wasm::WASM_SEC_##X } 34 ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), 35 ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), 36 ENUM_ENTRY(GLOBAL), ENUM_ENTRY(TAG), ENUM_ENTRY(EXPORT), 37 ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), 38 ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), 39 #undef ENUM_ENTRY 40 }; 41 42 static const EnumEntry<unsigned> WasmSymbolFlags[] = { 43 #define ENUM_ENTRY(X) \ 44 { #X, wasm::WASM_SYMBOL_##X } 45 ENUM_ENTRY(BINDING_GLOBAL), 46 ENUM_ENTRY(BINDING_WEAK), 47 ENUM_ENTRY(BINDING_LOCAL), 48 ENUM_ENTRY(VISIBILITY_DEFAULT), 49 ENUM_ENTRY(VISIBILITY_HIDDEN), 50 ENUM_ENTRY(UNDEFINED), 51 ENUM_ENTRY(EXPORTED), 52 ENUM_ENTRY(EXPLICIT_NAME), 53 ENUM_ENTRY(NO_STRIP), 54 #undef ENUM_ENTRY 55 }; 56 57 class WasmDumper : public ObjDumper { 58 public: 59 WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer) 60 : ObjDumper(Writer, Obj->getFileName()), Obj(Obj) {} 61 62 void printFileHeaders() override; 63 void printSectionHeaders() override; 64 void printRelocations() override; 65 void printUnwindInfo() override { llvm_unreachable("unimplemented"); } 66 void printStackMap() const override { llvm_unreachable("unimplemented"); } 67 68 protected: 69 void printSymbol(const SymbolRef &Sym); 70 void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); 71 72 private: 73 void printSymbols() override; 74 void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } 75 76 const WasmObjectFile *Obj; 77 }; 78 79 void WasmDumper::printFileHeaders() { 80 W.printHex("Version", Obj->getHeader().Version); 81 } 82 83 void WasmDumper::printRelocation(const SectionRef &Section, 84 const RelocationRef &Reloc) { 85 SmallString<64> RelocTypeName; 86 uint64_t RelocType = Reloc.getType(); 87 Reloc.getTypeName(RelocTypeName); 88 const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc); 89 90 StringRef SymName; 91 symbol_iterator SI = Reloc.getSymbol(); 92 if (SI != Obj->symbol_end()) 93 SymName = unwrapOrError(Obj->getFileName(), SI->getName()); 94 95 bool HasAddend = wasm::relocTypeHasAddend(static_cast<uint32_t>(RelocType)); 96 97 if (opts::ExpandRelocs) { 98 DictScope Group(W, "Relocation"); 99 W.printNumber("Type", RelocTypeName, RelocType); 100 W.printHex("Offset", Reloc.getOffset()); 101 if (!SymName.empty()) 102 W.printString("Symbol", SymName); 103 else 104 W.printHex("Index", WasmReloc.Index); 105 if (HasAddend) 106 W.printNumber("Addend", WasmReloc.Addend); 107 } else { 108 raw_ostream &OS = W.startLine(); 109 OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " "; 110 if (!SymName.empty()) 111 OS << SymName; 112 else 113 OS << WasmReloc.Index; 114 if (HasAddend) 115 OS << " " << WasmReloc.Addend; 116 OS << "\n"; 117 } 118 } 119 120 void WasmDumper::printRelocations() { 121 ListScope D(W, "Relocations"); 122 123 int SectionNumber = 0; 124 for (const SectionRef &Section : Obj->sections()) { 125 bool PrintedGroup = false; 126 StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); 127 128 ++SectionNumber; 129 130 for (const RelocationRef &Reloc : Section.relocations()) { 131 if (!PrintedGroup) { 132 W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; 133 W.indent(); 134 PrintedGroup = true; 135 } 136 137 printRelocation(Section, Reloc); 138 } 139 140 if (PrintedGroup) { 141 W.unindent(); 142 W.startLine() << "}\n"; 143 } 144 } 145 } 146 147 void WasmDumper::printSymbols() { 148 ListScope Group(W, "Symbols"); 149 150 for (const SymbolRef &Symbol : Obj->symbols()) 151 printSymbol(Symbol); 152 } 153 154 void WasmDumper::printSectionHeaders() { 155 ListScope Group(W, "Sections"); 156 for (const SectionRef &Section : Obj->sections()) { 157 const WasmSection &WasmSec = Obj->getWasmSection(Section); 158 DictScope SectionD(W, "Section"); 159 W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes)); 160 W.printNumber("Size", static_cast<uint64_t>(WasmSec.Content.size())); 161 W.printNumber("Offset", WasmSec.Offset); 162 switch (WasmSec.Type) { 163 case wasm::WASM_SEC_CUSTOM: 164 W.printString("Name", WasmSec.Name); 165 if (WasmSec.Name == "linking") { 166 const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); 167 if (!LinkingData.InitFunctions.empty()) { 168 ListScope Group(W, "InitFunctions"); 169 for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions) 170 W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n"; 171 } 172 } 173 break; 174 case wasm::WASM_SEC_DATA: { 175 ListScope Group(W, "Segments"); 176 for (const WasmSegment &Segment : Obj->dataSegments()) { 177 const wasm::WasmDataSegment &Seg = Segment.Data; 178 DictScope Group(W, "Segment"); 179 if (!Seg.Name.empty()) 180 W.printString("Name", Seg.Name); 181 W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size())); 182 if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) 183 W.printNumber("Offset", Seg.Offset.Value.Int32); 184 else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST) 185 W.printNumber("Offset", Seg.Offset.Value.Int64); 186 else 187 llvm_unreachable("unknown init expr opcode"); 188 } 189 break; 190 } 191 case wasm::WASM_SEC_MEMORY: 192 ListScope Group(W, "Memories"); 193 for (const wasm::WasmLimits &Memory : Obj->memories()) { 194 DictScope Group(W, "Memory"); 195 W.printNumber("MinPages", Memory.Minimum); 196 if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { 197 W.printNumber("MaxPages", WasmSec.Offset); 198 } 199 } 200 break; 201 } 202 203 if (opts::SectionRelocations) { 204 ListScope D(W, "Relocations"); 205 for (const RelocationRef &Reloc : Section.relocations()) 206 printRelocation(Section, Reloc); 207 } 208 209 if (opts::SectionData) { 210 W.printBinaryBlock("SectionData", WasmSec.Content); 211 } 212 } 213 } 214 215 void WasmDumper::printSymbol(const SymbolRef &Sym) { 216 DictScope D(W, "Symbol"); 217 WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); 218 W.printString("Name", Symbol.Info.Name); 219 W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes)); 220 W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags)); 221 222 if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { 223 if (Symbol.Info.ImportName) { 224 W.printString("ImportName", *Symbol.Info.ImportName); 225 } 226 if (Symbol.Info.ImportModule) { 227 W.printString("ImportModule", *Symbol.Info.ImportModule); 228 } 229 } 230 if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { 231 W.printHex("ElementIndex", Symbol.Info.ElementIndex); 232 } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { 233 W.printHex("Offset", Symbol.Info.DataRef.Offset); 234 W.printHex("Segment", Symbol.Info.DataRef.Segment); 235 W.printHex("Size", Symbol.Info.DataRef.Size); 236 } 237 } 238 239 } // namespace 240 241 namespace llvm { 242 243 std::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj, 244 ScopedPrinter &Writer) { 245 return std::make_unique<WasmDumper>(&Obj, Writer); 246 } 247 248 } // namespace llvm 249