Mercurial > hg > CbC > CbC_llvm
diff lib/MC/WasmObjectWriter.cpp @ 121:803732b1fca8
LLVM 5.0
author | kono |
---|---|
date | Fri, 27 Oct 2017 17:07:41 +0900 |
parents | |
children | 3a76565eade5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/MC/WasmObjectWriter.cpp Fri Oct 27 17:07:41 2017 +0900 @@ -0,0 +1,1340 @@ +//===- lib/MC/WasmObjectWriter.cpp - Wasm File Writer ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Wasm object file writer information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSectionWasm.h" +#include "llvm/MC/MCSymbolWasm.h" +#include "llvm/MC/MCValue.h" +#include "llvm/MC/MCWasmObjectWriter.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/StringSaver.h" +#include <vector> + +using namespace llvm; + +#define DEBUG_TYPE "mc" + +namespace { + +// For patching purposes, we need to remember where each section starts, both +// for patching up the section size field, and for patching up references to +// locations within the section. +struct SectionBookkeeping { + // Where the size of the section is written. + uint64_t SizeOffset; + // Where the contents of the section starts (after the header). + uint64_t ContentsOffset; +}; + +// The signature of a wasm function, in a struct capable of being used as a +// DenseMap key. +struct WasmFunctionType { + // Support empty and tombstone instances, needed by DenseMap. + enum { Plain, Empty, Tombstone } State; + + // The return types of the function. + SmallVector<wasm::ValType, 1> Returns; + + // The parameter types of the function. + SmallVector<wasm::ValType, 4> Params; + + WasmFunctionType() : State(Plain) {} + + bool operator==(const WasmFunctionType &Other) const { + return State == Other.State && Returns == Other.Returns && + Params == Other.Params; + } +}; + +// Traits for using WasmFunctionType in a DenseMap. +struct WasmFunctionTypeDenseMapInfo { + static WasmFunctionType getEmptyKey() { + WasmFunctionType FuncTy; + FuncTy.State = WasmFunctionType::Empty; + return FuncTy; + } + static WasmFunctionType getTombstoneKey() { + WasmFunctionType FuncTy; + FuncTy.State = WasmFunctionType::Tombstone; + return FuncTy; + } + static unsigned getHashValue(const WasmFunctionType &FuncTy) { + uintptr_t Value = FuncTy.State; + for (wasm::ValType Ret : FuncTy.Returns) + Value += DenseMapInfo<int32_t>::getHashValue(int32_t(Ret)); + for (wasm::ValType Param : FuncTy.Params) + Value += DenseMapInfo<int32_t>::getHashValue(int32_t(Param)); + return Value; + } + static bool isEqual(const WasmFunctionType &LHS, + const WasmFunctionType &RHS) { + return LHS == RHS; + } +}; + +// A wasm data segment. A wasm binary contains only a single data section +// but that can contain many segments, each with their own virtual location +// in memory. Each MCSection data created by llvm is modeled as its own +// wasm data segment. +struct WasmDataSegment { + MCSectionWasm *Section; + StringRef Name; + uint32_t Offset; + uint32_t Alignment; + uint32_t Flags; + SmallVector<char, 4> Data; +}; + +// A wasm import to be written into the import section. +struct WasmImport { + StringRef ModuleName; + StringRef FieldName; + unsigned Kind; + int32_t Type; +}; + +// A wasm function to be written into the function section. +struct WasmFunction { + int32_t Type; + const MCSymbolWasm *Sym; +}; + +// A wasm export to be written into the export section. +struct WasmExport { + StringRef FieldName; + unsigned Kind; + uint32_t Index; +}; + +// A wasm global to be written into the global section. +struct WasmGlobal { + wasm::ValType Type; + bool IsMutable; + bool HasImport; + uint64_t InitialValue; + uint32_t ImportIndex; +}; + +// Information about a single relocation. +struct WasmRelocationEntry { + uint64_t Offset; // Where is the relocation. + const MCSymbolWasm *Symbol; // The symbol to relocate with. + int64_t Addend; // A value to add to the symbol. + unsigned Type; // The type of the relocation. + const MCSectionWasm *FixupSection;// The section the relocation is targeting. + + WasmRelocationEntry(uint64_t Offset, const MCSymbolWasm *Symbol, + int64_t Addend, unsigned Type, + const MCSectionWasm *FixupSection) + : Offset(Offset), Symbol(Symbol), Addend(Addend), Type(Type), + FixupSection(FixupSection) {} + + bool hasAddend() const { + switch (Type) { + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + return true; + default: + return false; + } + } + + void print(raw_ostream &Out) const { + Out << "Off=" << Offset << ", Sym=" << *Symbol << ", Addend=" << Addend + << ", Type=" << Type + << ", FixupSection=" << FixupSection->getSectionName(); + } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(dbgs()); } +#endif +}; + +#if !defined(NDEBUG) +raw_ostream &operator<<(raw_ostream &OS, const WasmRelocationEntry &Rel) { + Rel.print(OS); + return OS; +} +#endif + +class WasmObjectWriter : public MCObjectWriter { + /// Helper struct for containing some precomputed information on symbols. + struct WasmSymbolData { + const MCSymbolWasm *Symbol; + StringRef Name; + + // Support lexicographic sorting. + bool operator<(const WasmSymbolData &RHS) const { return Name < RHS.Name; } + }; + + /// The target specific Wasm writer instance. + std::unique_ptr<MCWasmObjectTargetWriter> TargetObjectWriter; + + // Relocations for fixing up references in the code section. + std::vector<WasmRelocationEntry> CodeRelocations; + + // Relocations for fixing up references in the data section. + std::vector<WasmRelocationEntry> DataRelocations; + + // Index values to use for fixing up call_indirect type indices. + // Maps function symbols to the index of the type of the function + DenseMap<const MCSymbolWasm *, uint32_t> TypeIndices; + // Maps function symbols to the table element index space. Used + // for TABLE_INDEX relocation types (i.e. address taken functions). + DenseMap<const MCSymbolWasm *, uint32_t> IndirectSymbolIndices; + // Maps function/global symbols to the function/global index space. + DenseMap<const MCSymbolWasm *, uint32_t> SymbolIndices; + + DenseMap<WasmFunctionType, int32_t, WasmFunctionTypeDenseMapInfo> + FunctionTypeIndices; + SmallVector<WasmFunctionType, 4> FunctionTypes; + SmallVector<WasmGlobal, 4> Globals; + unsigned NumGlobalImports = 0; + + // TargetObjectWriter wrappers. + bool is64Bit() const { return TargetObjectWriter->is64Bit(); } + unsigned getRelocType(const MCValue &Target, const MCFixup &Fixup) const { + return TargetObjectWriter->getRelocType(Target, Fixup); + } + + void startSection(SectionBookkeeping &Section, unsigned SectionId, + const char *Name = nullptr); + void endSection(SectionBookkeeping &Section); + +public: + WasmObjectWriter(std::unique_ptr<MCWasmObjectTargetWriter> MOTW, + raw_pwrite_stream &OS) + : MCObjectWriter(OS, /*IsLittleEndian=*/true), + TargetObjectWriter(std::move(MOTW)) {} + +private: + ~WasmObjectWriter() override; + + void reset() override { + CodeRelocations.clear(); + DataRelocations.clear(); + TypeIndices.clear(); + SymbolIndices.clear(); + IndirectSymbolIndices.clear(); + FunctionTypeIndices.clear(); + FunctionTypes.clear(); + Globals.clear(); + MCObjectWriter::reset(); + NumGlobalImports = 0; + } + + void writeHeader(const MCAssembler &Asm); + + void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout, + const MCFragment *Fragment, const MCFixup &Fixup, + MCValue Target, uint64_t &FixedValue) override; + + void executePostLayoutBinding(MCAssembler &Asm, + const MCAsmLayout &Layout) override; + + void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override; + + void writeString(const StringRef Str) { + encodeULEB128(Str.size(), getStream()); + writeBytes(Str); + } + + void writeValueType(wasm::ValType Ty) { + encodeSLEB128(int32_t(Ty), getStream()); + } + + void writeTypeSection(ArrayRef<WasmFunctionType> FunctionTypes); + void writeImportSection(ArrayRef<WasmImport> Imports); + void writeFunctionSection(ArrayRef<WasmFunction> Functions); + void writeTableSection(uint32_t NumElements); + void writeMemorySection(uint32_t DataSize); + void writeGlobalSection(); + void writeExportSection(ArrayRef<WasmExport> Exports); + void writeElemSection(ArrayRef<uint32_t> TableElems); + void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout, + ArrayRef<WasmFunction> Functions); + void writeDataSection(ArrayRef<WasmDataSegment> Segments); + void writeNameSection(ArrayRef<WasmFunction> Functions, + ArrayRef<WasmImport> Imports, + uint32_t NumFuncImports); + void writeCodeRelocSection(); + void writeDataRelocSection(); + void writeLinkingMetaDataSection( + ArrayRef<WasmDataSegment> Segments, uint32_t DataSize, + SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags, + bool HasStackPointer, uint32_t StackPointerGlobal); + + uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); + void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations, + uint64_t ContentsOffset); + + void writeRelocations(ArrayRef<WasmRelocationEntry> Relocations); + uint32_t getRelocationIndexValue(const WasmRelocationEntry &RelEntry); + uint32_t getFunctionType(const MCSymbolWasm& Symbol); + uint32_t registerFunctionType(const MCSymbolWasm& Symbol); +}; + +} // end anonymous namespace + +WasmObjectWriter::~WasmObjectWriter() {} + +// Write out a section header and a patchable section size field. +void WasmObjectWriter::startSection(SectionBookkeeping &Section, + unsigned SectionId, + const char *Name) { + assert((Name != nullptr) == (SectionId == wasm::WASM_SEC_CUSTOM) && + "Only custom sections can have names"); + + DEBUG(dbgs() << "startSection " << SectionId << ": " << Name << "\n"); + encodeULEB128(SectionId, getStream()); + + Section.SizeOffset = getStream().tell(); + + // The section size. We don't know the size yet, so reserve enough space + // for any 32-bit value; we'll patch it later. + encodeULEB128(UINT32_MAX, getStream()); + + // The position where the section starts, for measuring its size. + Section.ContentsOffset = getStream().tell(); + + // Custom sections in wasm also have a string identifier. + if (SectionId == wasm::WASM_SEC_CUSTOM) { + assert(Name); + writeString(StringRef(Name)); + } +} + +// Now that the section is complete and we know how big it is, patch up the +// section size field at the start of the section. +void WasmObjectWriter::endSection(SectionBookkeeping &Section) { + uint64_t Size = getStream().tell() - Section.ContentsOffset; + if (uint32_t(Size) != Size) + report_fatal_error("section size does not fit in a uint32_t"); + + DEBUG(dbgs() << "endSection size=" << Size << "\n"); + + // Write the final section size to the payload_len field, which follows + // the section id byte. + uint8_t Buffer[16]; + unsigned SizeLen = encodeULEB128(Size, Buffer, 5); + assert(SizeLen == 5); + getStream().pwrite((char *)Buffer, SizeLen, Section.SizeOffset); +} + +// Emit the Wasm header. +void WasmObjectWriter::writeHeader(const MCAssembler &Asm) { + writeBytes(StringRef(wasm::WasmMagic, sizeof(wasm::WasmMagic))); + writeLE32(wasm::WasmVersion); +} + +void WasmObjectWriter::executePostLayoutBinding(MCAssembler &Asm, + const MCAsmLayout &Layout) { +} + +void WasmObjectWriter::recordRelocation(MCAssembler &Asm, + const MCAsmLayout &Layout, + const MCFragment *Fragment, + const MCFixup &Fixup, MCValue Target, + uint64_t &FixedValue) { + MCAsmBackend &Backend = Asm.getBackend(); + bool IsPCRel = Backend.getFixupKindInfo(Fixup.getKind()).Flags & + MCFixupKindInfo::FKF_IsPCRel; + const auto &FixupSection = cast<MCSectionWasm>(*Fragment->getParent()); + uint64_t C = Target.getConstant(); + uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); + MCContext &Ctx = Asm.getContext(); + + if (const MCSymbolRefExpr *RefB = Target.getSymB()) { + assert(RefB->getKind() == MCSymbolRefExpr::VK_None && + "Should not have constructed this"); + + // Let A, B and C being the components of Target and R be the location of + // the fixup. If the fixup is not pcrel, we want to compute (A - B + C). + // If it is pcrel, we want to compute (A - B + C - R). + + // In general, Wasm has no relocations for -B. It can only represent (A + C) + // or (A + C - R). If B = R + K and the relocation is not pcrel, we can + // replace B to implement it: (A - R - K + C) + if (IsPCRel) { + Ctx.reportError( + Fixup.getLoc(), + "No relocation available to represent this relative expression"); + return; + } + + const auto &SymB = cast<MCSymbolWasm>(RefB->getSymbol()); + + if (SymB.isUndefined()) { + Ctx.reportError(Fixup.getLoc(), + Twine("symbol '") + SymB.getName() + + "' can not be undefined in a subtraction expression"); + return; + } + + assert(!SymB.isAbsolute() && "Should have been folded"); + const MCSection &SecB = SymB.getSection(); + if (&SecB != &FixupSection) { + Ctx.reportError(Fixup.getLoc(), + "Cannot represent a difference across sections"); + return; + } + + uint64_t SymBOffset = Layout.getSymbolOffset(SymB); + uint64_t K = SymBOffset - FixupOffset; + IsPCRel = true; + C -= K; + } + + // We either rejected the fixup or folded B into C at this point. + const MCSymbolRefExpr *RefA = Target.getSymA(); + const auto *SymA = RefA ? cast<MCSymbolWasm>(&RefA->getSymbol()) : nullptr; + + if (SymA && SymA->isVariable()) { + const MCExpr *Expr = SymA->getVariableValue(); + const auto *Inner = cast<MCSymbolRefExpr>(Expr); + if (Inner->getKind() == MCSymbolRefExpr::VK_WEAKREF) + llvm_unreachable("weakref used in reloc not yet implemented"); + } + + // Put any constant offset in an addend. Offsets can be negative, and + // LLVM expects wrapping, in contrast to wasm's immediates which can't + // be negative and don't wrap. + FixedValue = 0; + + if (SymA) + SymA->setUsedInReloc(); + + assert(!IsPCRel); + assert(SymA); + + unsigned Type = getRelocType(Target, Fixup); + + WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); + DEBUG(dbgs() << "WasmReloc: " << Rec << "\n"); + + if (FixupSection.isWasmData()) + DataRelocations.push_back(Rec); + else if (FixupSection.getKind().isText()) + CodeRelocations.push_back(Rec); + else if (!FixupSection.getKind().isMetadata()) + // TODO(sbc): Add support for debug sections. + llvm_unreachable("unexpected section type"); +} + +// Write X as an (unsigned) LEB value at offset Offset in Stream, padded +// to allow patching. +static void +WritePatchableLEB(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { + uint8_t Buffer[5]; + unsigned SizeLen = encodeULEB128(X, Buffer, 5); + assert(SizeLen == 5); + Stream.pwrite((char *)Buffer, SizeLen, Offset); +} + +// Write X as an signed LEB value at offset Offset in Stream, padded +// to allow patching. +static void +WritePatchableSLEB(raw_pwrite_stream &Stream, int32_t X, uint64_t Offset) { + uint8_t Buffer[5]; + unsigned SizeLen = encodeSLEB128(X, Buffer, 5); + assert(SizeLen == 5); + Stream.pwrite((char *)Buffer, SizeLen, Offset); +} + +// Write X as a plain integer value at offset Offset in Stream. +static void WriteI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { + uint8_t Buffer[4]; + support::endian::write32le(Buffer, X); + Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); +} + +static const MCSymbolWasm* ResolveSymbol(const MCSymbolWasm& Symbol) { + if (Symbol.isVariable()) { + const MCExpr *Expr = Symbol.getVariableValue(); + auto *Inner = cast<MCSymbolRefExpr>(Expr); + return cast<MCSymbolWasm>(&Inner->getSymbol()); + } + return &Symbol; +} + +// Compute a value to write into the code at the location covered +// by RelEntry. This value isn't used by the static linker, since +// we have addends; it just serves to make the code more readable +// and to make standalone wasm modules directly usable. +uint32_t +WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) { + const MCSymbolWasm *Sym = ResolveSymbol(*RelEntry.Symbol); + + // For undefined symbols, use a hopefully invalid value. + if (!Sym->isDefined(/*SetUsed=*/false)) + return UINT32_MAX; + + uint32_t GlobalIndex = SymbolIndices[Sym]; + const WasmGlobal& Global = Globals[GlobalIndex - NumGlobalImports]; + uint64_t Address = Global.InitialValue + RelEntry.Addend; + + // Ignore overflow. LLVM allows address arithmetic to silently wrap. + uint32_t Value = Address; + + return Value; +} + +static void addData(SmallVectorImpl<char> &DataBytes, + MCSectionWasm &DataSection) { + DEBUG(errs() << "addData: " << DataSection.getSectionName() << "\n"); + + DataBytes.resize(alignTo(DataBytes.size(), DataSection.getAlignment())); + + size_t LastFragmentSize = 0; + for (const MCFragment &Frag : DataSection) { + if (Frag.hasInstructions()) + report_fatal_error("only data supported in data sections"); + + if (auto *Align = dyn_cast<MCAlignFragment>(&Frag)) { + if (Align->getValueSize() != 1) + report_fatal_error("only byte values supported for alignment"); + // If nops are requested, use zeros, as this is the data section. + uint8_t Value = Align->hasEmitNops() ? 0 : Align->getValue(); + uint64_t Size = std::min<uint64_t>(alignTo(DataBytes.size(), + Align->getAlignment()), + DataBytes.size() + + Align->getMaxBytesToEmit()); + DataBytes.resize(Size, Value); + } else if (auto *Fill = dyn_cast<MCFillFragment>(&Frag)) { + DataBytes.insert(DataBytes.end(), Fill->getSize(), Fill->getValue()); + } else { + const auto &DataFrag = cast<MCDataFragment>(Frag); + const SmallVectorImpl<char> &Contents = DataFrag.getContents(); + + DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); + LastFragmentSize = Contents.size(); + } + } + + // Don't allow empty segments, or segments that end with zero-sized + // fragment, otherwise the linker cannot map symbols to a unique + // data segment. This can be triggered by zero-sized structs + // See: test/MC/WebAssembly/bss.ll + if (LastFragmentSize == 0) + DataBytes.resize(DataBytes.size() + 1); + DEBUG(dbgs() << "addData -> " << DataBytes.size() << "\n"); +} + +uint32_t WasmObjectWriter::getRelocationIndexValue( + const WasmRelocationEntry &RelEntry) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + if (!IndirectSymbolIndices.count(RelEntry.Symbol)) + report_fatal_error("symbol not found table index space: " + + RelEntry.Symbol->getName()); + return IndirectSymbolIndices[RelEntry.Symbol]; + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + if (!SymbolIndices.count(RelEntry.Symbol)) + report_fatal_error("symbol not found function/global index space: " + + RelEntry.Symbol->getName()); + return SymbolIndices[RelEntry.Symbol]; + case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB: + if (!TypeIndices.count(RelEntry.Symbol)) + report_fatal_error("symbol not found in type index space: " + + RelEntry.Symbol->getName()); + return TypeIndices[RelEntry.Symbol]; + default: + llvm_unreachable("invalid relocation type"); + } +} + +// Apply the portions of the relocation records that we can handle ourselves +// directly. +void WasmObjectWriter::applyRelocations( + ArrayRef<WasmRelocationEntry> Relocations, uint64_t ContentsOffset) { + raw_pwrite_stream &Stream = getStream(); + for (const WasmRelocationEntry &RelEntry : Relocations) { + uint64_t Offset = ContentsOffset + + RelEntry.FixupSection->getSectionOffset() + + RelEntry.Offset; + + DEBUG(dbgs() << "applyRelocation: " << RelEntry << "\n"); + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: { + uint32_t Index = getRelocationIndexValue(RelEntry); + WritePatchableSLEB(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: { + uint32_t Index = getRelocationIndexValue(RelEntry); + WriteI32(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: { + uint32_t Value = getProvisionalValue(RelEntry); + WritePatchableSLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: { + uint32_t Value = getProvisionalValue(RelEntry); + WritePatchableLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: { + uint32_t Value = getProvisionalValue(RelEntry); + WriteI32(Stream, Value, Offset); + break; + } + default: + llvm_unreachable("invalid relocation type"); + } + } +} + +// Write out the portions of the relocation records that the linker will +// need to handle. +void WasmObjectWriter::writeRelocations( + ArrayRef<WasmRelocationEntry> Relocations) { + raw_pwrite_stream &Stream = getStream(); + for (const WasmRelocationEntry& RelEntry : Relocations) { + + uint64_t Offset = RelEntry.Offset + + RelEntry.FixupSection->getSectionOffset(); + uint32_t Index = getRelocationIndexValue(RelEntry); + + encodeULEB128(RelEntry.Type, Stream); + encodeULEB128(Offset, Stream); + encodeULEB128(Index, Stream); + if (RelEntry.hasAddend()) + encodeSLEB128(RelEntry.Addend, Stream); + } +} + +void WasmObjectWriter::writeTypeSection( + ArrayRef<WasmFunctionType> FunctionTypes) { + if (FunctionTypes.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_TYPE); + + encodeULEB128(FunctionTypes.size(), getStream()); + + for (const WasmFunctionType &FuncTy : FunctionTypes) { + encodeSLEB128(wasm::WASM_TYPE_FUNC, getStream()); + encodeULEB128(FuncTy.Params.size(), getStream()); + for (wasm::ValType Ty : FuncTy.Params) + writeValueType(Ty); + encodeULEB128(FuncTy.Returns.size(), getStream()); + for (wasm::ValType Ty : FuncTy.Returns) + writeValueType(Ty); + } + + endSection(Section); +} + +void WasmObjectWriter::writeImportSection(ArrayRef<WasmImport> Imports) { + if (Imports.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_IMPORT); + + encodeULEB128(Imports.size(), getStream()); + for (const WasmImport &Import : Imports) { + writeString(Import.ModuleName); + writeString(Import.FieldName); + + encodeULEB128(Import.Kind, getStream()); + + switch (Import.Kind) { + case wasm::WASM_EXTERNAL_FUNCTION: + encodeULEB128(Import.Type, getStream()); + break; + case wasm::WASM_EXTERNAL_GLOBAL: + encodeSLEB128(int32_t(Import.Type), getStream()); + encodeULEB128(0, getStream()); // mutability + break; + default: + llvm_unreachable("unsupported import kind"); + } + } + + endSection(Section); +} + +void WasmObjectWriter::writeFunctionSection(ArrayRef<WasmFunction> Functions) { + if (Functions.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_FUNCTION); + + encodeULEB128(Functions.size(), getStream()); + for (const WasmFunction &Func : Functions) + encodeULEB128(Func.Type, getStream()); + + endSection(Section); +} + +void WasmObjectWriter::writeTableSection(uint32_t NumElements) { + // For now, always emit the table section, since indirect calls are not + // valid without it. In the future, we could perhaps be more clever and omit + // it if there are no indirect calls. + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_TABLE); + + encodeULEB128(1, getStream()); // The number of tables. + // Fixed to 1 for now. + encodeSLEB128(wasm::WASM_TYPE_ANYFUNC, getStream()); // Type of table + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumElements, getStream()); // initial + + endSection(Section); +} + +void WasmObjectWriter::writeMemorySection(uint32_t DataSize) { + // For now, always emit the memory section, since loads and stores are not + // valid without it. In the future, we could perhaps be more clever and omit + // it if there are no loads or stores. + SectionBookkeeping Section; + uint32_t NumPages = (DataSize + wasm::WasmPageSize - 1) / wasm::WasmPageSize; + + startSection(Section, wasm::WASM_SEC_MEMORY); + encodeULEB128(1, getStream()); // number of memory spaces + + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumPages, getStream()); // initial + + endSection(Section); +} + +void WasmObjectWriter::writeGlobalSection() { + if (Globals.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_GLOBAL); + + encodeULEB128(Globals.size(), getStream()); + for (const WasmGlobal &Global : Globals) { + writeValueType(Global.Type); + write8(Global.IsMutable); + + if (Global.HasImport) { + assert(Global.InitialValue == 0); + write8(wasm::WASM_OPCODE_GET_GLOBAL); + encodeULEB128(Global.ImportIndex, getStream()); + } else { + assert(Global.ImportIndex == 0); + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Global.InitialValue, getStream()); // offset + } + write8(wasm::WASM_OPCODE_END); + } + + endSection(Section); +} + +void WasmObjectWriter::writeExportSection(ArrayRef<WasmExport> Exports) { + if (Exports.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_EXPORT); + + encodeULEB128(Exports.size(), getStream()); + for (const WasmExport &Export : Exports) { + writeString(Export.FieldName); + encodeSLEB128(Export.Kind, getStream()); + encodeULEB128(Export.Index, getStream()); + } + + endSection(Section); +} + +void WasmObjectWriter::writeElemSection(ArrayRef<uint32_t> TableElems) { + if (TableElems.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_ELEM); + + encodeULEB128(1, getStream()); // number of "segments" + encodeULEB128(0, getStream()); // the table index + + // init expr for starting offset + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(0, getStream()); + write8(wasm::WASM_OPCODE_END); + + encodeULEB128(TableElems.size(), getStream()); + for (uint32_t Elem : TableElems) + encodeULEB128(Elem, getStream()); + + endSection(Section); +} + +void WasmObjectWriter::writeCodeSection(const MCAssembler &Asm, + const MCAsmLayout &Layout, + ArrayRef<WasmFunction> Functions) { + if (Functions.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_CODE); + + encodeULEB128(Functions.size(), getStream()); + + for (const WasmFunction &Func : Functions) { + auto &FuncSection = static_cast<MCSectionWasm &>(Func.Sym->getSection()); + + int64_t Size = 0; + if (!Func.Sym->getSize()->evaluateAsAbsolute(Size, Layout)) + report_fatal_error(".size expression must be evaluatable"); + + encodeULEB128(Size, getStream()); + FuncSection.setSectionOffset(getStream().tell() - Section.ContentsOffset); + Asm.writeSectionData(&FuncSection, Layout); + } + + // Apply fixups. + applyRelocations(CodeRelocations, Section.ContentsOffset); + + endSection(Section); +} + +void WasmObjectWriter::writeDataSection(ArrayRef<WasmDataSegment> Segments) { + if (Segments.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_DATA); + + encodeULEB128(Segments.size(), getStream()); // count + + for (const WasmDataSegment & Segment : Segments) { + encodeULEB128(0, getStream()); // memory index + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Segment.Offset, getStream()); // offset + write8(wasm::WASM_OPCODE_END); + encodeULEB128(Segment.Data.size(), getStream()); // size + Segment.Section->setSectionOffset(getStream().tell() - Section.ContentsOffset); + writeBytes(Segment.Data); // data + } + + // Apply fixups. + applyRelocations(DataRelocations, Section.ContentsOffset); + + endSection(Section); +} + +void WasmObjectWriter::writeNameSection( + ArrayRef<WasmFunction> Functions, + ArrayRef<WasmImport> Imports, + unsigned NumFuncImports) { + uint32_t TotalFunctions = NumFuncImports + Functions.size(); + if (TotalFunctions == 0) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_CUSTOM, "name"); + SectionBookkeeping SubSection; + startSection(SubSection, wasm::WASM_NAMES_FUNCTION); + + encodeULEB128(TotalFunctions, getStream()); + uint32_t Index = 0; + for (const WasmImport &Import : Imports) { + if (Import.Kind == wasm::WASM_EXTERNAL_FUNCTION) { + encodeULEB128(Index, getStream()); + writeString(Import.FieldName); + ++Index; + } + } + for (const WasmFunction &Func : Functions) { + encodeULEB128(Index, getStream()); + writeString(Func.Sym->getName()); + ++Index; + } + + endSection(SubSection); + endSection(Section); +} + +void WasmObjectWriter::writeCodeRelocSection() { + // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md + // for descriptions of the reloc sections. + + if (CodeRelocations.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.CODE"); + + encodeULEB128(wasm::WASM_SEC_CODE, getStream()); + encodeULEB128(CodeRelocations.size(), getStream()); + + writeRelocations(CodeRelocations); + + endSection(Section); +} + +void WasmObjectWriter::writeDataRelocSection() { + // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md + // for descriptions of the reloc sections. + + if (DataRelocations.empty()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.DATA"); + + encodeULEB128(wasm::WASM_SEC_DATA, getStream()); + encodeULEB128(DataRelocations.size(), getStream()); + + writeRelocations(DataRelocations); + + endSection(Section); +} + +void WasmObjectWriter::writeLinkingMetaDataSection( + ArrayRef<WasmDataSegment> Segments, uint32_t DataSize, + SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags, + bool HasStackPointer, uint32_t StackPointerGlobal) { + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); + SectionBookkeeping SubSection; + + if (HasStackPointer) { + startSection(SubSection, wasm::WASM_STACK_POINTER); + encodeULEB128(StackPointerGlobal, getStream()); // id + endSection(SubSection); + } + + if (SymbolFlags.size() != 0) { + startSection(SubSection, wasm::WASM_SYMBOL_INFO); + encodeULEB128(SymbolFlags.size(), getStream()); + for (auto Pair: SymbolFlags) { + writeString(Pair.first); + encodeULEB128(Pair.second, getStream()); + } + endSection(SubSection); + } + + if (DataSize > 0) { + startSection(SubSection, wasm::WASM_DATA_SIZE); + encodeULEB128(DataSize, getStream()); + endSection(SubSection); + } + + if (Segments.size()) { + startSection(SubSection, wasm::WASM_SEGMENT_INFO); + encodeULEB128(Segments.size(), getStream()); + for (const WasmDataSegment &Segment : Segments) { + writeString(Segment.Name); + encodeULEB128(Segment.Alignment, getStream()); + encodeULEB128(Segment.Flags, getStream()); + } + endSection(SubSection); + } + + endSection(Section); +} + +uint32_t WasmObjectWriter::getFunctionType(const MCSymbolWasm& Symbol) { + assert(Symbol.isFunction()); + assert(TypeIndices.count(&Symbol)); + return TypeIndices[&Symbol]; +} + +uint32_t WasmObjectWriter::registerFunctionType(const MCSymbolWasm& Symbol) { + assert(Symbol.isFunction()); + + WasmFunctionType F; + const MCSymbolWasm* ResolvedSym = ResolveSymbol(Symbol); + F.Returns = ResolvedSym->getReturns(); + F.Params = ResolvedSym->getParams(); + + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + TypeIndices[&Symbol] = Pair.first->second; + + DEBUG(dbgs() << "registerFunctionType: " << Symbol << " new:" << Pair.second << "\n"); + DEBUG(dbgs() << " -> type index: " << Pair.first->second << "\n"); + return Pair.first->second; +} + +void WasmObjectWriter::writeObject(MCAssembler &Asm, + const MCAsmLayout &Layout) { + DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); + MCContext &Ctx = Asm.getContext(); + wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32; + + // Collect information from the available symbols. + SmallVector<WasmFunction, 4> Functions; + SmallVector<uint32_t, 4> TableElems; + SmallVector<WasmImport, 4> Imports; + SmallVector<WasmExport, 4> Exports; + SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags; + SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken; + unsigned NumFuncImports = 0; + SmallVector<WasmDataSegment, 4> DataSegments; + uint32_t StackPointerGlobal = 0; + uint32_t DataSize = 0; + bool HasStackPointer = false; + + // Populate the IsAddressTaken set. + for (const WasmRelocationEntry &RelEntry : CodeRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + for (const WasmRelocationEntry &RelEntry : DataRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + + // Populate FunctionTypeIndices and Imports. + for (const MCSymbol &S : Asm.symbols()) { + const auto &WS = static_cast<const MCSymbolWasm &>(S); + + if (WS.isTemporary()) + continue; + + if (WS.isFunction()) + registerFunctionType(WS); + + // If the symbol is not defined in this translation unit, import it. + if (!WS.isDefined(/*SetUsed=*/false)) { + WasmImport Import; + Import.ModuleName = WS.getModuleName(); + Import.FieldName = WS.getName(); + + if (WS.isFunction()) { + Import.Kind = wasm::WASM_EXTERNAL_FUNCTION; + Import.Type = getFunctionType(WS); + SymbolIndices[&WS] = NumFuncImports; + ++NumFuncImports; + } else { + Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Import.Type = int32_t(PtrType); + SymbolIndices[&WS] = NumGlobalImports; + ++NumGlobalImports; + } + + Imports.push_back(Import); + } + } + + // In the special .global_variables section, we've encoded global + // variables used by the function. Translate them into the Globals + // list. + MCSectionWasm *GlobalVars = + Ctx.getWasmSection(".global_variables", SectionKind::getMetadata()); + if (!GlobalVars->getFragmentList().empty()) { + if (GlobalVars->getFragmentList().size() != 1) + report_fatal_error("only one .global_variables fragment supported"); + const MCFragment &Frag = *GlobalVars->begin(); + if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data) + report_fatal_error("only data supported in .global_variables"); + const auto &DataFrag = cast<MCDataFragment>(Frag); + if (!DataFrag.getFixups().empty()) + report_fatal_error("fixups not supported in .global_variables"); + const SmallVectorImpl<char> &Contents = DataFrag.getContents(); + for (const uint8_t *p = (const uint8_t *)Contents.data(), + *end = (const uint8_t *)Contents.data() + Contents.size(); + p != end; ) { + WasmGlobal G; + if (end - p < 3) + report_fatal_error("truncated global variable encoding"); + G.Type = wasm::ValType(int8_t(*p++)); + G.IsMutable = bool(*p++); + G.HasImport = bool(*p++); + if (G.HasImport) { + G.InitialValue = 0; + + WasmImport Import; + Import.ModuleName = (const char *)p; + const uint8_t *nul = (const uint8_t *)memchr(p, '\0', end - p); + if (!nul) + report_fatal_error("global module name must be nul-terminated"); + p = nul + 1; + nul = (const uint8_t *)memchr(p, '\0', end - p); + if (!nul) + report_fatal_error("global base name must be nul-terminated"); + Import.FieldName = (const char *)p; + p = nul + 1; + + Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Import.Type = int32_t(G.Type); + + G.ImportIndex = NumGlobalImports; + ++NumGlobalImports; + + Imports.push_back(Import); + } else { + unsigned n; + G.InitialValue = decodeSLEB128(p, &n); + G.ImportIndex = 0; + if ((ptrdiff_t)n > end - p) + report_fatal_error("global initial value must be valid SLEB128"); + p += n; + } + Globals.push_back(G); + } + } + + // In the special .stack_pointer section, we've encoded the stack pointer + // index. + MCSectionWasm *StackPtr = + Ctx.getWasmSection(".stack_pointer", SectionKind::getMetadata()); + if (!StackPtr->getFragmentList().empty()) { + if (StackPtr->getFragmentList().size() != 1) + report_fatal_error("only one .stack_pointer fragment supported"); + const MCFragment &Frag = *StackPtr->begin(); + if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data) + report_fatal_error("only data supported in .stack_pointer"); + const auto &DataFrag = cast<MCDataFragment>(Frag); + if (!DataFrag.getFixups().empty()) + report_fatal_error("fixups not supported in .stack_pointer"); + const SmallVectorImpl<char> &Contents = DataFrag.getContents(); + if (Contents.size() != 4) + report_fatal_error("only one entry supported in .stack_pointer"); + HasStackPointer = true; + StackPointerGlobal = NumGlobalImports + *(const int32_t *)Contents.data(); + } + + for (MCSection &Sec : Asm) { + auto &Section = static_cast<MCSectionWasm &>(Sec); + if (!Section.isWasmData()) + continue; + + DataSize = alignTo(DataSize, Section.getAlignment()); + DataSegments.emplace_back(); + WasmDataSegment &Segment = DataSegments.back(); + Segment.Name = Section.getSectionName(); + Segment.Offset = DataSize; + Segment.Section = &Section; + addData(Segment.Data, Section); + Segment.Alignment = Section.getAlignment(); + Segment.Flags = 0; + DataSize += Segment.Data.size(); + Section.setMemoryOffset(Segment.Offset); + } + + // Handle regular defined and undefined symbols. + for (const MCSymbol &S : Asm.symbols()) { + // Ignore unnamed temporary symbols, which aren't ever exported, imported, + // or used in relocations. + if (S.isTemporary() && S.getName().empty()) + continue; + + const auto &WS = static_cast<const MCSymbolWasm &>(S); + DEBUG(dbgs() << "MCSymbol: '" << S << "'" + << " isDefined=" << S.isDefined() << " isExternal=" + << S.isExternal() << " isTemporary=" << S.isTemporary() + << " isFunction=" << WS.isFunction() + << " isWeak=" << WS.isWeak() + << " isVariable=" << WS.isVariable() << "\n"); + + if (WS.isWeak()) + SymbolFlags.emplace_back(WS.getName(), wasm::WASM_SYMBOL_BINDING_WEAK); + + if (WS.isVariable()) + continue; + + unsigned Index; + + if (WS.isFunction()) { + if (WS.isDefined(/*SetUsed=*/false)) { + if (WS.getOffset() != 0) + report_fatal_error( + "function sections must contain one function each"); + + if (WS.getSize() == 0) + report_fatal_error( + "function symbols must have a size set with .size"); + + // A definition. Take the next available index. + Index = NumFuncImports + Functions.size(); + + // Prepare the function. + WasmFunction Func; + Func.Type = getFunctionType(WS); + Func.Sym = &WS; + SymbolIndices[&WS] = Index; + Functions.push_back(Func); + } else { + // An import; the index was assigned above. + Index = SymbolIndices.find(&WS)->second; + } + + DEBUG(dbgs() << " -> function index: " << Index << "\n"); + + // If needed, prepare the function to be called indirectly. + if (IsAddressTaken.count(&WS) != 0) { + IndirectSymbolIndices[&WS] = TableElems.size(); + DEBUG(dbgs() << " -> adding to table: " << TableElems.size() << "\n"); + TableElems.push_back(Index); + } + } else { + if (WS.isTemporary() && !WS.getSize()) + continue; + + if (!WS.isDefined(/*SetUsed=*/false)) + continue; + + if (!WS.getSize()) + report_fatal_error("data symbols must have a size set with .size: " + + WS.getName()); + + int64_t Size = 0; + if (!WS.getSize()->evaluateAsAbsolute(Size, Layout)) + report_fatal_error(".size expression must be evaluatable"); + + // For each global, prepare a corresponding wasm global holding its + // address. For externals these will also be named exports. + Index = NumGlobalImports + Globals.size(); + auto &DataSection = static_cast<MCSectionWasm &>(WS.getSection()); + + WasmGlobal Global; + Global.Type = PtrType; + Global.IsMutable = false; + Global.HasImport = false; + Global.InitialValue = DataSection.getMemoryOffset() + Layout.getSymbolOffset(WS); + Global.ImportIndex = 0; + SymbolIndices[&WS] = Index; + DEBUG(dbgs() << " -> global index: " << Index << "\n"); + Globals.push_back(Global); + } + + // If the symbol is visible outside this translation unit, export it. + if (WS.isDefined(/*SetUsed=*/false)) { + WasmExport Export; + Export.FieldName = WS.getName(); + Export.Index = Index; + if (WS.isFunction()) + Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; + else + Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; + DEBUG(dbgs() << " -> export " << Exports.size() << "\n"); + Exports.push_back(Export); + if (!WS.isExternal()) + SymbolFlags.emplace_back(WS.getName(), wasm::WASM_SYMBOL_BINDING_LOCAL); + } + } + + // Handle weak aliases. We need to process these in a separate pass because + // we need to have processed the target of the alias before the alias itself + // and the symbols are not necessarily ordered in this way. + for (const MCSymbol &S : Asm.symbols()) { + if (!S.isVariable()) + continue; + + assert(S.isDefined(/*SetUsed=*/false)); + + // Find the target symbol of this weak alias and export that index + const auto &WS = static_cast<const MCSymbolWasm &>(S); + const MCSymbolWasm *ResolvedSym = ResolveSymbol(WS); + DEBUG(dbgs() << WS.getName() << ": weak alias of '" << *ResolvedSym << "'\n"); + assert(SymbolIndices.count(ResolvedSym) > 0); + uint32_t Index = SymbolIndices.find(ResolvedSym)->second; + DEBUG(dbgs() << " -> index:" << Index << "\n"); + + SymbolIndices[&WS] = Index; + WasmExport Export; + Export.FieldName = WS.getName(); + Export.Index = Index; + if (WS.isFunction()) + Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; + else + Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; + DEBUG(dbgs() << " -> export " << Exports.size() << "\n"); + Exports.push_back(Export); + + if (!WS.isExternal()) + SymbolFlags.emplace_back(WS.getName(), wasm::WASM_SYMBOL_BINDING_LOCAL); + } + + // Add types for indirect function calls. + for (const WasmRelocationEntry &Fixup : CodeRelocations) { + if (Fixup.Type != wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB) + continue; + + registerFunctionType(*Fixup.Symbol); + } + + // Write out the Wasm header. + writeHeader(Asm); + + writeTypeSection(FunctionTypes); + writeImportSection(Imports); + writeFunctionSection(Functions); + writeTableSection(TableElems.size()); + writeMemorySection(DataSize); + writeGlobalSection(); + writeExportSection(Exports); + // TODO: Start Section + writeElemSection(TableElems); + writeCodeSection(Asm, Layout, Functions); + writeDataSection(DataSegments); + writeNameSection(Functions, Imports, NumFuncImports); + writeCodeRelocSection(); + writeDataRelocSection(); + writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, + HasStackPointer, StackPointerGlobal); + + // TODO: Translate the .comment section to the output. + // TODO: Translate debug sections to the output. +} + +std::unique_ptr<MCObjectWriter> +llvm::createWasmObjectWriter(std::unique_ptr<MCWasmObjectTargetWriter> MOTW, + raw_pwrite_stream &OS) { + // FIXME: Can't use make_unique<WasmObjectWriter>(...) as WasmObjectWriter's + // destructor is private. Is that necessary? + return std::unique_ptr<MCObjectWriter>( + new WasmObjectWriter(std::move(MOTW), OS)); +}