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