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(bool ) override; |
74 | void printDynamicSymbols() override { llvm_unreachable("unimplemented" ); } |
75 | |
76 | const WasmObjectFile *Obj; |
77 | }; |
78 | |
79 | void WasmDumper::() { |
80 | W.printHex(Label: "Version" , Value: 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(Result&: RelocTypeName); |
88 | const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Ref: Reloc); |
89 | |
90 | StringRef SymName; |
91 | symbol_iterator SI = Reloc.getSymbol(); |
92 | if (SI != Obj->symbol_end()) |
93 | SymName = unwrapOrError(Input: Obj->getFileName(), EO: SI->getName()); |
94 | |
95 | bool HasAddend = wasm::relocTypeHasAddend(type: static_cast<uint32_t>(RelocType)); |
96 | |
97 | if (opts::ExpandRelocs) { |
98 | DictScope Group(W, "Relocation" ); |
99 | W.printNumber(Label: "Type" , Str: RelocTypeName, Value: RelocType); |
100 | W.printHex(Label: "Offset" , Value: Reloc.getOffset()); |
101 | if (!SymName.empty()) |
102 | W.printString(Label: "Symbol" , Value: SymName); |
103 | else |
104 | W.printHex(Label: "Index" , Value: WasmReloc.Index); |
105 | if (HasAddend) |
106 | W.printNumber(Label: "Addend" , Value: WasmReloc.Addend); |
107 | } else { |
108 | raw_ostream &OS = W.startLine(); |
109 | OS << W.hex(Value: 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(Input: Obj->getFileName(), EO: 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(bool /*ExtraSymInfo*/) { |
148 | ListScope Group(W, "Symbols" ); |
149 | |
150 | for (const SymbolRef &Symbol : Obj->symbols()) |
151 | printSymbol(Sym: Symbol); |
152 | } |
153 | |
154 | void WasmDumper::() { |
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(Label: "Type" , Value: WasmSec.Type, EnumValues: ArrayRef(WasmSectionTypes)); |
160 | W.printNumber(Label: "Size" , Value: static_cast<uint64_t>(WasmSec.Content.size())); |
161 | W.printNumber(Label: "Offset" , Value: WasmSec.Offset); |
162 | switch (WasmSec.Type) { |
163 | case wasm::WASM_SEC_CUSTOM: |
164 | W.printString(Label: "Name" , Value: 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(Label: "Name" , Value: Seg.Name); |
181 | W.printNumber(Label: "Size" , Value: 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(Label: "Offset" , Value: Seg.Offset.Inst.Value.Int32); |
186 | else if (Seg.Offset.Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) |
187 | W.printNumber(Label: "Offset" , Value: 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(Label: "Global" , Value: 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(Label: "MinPages" , Value: Memory.Minimum); |
201 | if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { |
202 | W.printNumber(Label: "MaxPages" , Value: 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(Label: "SectionData" , Value: WasmSec.Content); |
216 | } |
217 | } |
218 | } |
219 | |
220 | void WasmDumper::printSymbol(const SymbolRef &Sym) { |
221 | DictScope D(W, "Symbol" ); |
222 | WasmSymbol Symbol = Obj->getWasmSymbol(Symb: Sym.getRawDataRefImpl()); |
223 | W.printString(Label: "Name" , Value: Symbol.Info.Name); |
224 | W.printEnum(Label: "Type" , Value: Symbol.Info.Kind, EnumValues: ArrayRef(WasmSymbolTypes)); |
225 | W.printFlags(Label: "Flags" , Value: Symbol.Info.Flags, Flags: ArrayRef(WasmSymbolFlags)); |
226 | |
227 | if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { |
228 | if (Symbol.Info.ImportName) { |
229 | W.printString(Label: "ImportName" , Value: *Symbol.Info.ImportName); |
230 | } |
231 | if (Symbol.Info.ImportModule) { |
232 | W.printString(Label: "ImportModule" , Value: *Symbol.Info.ImportModule); |
233 | } |
234 | } |
235 | if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { |
236 | W.printHex(Label: "ElementIndex" , Value: Symbol.Info.ElementIndex); |
237 | } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { |
238 | W.printHex(Label: "Offset" , Value: Symbol.Info.DataRef.Offset); |
239 | W.printHex(Label: "Segment" , Value: Symbol.Info.DataRef.Segment); |
240 | W.printHex(Label: "Size" , Value: 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>(args: &Obj, args&: Writer); |
251 | } |
252 | |
253 | } // namespace llvm |
254 | |