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 "Error.h" 14 #include "ObjDumper.h" 15 #include "llvm-readobj.h" 16 #include "llvm/Object/Wasm.h" 17 #include "llvm/Support/ScopedPrinter.h" 18 19 using namespace llvm; 20 using namespace object; 21 22 namespace { 23 24 static const EnumEntry<unsigned> WasmSymbolTypes[] = { 25 #define ENUM_ENTRY(X) \ 26 { #X, wasm::WASM_SYMBOL_TYPE_##X } 27 ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), 28 ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), 29 #undef ENUM_ENTRY 30 }; 31 32 static const EnumEntry<uint32_t> WasmSectionTypes[] = { 33 #define ENUM_ENTRY(X) \ 34 { #X, wasm::WASM_SEC_##X } 35 ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), 36 ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), 37 ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), 38 ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), 39 ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), 40 #undef ENUM_ENTRY 41 }; 42 43 static const EnumEntry<unsigned> WasmSymbolFlags[] = { 44 #define ENUM_ENTRY(X) \ 45 { #X, wasm::WASM_SYMBOL_##X } 46 ENUM_ENTRY(BINDING_GLOBAL), 47 ENUM_ENTRY(BINDING_WEAK), 48 ENUM_ENTRY(BINDING_LOCAL), 49 ENUM_ENTRY(VISIBILITY_DEFAULT), 50 ENUM_ENTRY(VISIBILITY_HIDDEN), 51 ENUM_ENTRY(UNDEFINED), 52 ENUM_ENTRY(EXPORTED), 53 ENUM_ENTRY(EXPLICIT_NAME), 54 #undef ENUM_ENTRY 55 }; 56 57 class WasmDumper : public ObjDumper { 58 public: 59 WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer) 60 : ObjDumper(Writer), 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 = error(SI->getName()); 94 95 bool HasAddend = false; 96 switch (RelocType) { 97 case wasm::R_WASM_MEMORY_ADDR_LEB: 98 case wasm::R_WASM_MEMORY_ADDR_SLEB: 99 case wasm::R_WASM_MEMORY_ADDR_I32: 100 case wasm::R_WASM_FUNCTION_OFFSET_I32: 101 case wasm::R_WASM_SECTION_OFFSET_I32: 102 HasAddend = true; 103 break; 104 default: 105 break; 106 } 107 if (opts::ExpandRelocs) { 108 DictScope Group(W, "Relocation"); 109 W.printNumber("Type", RelocTypeName, RelocType); 110 W.printHex("Offset", Reloc.getOffset()); 111 if (!SymName.empty()) 112 W.printString("Symbol", SymName); 113 else 114 W.printHex("Index", WasmReloc.Index); 115 if (HasAddend) 116 W.printNumber("Addend", WasmReloc.Addend); 117 } else { 118 raw_ostream &OS = W.startLine(); 119 OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " "; 120 if (!SymName.empty()) 121 OS << SymName; 122 else 123 OS << WasmReloc.Index; 124 if (HasAddend) 125 OS << " " << WasmReloc.Addend; 126 OS << "\n"; 127 } 128 } 129 130 void WasmDumper::printRelocations() { 131 ListScope D(W, "Relocations"); 132 133 int SectionNumber = 0; 134 for (const SectionRef &Section : Obj->sections()) { 135 bool PrintedGroup = false; 136 StringRef Name; 137 error(Section.getName(Name)); 138 ++SectionNumber; 139 140 for (const RelocationRef &Reloc : Section.relocations()) { 141 if (!PrintedGroup) { 142 W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; 143 W.indent(); 144 PrintedGroup = true; 145 } 146 147 printRelocation(Section, Reloc); 148 } 149 150 if (PrintedGroup) { 151 W.unindent(); 152 W.startLine() << "}\n"; 153 } 154 } 155 } 156 157 void WasmDumper::printSymbols() { 158 ListScope Group(W, "Symbols"); 159 160 for (const SymbolRef &Symbol : Obj->symbols()) 161 printSymbol(Symbol); 162 } 163 164 void WasmDumper::printSectionHeaders() { 165 ListScope Group(W, "Sections"); 166 for (const SectionRef &Section : Obj->sections()) { 167 const WasmSection &WasmSec = Obj->getWasmSection(Section); 168 DictScope SectionD(W, "Section"); 169 W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes)); 170 W.printNumber("Size", static_cast<uint64_t>(WasmSec.Content.size())); 171 W.printNumber("Offset", WasmSec.Offset); 172 switch (WasmSec.Type) { 173 case wasm::WASM_SEC_CUSTOM: 174 W.printString("Name", WasmSec.Name); 175 if (WasmSec.Name == "linking") { 176 const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); 177 if (!LinkingData.InitFunctions.empty()) { 178 ListScope Group(W, "InitFunctions"); 179 for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions) 180 W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n"; 181 } 182 } 183 break; 184 case wasm::WASM_SEC_DATA: { 185 ListScope Group(W, "Segments"); 186 for (const WasmSegment &Segment : Obj->dataSegments()) { 187 const wasm::WasmDataSegment &Seg = Segment.Data; 188 DictScope Group(W, "Segment"); 189 if (!Seg.Name.empty()) 190 W.printString("Name", Seg.Name); 191 W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size())); 192 if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) 193 W.printNumber("Offset", Seg.Offset.Value.Int32); 194 } 195 break; 196 } 197 case wasm::WASM_SEC_MEMORY: 198 ListScope Group(W, "Memories"); 199 for (const wasm::WasmLimits &Memory : Obj->memories()) { 200 DictScope Group(W, "Memory"); 201 W.printNumber("InitialPages", Memory.Initial); 202 if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { 203 W.printNumber("MaxPages", WasmSec.Offset); 204 } 205 } 206 break; 207 } 208 209 if (opts::SectionRelocations) { 210 ListScope D(W, "Relocations"); 211 for (const RelocationRef &Reloc : Section.relocations()) 212 printRelocation(Section, Reloc); 213 } 214 215 if (opts::SectionData) { 216 W.printBinaryBlock("SectionData", WasmSec.Content); 217 } 218 } 219 } 220 221 void WasmDumper::printSymbol(const SymbolRef &Sym) { 222 DictScope D(W, "Symbol"); 223 WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); 224 W.printString("Name", Symbol.Info.Name); 225 W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes)); 226 W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags)); 227 228 if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { 229 W.printString("ImportName", Symbol.Info.ImportName); 230 W.printString("ImportModule", Symbol.Info.ImportModule); 231 } 232 if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { 233 W.printHex("ElementIndex", Symbol.Info.ElementIndex); 234 } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { 235 W.printHex("Offset", Symbol.Info.DataRef.Offset); 236 W.printHex("Segment", Symbol.Info.DataRef.Segment); 237 W.printHex("Size", Symbol.Info.DataRef.Size); 238 } 239 } 240 241 } // namespace 242 243 namespace llvm { 244 245 std::error_code createWasmDumper(const object::ObjectFile *Obj, 246 ScopedPrinter &Writer, 247 std::unique_ptr<ObjDumper> &Result) { 248 const auto *WasmObj = dyn_cast<WasmObjectFile>(Obj); 249 assert(WasmObj && "createWasmDumper called with non-wasm object"); 250 251 Result.reset(new WasmDumper(WasmObj, Writer)); 252 return readobj_error::success; 253 } 254 255 } // namespace llvm 256