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