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 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 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 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, ArrayRef(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.Extended) 183 llvm_unreachable("extended const exprs not supported"); 184 else if (Seg.Offset.Inst.Opcode == wasm::WASM_OPCODE_I32_CONST) 185 W.printNumber("Offset", Seg.Offset.Inst.Value.Int32); 186 else if (Seg.Offset.Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) 187 W.printNumber("Offset", Seg.Offset.Inst.Value.Int64); 188 else if (Seg.Offset.Inst.Opcode == wasm::WASM_OPCODE_GLOBAL_GET) { 189 ListScope Group(W, "Offset"); 190 W.printNumber("Global", Seg.Offset.Inst.Value.Global); 191 } else 192 llvm_unreachable("unknown init expr opcode"); 193 } 194 break; 195 } 196 case wasm::WASM_SEC_MEMORY: 197 ListScope Group(W, "Memories"); 198 for (const wasm::WasmLimits &Memory : Obj->memories()) { 199 DictScope Group(W, "Memory"); 200 W.printNumber("MinPages", Memory.Minimum); 201 if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { 202 W.printNumber("MaxPages", WasmSec.Offset); 203 } 204 } 205 break; 206 } 207 208 if (opts::SectionRelocations) { 209 ListScope D(W, "Relocations"); 210 for (const RelocationRef &Reloc : Section.relocations()) 211 printRelocation(Section, Reloc); 212 } 213 214 if (opts::SectionData) { 215 W.printBinaryBlock("SectionData", WasmSec.Content); 216 } 217 } 218 } 219 220 void WasmDumper::printSymbol(const SymbolRef &Sym) { 221 DictScope D(W, "Symbol"); 222 WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); 223 W.printString("Name", Symbol.Info.Name); 224 W.printEnum("Type", Symbol.Info.Kind, ArrayRef(WasmSymbolTypes)); 225 W.printFlags("Flags", Symbol.Info.Flags, ArrayRef(WasmSymbolFlags)); 226 227 if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { 228 if (Symbol.Info.ImportName) { 229 W.printString("ImportName", *Symbol.Info.ImportName); 230 } 231 if (Symbol.Info.ImportModule) { 232 W.printString("ImportModule", *Symbol.Info.ImportModule); 233 } 234 } 235 if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { 236 W.printHex("ElementIndex", Symbol.Info.ElementIndex); 237 } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { 238 W.printHex("Offset", Symbol.Info.DataRef.Offset); 239 W.printHex("Segment", Symbol.Info.DataRef.Segment); 240 W.printHex("Size", Symbol.Info.DataRef.Size); 241 } 242 } 243 244 } // namespace 245 246 namespace llvm { 247 248 std::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj, 249 ScopedPrinter &Writer) { 250 return std::make_unique<WasmDumper>(&Obj, Writer); 251 } 252 253 } // namespace llvm 254