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 | |
16 | using namespace llvm; |
17 | using object::WasmSection; |
18 | |
19 | namespace { |
20 | |
21 | class WasmDumper { |
22 | const object::WasmObjectFile &Obj; |
23 | |
24 | public: |
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 | |
35 | static 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 | |
44 | static 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 | |
53 | std::unique_ptr<WasmYAML::CustomSection> |
54 | WasmDumper::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> = |
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 | |
199 | ErrorOr<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 | |
424 | std::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 | |