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 | return L; |
41 | } |
42 | |
43 | static 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 | |
52 | std::unique_ptr<WasmYAML::CustomSection> |
53 | WasmDumper::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> = |
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 | |
197 | ErrorOr<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 | |
422 | std::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 | |