/src/WasmEdge/lib/driver/parseTool.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "ast/module.h" |
5 | | #include "common/configure.h" |
6 | | #include "common/filesystem.h" |
7 | | #include "common/spdlog.h" |
8 | | #include "driver/tool.h" |
9 | | #include "loader/loader.h" |
10 | | |
11 | | #include <cstdlib> |
12 | | #include <map> |
13 | | #include <string> |
14 | | #include <string_view> |
15 | | |
16 | | using namespace std::literals; |
17 | | |
18 | | namespace WasmEdge { |
19 | | namespace Driver { |
20 | | |
21 | | namespace { |
22 | | |
23 | | struct NameSection { |
24 | | std::map<uint32_t, std::string> FuncNames; |
25 | | std::map<uint32_t, std::string> GlobalNames; |
26 | | }; |
27 | | |
28 | 0 | uint32_t decodeLEB128U32(Span<const Byte> Data, uint32_t &Offset) { |
29 | 0 | uint32_t Result = 0; |
30 | 0 | uint32_t Shift = 0; |
31 | 0 | while (Offset < Data.size()) { |
32 | 0 | uint8_t B = static_cast<uint8_t>(Data[Offset++]); |
33 | 0 | Result |= static_cast<uint32_t>(B & 0x7F) << Shift; |
34 | 0 | if ((B & 0x80) == 0) |
35 | 0 | break; |
36 | 0 | Shift += 7; |
37 | 0 | } |
38 | 0 | return Result; |
39 | 0 | } |
40 | | |
41 | 0 | std::string decodeName(Span<const Byte> Data, uint32_t &Offset) { |
42 | 0 | uint32_t Len = decodeLEB128U32(Data, Offset); |
43 | 0 | if (Offset + Len > Data.size()) |
44 | 0 | return {}; |
45 | 0 | std::string Name(reinterpret_cast<const char *>(&Data[Offset]), Len); |
46 | 0 | Offset += Len; |
47 | 0 | return Name; |
48 | 0 | } |
49 | | |
50 | | std::map<uint32_t, std::string> decodeNameMap(Span<const Byte> Data, |
51 | 0 | uint32_t &Offset) { |
52 | 0 | std::map<uint32_t, std::string> Map; |
53 | 0 | uint32_t Count = decodeLEB128U32(Data, Offset); |
54 | 0 | for (uint32_t I = 0; I < Count && Offset < Data.size(); I++) { |
55 | 0 | uint32_t Idx = decodeLEB128U32(Data, Offset); |
56 | 0 | std::string Name = decodeName(Data, Offset); |
57 | 0 | Map[Idx] = std::move(Name); |
58 | 0 | } |
59 | 0 | return Map; |
60 | 0 | } |
61 | | |
62 | 0 | NameSection parseNameSection(Span<const Byte> Data) { |
63 | 0 | NameSection NS; |
64 | 0 | uint32_t Offset = 0; |
65 | 0 | while (Offset < Data.size()) { |
66 | 0 | uint8_t SubId = static_cast<uint8_t>(Data[Offset++]); |
67 | 0 | uint32_t SubSize = decodeLEB128U32(Data, Offset); |
68 | 0 | uint32_t SubEnd = Offset + SubSize; |
69 | 0 | if (SubEnd > Data.size()) |
70 | 0 | break; |
71 | 0 | if (SubId == 1) { |
72 | 0 | NS.FuncNames = decodeNameMap(Data, Offset); |
73 | 0 | } else if (SubId == 7) { |
74 | 0 | NS.GlobalNames = decodeNameMap(Data, Offset); |
75 | 0 | } |
76 | 0 | Offset = SubEnd; |
77 | 0 | } |
78 | 0 | return NS; |
79 | 0 | } |
80 | | |
81 | 0 | std::string getInitExprStr(const AST::Expression &Expr) { |
82 | 0 | const auto Instrs = Expr.getInstrs(); |
83 | 0 | if (Instrs.empty()) |
84 | 0 | return {}; |
85 | 0 | const auto &Instr = Instrs[0]; |
86 | 0 | switch (Instr.getOpCode()) { |
87 | 0 | case OpCode::I32__const: |
88 | 0 | return fmt::format("i32={}", Instr.getNum().get<int32_t>()); |
89 | 0 | case OpCode::I64__const: |
90 | 0 | return fmt::format("i64={}", Instr.getNum().get<int64_t>()); |
91 | 0 | case OpCode::F32__const: |
92 | 0 | return fmt::format("f32={}", Instr.getNum().get<float>()); |
93 | 0 | case OpCode::F64__const: |
94 | 0 | return fmt::format("f64={}", Instr.getNum().get<double>()); |
95 | 0 | case OpCode::Global__get: |
96 | 0 | return fmt::format("global.get {}", Instr.getTargetIndex()); |
97 | 0 | case OpCode::Ref__func: |
98 | 0 | return fmt::format("ref.func {}", Instr.getTargetIndex()); |
99 | 0 | case OpCode::Ref__null: |
100 | 0 | return fmt::format("ref.null {}", Instr.getValType()); |
101 | 0 | default: |
102 | 0 | return {}; |
103 | 0 | } |
104 | 0 | } |
105 | | |
106 | | // Renders a single element segment entry, i.e. one of the init expressions |
107 | | // in an element segment's `getInitExprs()` list. |
108 | 0 | std::string getElemEntryStr(const AST::Expression &Expr) { |
109 | 0 | const auto Instrs = Expr.getInstrs(); |
110 | 0 | if (Instrs.empty()) |
111 | 0 | return "ref.null"; |
112 | 0 | const auto &Instr = Instrs[0]; |
113 | 0 | switch (Instr.getOpCode()) { |
114 | 0 | case OpCode::Ref__func: |
115 | 0 | return fmt::format("func[{}]", Instr.getTargetIndex()); |
116 | 0 | case OpCode::Ref__null: |
117 | 0 | return "ref.null"; |
118 | 0 | default: |
119 | 0 | return getInitExprStr(Expr); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | } // namespace |
124 | | |
125 | 0 | int ParseTool(struct DriverToolOptions &Opt) noexcept { |
126 | 0 | std::ios::sync_with_stdio(false); |
127 | |
|
128 | 0 | Configure Conf = createConfigure(Opt); |
129 | 0 | const auto InputPath = |
130 | 0 | std::filesystem::absolute(std::filesystem::u8path(Opt.SoName.value())); |
131 | |
|
132 | 0 | Loader::Loader Loader(Conf); |
133 | 0 | auto Res = Loader.parseModule(InputPath); |
134 | 0 | if (!Res) { |
135 | 0 | spdlog::error("Failed to parse WASM module."sv); |
136 | 0 | return EXIT_FAILURE; |
137 | 0 | } |
138 | 0 | const auto &Mod = **Res; |
139 | |
|
140 | 0 | const auto &Customs = Mod.getCustomSections(); |
141 | 0 | NameSection NS; |
142 | 0 | for (const auto &Custom : Customs) { |
143 | 0 | if (Custom.getName() == "name") { |
144 | 0 | NS = parseNameSection(Custom.getContent()); |
145 | 0 | break; |
146 | 0 | } |
147 | 0 | } |
148 | |
|
149 | 0 | const auto &Imports = Mod.getImportSection().getContent(); |
150 | 0 | uint32_t ImportedFuncCount = 0; |
151 | 0 | uint32_t ImportedGlobalCount = 0; |
152 | 0 | uint32_t ImportedTableCount = 0; |
153 | 0 | uint32_t ImportedMemCount = 0; |
154 | 0 | uint32_t ImportedTagCount = 0; |
155 | 0 | for (const auto &Imp : Imports) { |
156 | 0 | if (Imp.getExternalType() == ExternalType::Function) |
157 | 0 | ImportedFuncCount++; |
158 | 0 | else if (Imp.getExternalType() == ExternalType::Global) |
159 | 0 | ImportedGlobalCount++; |
160 | 0 | else if (Imp.getExternalType() == ExternalType::Table) |
161 | 0 | ImportedTableCount++; |
162 | 0 | else if (Imp.getExternalType() == ExternalType::Memory) |
163 | 0 | ImportedMemCount++; |
164 | 0 | else if (Imp.getExternalType() == ExternalType::Tag) |
165 | 0 | ImportedTagCount++; |
166 | 0 | } |
167 | |
|
168 | 0 | auto FuncName = [&](uint32_t Idx) -> std::string { |
169 | 0 | auto It = NS.FuncNames.find(Idx); |
170 | 0 | if (It != NS.FuncNames.end()) |
171 | 0 | return fmt::format(" <{}>", It->second); |
172 | 0 | return {}; |
173 | 0 | }; |
174 | |
|
175 | 0 | auto GlobalName = [&](uint32_t Idx) -> std::string { |
176 | 0 | auto It = NS.GlobalNames.find(Idx); |
177 | 0 | if (It != NS.GlobalNames.end()) |
178 | 0 | return fmt::format(" <{}>", It->second); |
179 | 0 | return {}; |
180 | 0 | }; |
181 | |
|
182 | 0 | fmt::print("{}: file format wasm 0x1\n\n", InputPath.filename().string()); |
183 | 0 | fmt::print("Section Details:\n\n"); |
184 | | |
185 | | // Type Section |
186 | 0 | const auto &Types = Mod.getTypeSection().getContent(); |
187 | 0 | if (!Types.empty()) { |
188 | 0 | fmt::print("Type[{}]:\n", Types.size()); |
189 | 0 | for (uint32_t I = 0; I < Types.size(); I++) { |
190 | 0 | const auto &FuncType = Types[I].getCompositeType().getFuncType(); |
191 | 0 | fmt::print(" - type[{}] (", I); |
192 | 0 | const auto &Params = FuncType.getParamTypes(); |
193 | 0 | for (uint32_t J = 0; J < Params.size(); J++) { |
194 | 0 | if (J > 0) |
195 | 0 | fmt::print(", "); |
196 | 0 | fmt::print("{}", Params[J]); |
197 | 0 | } |
198 | 0 | fmt::print(") -> "); |
199 | 0 | const auto &Returns = FuncType.getReturnTypes(); |
200 | 0 | if (Returns.empty()) { |
201 | 0 | fmt::print("nil"); |
202 | 0 | } else { |
203 | 0 | for (uint32_t J = 0; J < Returns.size(); J++) { |
204 | 0 | if (J > 0) |
205 | 0 | fmt::print(", "); |
206 | 0 | fmt::print("{}", Returns[J]); |
207 | 0 | } |
208 | 0 | } |
209 | 0 | fmt::print("\n"); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | // Import Section |
214 | 0 | if (!Imports.empty()) { |
215 | 0 | fmt::print("Import[{}]:\n", Imports.size()); |
216 | 0 | uint32_t ImpFuncIdx = 0; |
217 | 0 | uint32_t ImpGlobalIdx = 0; |
218 | 0 | uint32_t ImpMemIdx = 0; |
219 | 0 | uint32_t ImpTableIdx = 0; |
220 | 0 | uint32_t ImpTagIdx = 0; |
221 | 0 | for (const auto &Imp : Imports) { |
222 | 0 | switch (Imp.getExternalType()) { |
223 | 0 | case ExternalType::Function: |
224 | 0 | fmt::print(" - func[{}] sig={}{} <- {}.{}\n", ImpFuncIdx, |
225 | 0 | Imp.getExternalFuncTypeIdx(), FuncName(ImpFuncIdx), |
226 | 0 | Imp.getModuleName(), Imp.getExternalName()); |
227 | 0 | ImpFuncIdx++; |
228 | 0 | break; |
229 | 0 | case ExternalType::Memory: { |
230 | 0 | const auto &Limit = Imp.getExternalMemoryType().getLimit(); |
231 | 0 | fmt::print(" - memory[{}] pages: initial={}", ImpMemIdx, |
232 | 0 | Limit.getMin()); |
233 | 0 | if (Limit.hasMax()) |
234 | 0 | fmt::print(" max={}", Limit.getMax()); |
235 | 0 | fmt::print(" <- {}.{}\n", Imp.getModuleName(), Imp.getExternalName()); |
236 | 0 | ImpMemIdx++; |
237 | 0 | break; |
238 | 0 | } |
239 | 0 | case ExternalType::Table: |
240 | 0 | fmt::print(" - table[{}] <- {}.{}\n", ImpTableIdx, Imp.getModuleName(), |
241 | 0 | Imp.getExternalName()); |
242 | 0 | ImpTableIdx++; |
243 | 0 | break; |
244 | 0 | case ExternalType::Global: { |
245 | 0 | const auto > = Imp.getExternalGlobalType(); |
246 | 0 | fmt::print(" - global[{}] {} mutable={}{} <- {}.{}\n", ImpGlobalIdx, |
247 | 0 | GT.getValType(), GT.getValMut() == ValMut::Var ? 1 : 0, |
248 | 0 | GlobalName(ImpGlobalIdx), Imp.getModuleName(), |
249 | 0 | Imp.getExternalName()); |
250 | 0 | ImpGlobalIdx++; |
251 | 0 | break; |
252 | 0 | } |
253 | 0 | case ExternalType::Tag: |
254 | 0 | fmt::print(" - tag[{}] sig={} <- {}.{}\n", ImpTagIdx, |
255 | 0 | Imp.getExternalTagType().getTypeIdx(), Imp.getModuleName(), |
256 | 0 | Imp.getExternalName()); |
257 | 0 | ImpTagIdx++; |
258 | 0 | break; |
259 | 0 | default: |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | // Function Section |
266 | 0 | const auto &Funcs = Mod.getFunctionSection().getContent(); |
267 | 0 | if (!Funcs.empty()) { |
268 | 0 | fmt::print("Function[{}]:\n", Funcs.size()); |
269 | 0 | for (uint32_t I = 0; I < Funcs.size(); I++) { |
270 | 0 | uint32_t Idx = I + ImportedFuncCount; |
271 | 0 | fmt::print(" - func[{}] sig={}{}\n", Idx, Funcs[I], FuncName(Idx)); |
272 | 0 | } |
273 | 0 | } |
274 | | |
275 | | // Table Section |
276 | 0 | const auto &Tables = Mod.getTableSection().getContent(); |
277 | 0 | if (!Tables.empty()) { |
278 | 0 | fmt::print("Table[{}]:\n", Tables.size()); |
279 | 0 | for (uint32_t I = 0; I < Tables.size(); I++) { |
280 | 0 | uint32_t Idx = I + ImportedTableCount; |
281 | 0 | const auto &TT = Tables[I].getTableType(); |
282 | 0 | const auto &Lim = TT.getLimit(); |
283 | 0 | fmt::print(" - table[{}] type={} initial={}", Idx, TT.getRefType(), |
284 | 0 | Lim.getMin()); |
285 | 0 | if (Lim.hasMax()) |
286 | 0 | fmt::print(" max={}", Lim.getMax()); |
287 | 0 | fmt::print("\n"); |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | // Memory Section |
292 | 0 | const auto &Mems = Mod.getMemorySection().getContent(); |
293 | 0 | if (!Mems.empty()) { |
294 | 0 | fmt::print("Memory[{}]:\n", Mems.size()); |
295 | 0 | for (uint32_t I = 0; I < Mems.size(); I++) { |
296 | 0 | uint32_t Idx = I + ImportedMemCount; |
297 | 0 | const auto &Lim = Mems[I].getLimit(); |
298 | 0 | fmt::print(" - memory[{}] pages: initial={}", Idx, Lim.getMin()); |
299 | 0 | if (Lim.hasMax()) |
300 | 0 | fmt::print(" max={}", Lim.getMax()); |
301 | 0 | fmt::print("\n"); |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | // Tag Section |
306 | 0 | const auto &Tags = Mod.getTagSection().getContent(); |
307 | 0 | if (!Tags.empty()) { |
308 | 0 | fmt::print("Tag[{}]:\n", Tags.size()); |
309 | 0 | for (uint32_t I = 0; I < Tags.size(); I++) { |
310 | 0 | uint32_t Idx = I + ImportedTagCount; |
311 | 0 | fmt::print(" - tag[{}] sig={}\n", Idx, Tags[I].getTypeIdx()); |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | // Global Section |
316 | 0 | const auto &Globals = Mod.getGlobalSection().getContent(); |
317 | 0 | if (!Globals.empty()) { |
318 | 0 | fmt::print("Global[{}]:\n", Globals.size()); |
319 | 0 | for (uint32_t I = 0; I < Globals.size(); I++) { |
320 | 0 | uint32_t Idx = I + ImportedGlobalCount; |
321 | 0 | const auto > = Globals[I].getGlobalType(); |
322 | 0 | std::string InitStr = getInitExprStr(Globals[I].getExpr()); |
323 | 0 | fmt::print(" - global[{}] {} mutable={}{}", Idx, GT.getValType(), |
324 | 0 | GT.getValMut() == ValMut::Var ? 1 : 0, GlobalName(Idx)); |
325 | 0 | if (!InitStr.empty()) |
326 | 0 | fmt::print(" - init {}", InitStr); |
327 | 0 | fmt::print("\n"); |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | // Export Section |
332 | 0 | const auto &Exports = Mod.getExportSection().getContent(); |
333 | 0 | if (!Exports.empty()) { |
334 | 0 | fmt::print("Export[{}]:\n", Exports.size()); |
335 | 0 | for (const auto &Exp : Exports) { |
336 | 0 | uint32_t Idx = Exp.getExternalIndex(); |
337 | 0 | switch (Exp.getExternalType()) { |
338 | 0 | case ExternalType::Function: |
339 | 0 | fmt::print(" - func[{}]{} -> \"{}\"\n", Idx, FuncName(Idx), |
340 | 0 | Exp.getExternalName()); |
341 | 0 | break; |
342 | 0 | case ExternalType::Global: |
343 | 0 | fmt::print(" - global[{}] -> \"{}\"\n", Idx, Exp.getExternalName()); |
344 | 0 | break; |
345 | 0 | case ExternalType::Memory: |
346 | 0 | fmt::print(" - memory[{}] -> \"{}\"\n", Idx, Exp.getExternalName()); |
347 | 0 | break; |
348 | 0 | case ExternalType::Table: |
349 | 0 | fmt::print(" - table[{}] -> \"{}\"\n", Idx, Exp.getExternalName()); |
350 | 0 | break; |
351 | 0 | default: |
352 | 0 | break; |
353 | 0 | } |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | | // Start Section |
358 | 0 | if (auto StartIdx = Mod.getStartSection().getContent()) { |
359 | 0 | fmt::print("Start:\n"); |
360 | 0 | fmt::print(" - func[{}]{}\n", *StartIdx, FuncName(*StartIdx)); |
361 | 0 | } |
362 | | |
363 | | // Element Section |
364 | 0 | const auto &Elems = Mod.getElementSection().getContent(); |
365 | 0 | if (!Elems.empty()) { |
366 | 0 | fmt::print("Element[{}]:\n", Elems.size()); |
367 | 0 | for (uint32_t I = 0; I < Elems.size(); I++) { |
368 | 0 | const auto &Elem = Elems[I]; |
369 | 0 | const auto &Entries = Elem.getInitExprs(); |
370 | 0 | switch (Elem.getMode()) { |
371 | 0 | case AST::ElementSegment::ElemMode::Active: { |
372 | 0 | fmt::print(" - segment[{}] flags=0 table={} type={} count={}", I, |
373 | 0 | Elem.getIdx(), Elem.getRefType(), Entries.size()); |
374 | 0 | std::string OffsetStr = getInitExprStr(Elem.getExpr()); |
375 | 0 | if (!OffsetStr.empty()) |
376 | 0 | fmt::print(" - init {}", OffsetStr); |
377 | 0 | fmt::print("\n"); |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | case AST::ElementSegment::ElemMode::Passive: |
381 | 0 | fmt::print(" - segment[{}] flags=1 passive type={} count={}\n", I, |
382 | 0 | Elem.getRefType(), Entries.size()); |
383 | 0 | break; |
384 | 0 | case AST::ElementSegment::ElemMode::Declarative: |
385 | 0 | fmt::print(" - segment[{}] flags=3 declarative type={} count={}\n", I, |
386 | 0 | Elem.getRefType(), Entries.size()); |
387 | 0 | break; |
388 | 0 | } |
389 | 0 | for (uint32_t J = 0; J < Entries.size(); J++) { |
390 | 0 | fmt::print(" - elem[{}] = {}\n", J, getElemEntryStr(Entries[J])); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | | // DataCount Section |
396 | 0 | if (auto Count = Mod.getDataCountSection().getContent()) { |
397 | 0 | fmt::print("DataCount section: {}\n", *Count); |
398 | 0 | } |
399 | | |
400 | | // Code Section |
401 | 0 | const auto &Codes = Mod.getCodeSection().getContent(); |
402 | 0 | if (!Codes.empty()) { |
403 | 0 | fmt::print("Code[{}]:\n", Codes.size()); |
404 | 0 | for (uint32_t I = 0; I < Codes.size(); I++) { |
405 | 0 | uint32_t Idx = I + ImportedFuncCount; |
406 | 0 | fmt::print(" - func[{}] size={}{}\n", Idx, Codes[I].getSegSize(), |
407 | 0 | FuncName(Idx)); |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | // Data Section |
412 | 0 | const auto &Datas = Mod.getDataSection().getContent(); |
413 | 0 | if (!Datas.empty()) { |
414 | 0 | fmt::print("Data[{}]:\n", Datas.size()); |
415 | 0 | for (uint32_t I = 0; I < Datas.size(); I++) { |
416 | 0 | const auto &Data = Datas[I]; |
417 | 0 | switch (Data.getMode()) { |
418 | 0 | case AST::DataSegment::DataMode::Active: { |
419 | 0 | fmt::print(" - segment[{}] memory={} size={}", I, Data.getIdx(), |
420 | 0 | Data.getData().size()); |
421 | 0 | std::string OffsetStr = getInitExprStr(Data.getExpr()); |
422 | 0 | if (!OffsetStr.empty()) |
423 | 0 | fmt::print(" - init {}", OffsetStr); |
424 | 0 | fmt::print("\n"); |
425 | 0 | break; |
426 | 0 | } |
427 | 0 | case AST::DataSegment::DataMode::Passive: |
428 | 0 | fmt::print(" - segment[{}] passive size={}\n", I, |
429 | 0 | Data.getData().size()); |
430 | 0 | break; |
431 | 0 | } |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | // Custom Sections |
436 | 0 | for (const auto &Custom : Customs) { |
437 | 0 | fmt::print("Custom:\n"); |
438 | 0 | fmt::print(" - name: \"{}\"\n", Custom.getName()); |
439 | 0 | if (Custom.getName() == "name") { |
440 | 0 | for (const auto &[Idx, Name] : NS.FuncNames) { |
441 | 0 | fmt::print(" - func[{}] <{}>\n", Idx, Name); |
442 | 0 | } |
443 | 0 | for (const auto &[Idx, Name] : NS.GlobalNames) { |
444 | 0 | fmt::print(" - global[{}] <{}>\n", Idx, Name); |
445 | 0 | } |
446 | 0 | } |
447 | 0 | } |
448 | |
|
449 | | return EXIT_SUCCESS; |
450 | 0 | } |
451 | | |
452 | | } // namespace Driver |
453 | | } // namespace WasmEdge |