1//===------ utils/wasm2yaml.cpp - obj2yaml conversion tool ------*- C++ -*-===//
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#include "obj2yaml.h"
10#include "llvm/Object/COFF.h"
11#include "llvm/ObjectYAML/WasmYAML.h"
12#include "llvm/Support/ErrorHandling.h"
13#include "llvm/Support/LEB128.h"
14#include "llvm/Support/YAMLTraits.h"
15
16using namespace llvm;
17using object::WasmSection;
18
19namespace {
20
21class WasmDumper {
22 const object::WasmObjectFile &Obj;
23
24public:
25 WasmDumper(const object::WasmObjectFile &O) : Obj(O) {}
26
27 ErrorOr<WasmYAML::Object *> dump();
28
29 std::unique_ptr<WasmYAML::CustomSection>
30 dumpCustomSection(const WasmSection &WasmSec);
31};
32
33} // namespace
34
35static WasmYAML::Limits makeLimits(const wasm::WasmLimits &Limits) {
36 WasmYAML::Limits L;
37 L.Flags = Limits.Flags;
38 L.Minimum = Limits.Minimum;
39 L.Maximum = Limits.Maximum;
40 L.PageSize = Limits.PageSize;
41 return L;
42}
43
44static WasmYAML::Table makeTable(uint32_t Index,
45 const wasm::WasmTableType &Type) {
46 WasmYAML::Table T;
47 T.Index = Index;
48 T.ElemType = (uint32_t)Type.ElemType;
49 T.TableLimits = makeLimits(Limits: Type.Limits);
50 return T;
51}
52
53std::unique_ptr<WasmYAML::CustomSection>
54WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
55 std::unique_ptr<WasmYAML::CustomSection> CustomSec;
56 if (WasmSec.Name == "dylink" || WasmSec.Name == "dylink.0") {
57 std::unique_ptr<WasmYAML::DylinkSection> DylinkSec =
58 std::make_unique<WasmYAML::DylinkSection>();
59 const wasm::WasmDylinkInfo& Info = Obj.dylinkInfo();
60 DylinkSec->MemorySize = Info.MemorySize;
61 DylinkSec->MemoryAlignment = Info.MemoryAlignment;
62 DylinkSec->TableSize = Info.TableSize;
63 DylinkSec->TableAlignment = Info.TableAlignment;
64 DylinkSec->Needed = Info.Needed;
65 DylinkSec->RuntimePath = Info.RuntimePath;
66 for (const auto &Imp : Info.ImportInfo)
67 DylinkSec->ImportInfo.push_back(x: {.Module: Imp.Module, .Field: Imp.Field, .Flags: Imp.Flags});
68 for (const auto &Exp : Info.ExportInfo)
69 DylinkSec->ExportInfo.push_back(x: {.Name: Exp.Name, .Flags: Exp.Flags});
70 CustomSec = std::move(DylinkSec);
71 } else if (WasmSec.Name == "name") {
72 std::unique_ptr<WasmYAML::NameSection> NameSec =
73 std::make_unique<WasmYAML::NameSection>();
74 for (const llvm::wasm::WasmDebugName &Name : Obj.debugNames()) {
75 WasmYAML::NameEntry NameEntry;
76 NameEntry.Name = Name.Name;
77 NameEntry.Index = Name.Index;
78 if (Name.Type == llvm::wasm::NameType::FUNCTION) {
79 NameSec->FunctionNames.push_back(x: NameEntry);
80 } else if (Name.Type == llvm::wasm::NameType::GLOBAL) {
81 NameSec->GlobalNames.push_back(x: NameEntry);
82 } else {
83 assert(Name.Type == llvm::wasm::NameType::DATA_SEGMENT);
84 NameSec->DataSegmentNames.push_back(x: NameEntry);
85 }
86 }
87 CustomSec = std::move(NameSec);
88 } else if (WasmSec.Name == "linking") {
89 std::unique_ptr<WasmYAML::LinkingSection> LinkingSec =
90 std::make_unique<WasmYAML::LinkingSection>();
91 LinkingSec->Version = Obj.linkingData().Version;
92
93 ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats;
94 for (StringRef ComdatName : Comdats)
95 LinkingSec->Comdats.emplace_back(args: WasmYAML::Comdat{.Name: ComdatName, .Entries: {}});
96 for (auto &Func : Obj.functions()) {
97 if (Func.Comdat != UINT32_MAX) {
98 LinkingSec->Comdats[Func.Comdat].Entries.emplace_back(
99 args: WasmYAML::ComdatEntry{.Kind: wasm::WASM_COMDAT_FUNCTION, .Index: Func.Index});
100 }
101 }
102
103 uint32_t SegmentIndex = 0;
104 for (const object::WasmSegment &Segment : Obj.dataSegments()) {
105 if (!Segment.Data.Name.empty()) {
106 WasmYAML::SegmentInfo SegmentInfo;
107 SegmentInfo.Name = Segment.Data.Name;
108 SegmentInfo.Index = SegmentIndex;
109 SegmentInfo.Alignment = Segment.Data.Alignment;
110 SegmentInfo.Flags = Segment.Data.LinkingFlags;
111 LinkingSec->SegmentInfos.push_back(x: SegmentInfo);
112 }
113 if (Segment.Data.Comdat != UINT32_MAX) {
114 LinkingSec->Comdats[Segment.Data.Comdat].Entries.emplace_back(
115 args: WasmYAML::ComdatEntry{.Kind: wasm::WASM_COMDAT_DATA, .Index: SegmentIndex});
116 }
117 SegmentIndex++;
118 }
119 uint32_t SectionIndex = 0;
120 for (const auto &Sec : Obj.sections()) {
121 const WasmSection &WasmSec = Obj.getWasmSection(Section: Sec);
122 if (WasmSec.Comdat != UINT32_MAX)
123 LinkingSec->Comdats[WasmSec.Comdat].Entries.emplace_back(
124 args: WasmYAML::ComdatEntry{.Kind: wasm::WASM_COMDAT_SECTION, .Index: SectionIndex});
125 SectionIndex++;
126 }
127
128 uint32_t SymbolIndex = 0;
129 for (const object::SymbolRef &Sym : Obj.symbols()) {
130 const wasm::WasmSymbolInfo &Symbol = Obj.getWasmSymbol(Symbol: Sym).Info;
131 WasmYAML::SymbolInfo Info;
132 Info.Index = SymbolIndex++;
133 Info.Kind = static_cast<uint32_t>(Symbol.Kind);
134 Info.Name = Symbol.Name;
135 Info.Flags = Symbol.Flags;
136 switch (Symbol.Kind) {
137 case wasm::WASM_SYMBOL_TYPE_DATA:
138 Info.DataRef = Symbol.DataRef;
139 break;
140 case wasm::WASM_SYMBOL_TYPE_FUNCTION:
141 case wasm::WASM_SYMBOL_TYPE_GLOBAL:
142 case wasm::WASM_SYMBOL_TYPE_TABLE:
143 case wasm::WASM_SYMBOL_TYPE_TAG:
144 Info.ElementIndex = Symbol.ElementIndex;
145 break;
146 case wasm::WASM_SYMBOL_TYPE_SECTION:
147 Info.ElementIndex = Symbol.ElementIndex;
148 break;
149 }
150 LinkingSec->SymbolTable.emplace_back(args&: Info);
151 }
152
153 for (const wasm::WasmInitFunc &Func : Obj.linkingData().InitFunctions) {
154 WasmYAML::InitFunction F{.Priority: Func.Priority, .Symbol: Func.Symbol};
155 LinkingSec->InitFunctions.emplace_back(args&: F);
156 }
157
158 CustomSec = std::move(LinkingSec);
159 } else if (WasmSec.Name == "producers") {
160 std::unique_ptr<WasmYAML::ProducersSection> ProducersSec =
161 std::make_unique<WasmYAML::ProducersSection>();
162 const llvm::wasm::WasmProducerInfo &Info = Obj.getProducerInfo();
163 for (auto &E : Info.Languages) {
164 WasmYAML::ProducerEntry Producer;
165 Producer.Name = E.first;
166 Producer.Version = E.second;
167 ProducersSec->Languages.push_back(x: Producer);
168 }
169 for (auto &E : Info.Tools) {
170 WasmYAML::ProducerEntry Producer;
171 Producer.Name = E.first;
172 Producer.Version = E.second;
173 ProducersSec->Tools.push_back(x: Producer);
174 }
175 for (auto &E : Info.SDKs) {
176 WasmYAML::ProducerEntry Producer;
177 Producer.Name = E.first;
178 Producer.Version = E.second;
179 ProducersSec->SDKs.push_back(x: Producer);
180 }
181 CustomSec = std::move(ProducersSec);
182 } else if (WasmSec.Name == "target_features") {
183 std::unique_ptr<WasmYAML::TargetFeaturesSection> TargetFeaturesSec =
184 std::make_unique<WasmYAML::TargetFeaturesSection>();
185 for (auto &E : Obj.getTargetFeatures()) {
186 WasmYAML::FeatureEntry Feature;
187 Feature.Prefix = E.Prefix;
188 Feature.Name = E.Name;
189 TargetFeaturesSec->Features.push_back(x: Feature);
190 }
191 CustomSec = std::move(TargetFeaturesSec);
192 } else {
193 CustomSec = std::make_unique<WasmYAML::CustomSection>(args: WasmSec.Name);
194 }
195 CustomSec->Payload = yaml::BinaryRef(WasmSec.Content);
196 return CustomSec;
197}
198
199ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
200 auto Y = std::make_unique<WasmYAML::Object>();
201
202 // Dump header
203 Y->Header.Version = Obj.getHeader().Version;
204
205 // Dump sections
206 for (const auto &Sec : Obj.sections()) {
207 const WasmSection &WasmSec = Obj.getWasmSection(Section: Sec);
208 std::unique_ptr<WasmYAML::Section> S;
209 switch (WasmSec.Type) {
210 case wasm::WASM_SEC_CUSTOM: {
211 if (WasmSec.Name.starts_with(Prefix: "reloc.")) {
212 // Relocations are attached the sections they apply to rather than
213 // being represented as a custom section in the YAML output.
214 continue;
215 }
216 S = dumpCustomSection(WasmSec);
217 break;
218 }
219 case wasm::WASM_SEC_TYPE: {
220 auto TypeSec = std::make_unique<WasmYAML::TypeSection>();
221 uint32_t Index = 0;
222 for (const auto &FunctionSig : Obj.types()) {
223 WasmYAML::Signature Sig;
224 Sig.Index = Index++;
225 for (const auto &ParamType : FunctionSig.Params)
226 Sig.ParamTypes.emplace_back(args: static_cast<uint32_t>(ParamType));
227 for (const auto &ReturnType : FunctionSig.Returns)
228 Sig.ReturnTypes.emplace_back(args: static_cast<uint32_t>(ReturnType));
229 TypeSec->Signatures.push_back(x: Sig);
230 }
231 S = std::move(TypeSec);
232 break;
233 }
234 case wasm::WASM_SEC_IMPORT: {
235 auto ImportSec = std::make_unique<WasmYAML::ImportSection>();
236 for (auto &Import : Obj.imports()) {
237 WasmYAML::Import Im;
238 Im.Module = Import.Module;
239 Im.Field = Import.Field;
240 Im.Kind = Import.Kind;
241 switch (Im.Kind) {
242 case wasm::WASM_EXTERNAL_FUNCTION:
243 Im.SigIndex = Import.SigIndex;
244 break;
245 case wasm::WASM_EXTERNAL_GLOBAL:
246 Im.GlobalImport.Type = Import.Global.Type;
247 Im.GlobalImport.Mutable = Import.Global.Mutable;
248 break;
249 case wasm::WASM_EXTERNAL_TAG:
250 Im.SigIndex = Import.SigIndex;
251 break;
252 case wasm::WASM_EXTERNAL_TABLE:
253 // FIXME: Currently we always output an index of 0 for any imported
254 // table.
255 Im.TableImport = makeTable(Index: 0, Type: Import.Table);
256 break;
257 case wasm::WASM_EXTERNAL_MEMORY:
258 Im.Memory = makeLimits(Limits: Import.Memory);
259 break;
260 }
261 ImportSec->Imports.push_back(x: Im);
262 }
263 S = std::move(ImportSec);
264 break;
265 }
266 case wasm::WASM_SEC_FUNCTION: {
267 auto FuncSec = std::make_unique<WasmYAML::FunctionSection>();
268 for (const auto &Func : Obj.functions()) {
269 FuncSec->FunctionTypes.push_back(x: Func.SigIndex);
270 }
271 S = std::move(FuncSec);
272 break;
273 }
274 case wasm::WASM_SEC_TABLE: {
275 auto TableSec = std::make_unique<WasmYAML::TableSection>();
276 for (const wasm::WasmTable &Table : Obj.tables()) {
277 TableSec->Tables.push_back(x: makeTable(Index: Table.Index, Type: Table.Type));
278 }
279 S = std::move(TableSec);
280 break;
281 }
282 case wasm::WASM_SEC_MEMORY: {
283 auto MemorySec = std::make_unique<WasmYAML::MemorySection>();
284 for (const wasm::WasmLimits &Memory : Obj.memories()) {
285 MemorySec->Memories.push_back(x: makeLimits(Limits: Memory));
286 }
287 S = std::move(MemorySec);
288 break;
289 }
290 case wasm::WASM_SEC_TAG: {
291 auto TagSec = std::make_unique<WasmYAML::TagSection>();
292 for (auto &Tag : Obj.tags()) {
293 TagSec->TagTypes.push_back(x: Tag.SigIndex);
294 }
295 S = std::move(TagSec);
296 break;
297 }
298 case wasm::WASM_SEC_GLOBAL: {
299 auto GlobalSec = std::make_unique<WasmYAML::GlobalSection>();
300 for (auto &Global : Obj.globals()) {
301 WasmYAML::Global G;
302 G.Index = Global.Index;
303 G.Type = Global.Type.Type;
304 G.Mutable = Global.Type.Mutable;
305 G.Init.Extended = Global.InitExpr.Extended;
306 if (Global.InitExpr.Extended) {
307 G.Init.Body = Global.InitExpr.Body;
308 } else {
309 G.Init.Inst = Global.InitExpr.Inst;
310 }
311 GlobalSec->Globals.push_back(x: G);
312 }
313 S = std::move(GlobalSec);
314 break;
315 }
316 case wasm::WASM_SEC_START: {
317 auto StartSec = std::make_unique<WasmYAML::StartSection>();
318 StartSec->StartFunction = Obj.startFunction();
319 S = std::move(StartSec);
320 break;
321 }
322 case wasm::WASM_SEC_EXPORT: {
323 auto ExportSec = std::make_unique<WasmYAML::ExportSection>();
324 for (auto &Export : Obj.exports()) {
325 WasmYAML::Export Ex;
326 Ex.Name = Export.Name;
327 Ex.Kind = Export.Kind;
328 Ex.Index = Export.Index;
329 ExportSec->Exports.push_back(x: Ex);
330 }
331 S = std::move(ExportSec);
332 break;
333 }
334 case wasm::WASM_SEC_ELEM: {
335 auto ElemSec = std::make_unique<WasmYAML::ElemSection>();
336 for (auto &Segment : Obj.elements()) {
337 WasmYAML::ElemSegment Seg;
338 Seg.Flags = Segment.Flags;
339 Seg.TableNumber = Segment.TableNumber;
340 Seg.ElemKind = (uint32_t)Segment.ElemKind;
341 Seg.Offset.Extended = Segment.Offset.Extended;
342 if (Seg.Offset.Extended) {
343 Seg.Offset.Body = yaml::BinaryRef(Segment.Offset.Body);
344 } else {
345 Seg.Offset.Inst = Segment.Offset.Inst;
346 }
347 append_range(C&: Seg.Functions, R: Segment.Functions);
348 ElemSec->Segments.push_back(x: Seg);
349 }
350 S = std::move(ElemSec);
351 break;
352 }
353 case wasm::WASM_SEC_CODE: {
354 auto CodeSec = std::make_unique<WasmYAML::CodeSection>();
355 for (auto &Func : Obj.functions()) {
356 WasmYAML::Function Function;
357 Function.Index = Func.Index;
358 for (auto &Local : Func.Locals) {
359 WasmYAML::LocalDecl LocalDecl;
360 LocalDecl.Type = Local.Type;
361 LocalDecl.Count = Local.Count;
362 Function.Locals.push_back(x: LocalDecl);
363 }
364 Function.Body = yaml::BinaryRef(Func.Body);
365 CodeSec->Functions.push_back(x: Function);
366 }
367 S = std::move(CodeSec);
368 break;
369 }
370 case wasm::WASM_SEC_DATA: {
371 auto DataSec = std::make_unique<WasmYAML::DataSection>();
372 for (const object::WasmSegment &Segment : Obj.dataSegments()) {
373 WasmYAML::DataSegment Seg;
374 Seg.SectionOffset = Segment.SectionOffset;
375 Seg.InitFlags = Segment.Data.InitFlags;
376 Seg.MemoryIndex = Segment.Data.MemoryIndex;
377 Seg.Offset.Extended = Segment.Data.Offset.Extended;
378 if (Seg.Offset.Extended) {
379 Seg.Offset.Body = yaml::BinaryRef(Segment.Data.Offset.Body);
380 } else {
381 Seg.Offset.Inst = Segment.Data.Offset.Inst;
382 }
383 Seg.Content = yaml::BinaryRef(Segment.Data.Content);
384 DataSec->Segments.push_back(x: Seg);
385 }
386 S = std::move(DataSec);
387 break;
388 }
389 case wasm::WASM_SEC_DATACOUNT: {
390 auto DataCountSec = std::make_unique<WasmYAML::DataCountSection>();
391 DataCountSec->Count = Obj.dataSegments().size();
392 S = std::move(DataCountSec);
393 break;
394 }
395 default:
396 llvm_unreachable("Unknown section type");
397 break;
398 }
399
400 // Only propagate the section size encoding length if it's not the minimal
401 // size or 5 (the default "padded" value). This is to avoid having every
402 // YAML output polluted with this value when we usually don't care about it
403 // (and avoid rewriting all the test expectations).
404 if (WasmSec.HeaderSecSizeEncodingLen &&
405 WasmSec.HeaderSecSizeEncodingLen !=
406 getULEB128Size(Value: WasmSec.Content.size()) &&
407 WasmSec.HeaderSecSizeEncodingLen != 5)
408 S->HeaderSecSizeEncodingLen = WasmSec.HeaderSecSizeEncodingLen;
409
410 for (const wasm::WasmRelocation &Reloc : WasmSec.Relocations) {
411 WasmYAML::Relocation R;
412 R.Type = Reloc.Type;
413 R.Index = Reloc.Index;
414 R.Offset = Reloc.Offset;
415 R.Addend = Reloc.Addend;
416 S->Relocations.push_back(x: R);
417 }
418 Y->Sections.push_back(x: std::move(S));
419 }
420
421 return Y.release();
422}
423
424std::error_code wasm2yaml(raw_ostream &Out, const object::WasmObjectFile &Obj) {
425 WasmDumper Dumper(Obj);
426 ErrorOr<WasmYAML::Object *> YAMLOrErr = Dumper.dump();
427 if (std::error_code EC = YAMLOrErr.getError())
428 return EC;
429
430 std::unique_ptr<WasmYAML::Object> YAML(YAMLOrErr.get());
431 yaml::Output Yout(Out);
432 Yout << *YAML;
433
434 return std::error_code();
435}
436