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