diff options
Diffstat (limited to 'lib/MC/WasmObjectWriter.cpp')
-rw-r--r-- | lib/MC/WasmObjectWriter.cpp | 1149 |
1 files changed, 1149 insertions, 0 deletions
diff --git a/lib/MC/WasmObjectWriter.cpp b/lib/MC/WasmObjectWriter.cpp new file mode 100644 index 000000000000..159cc3b4def2 --- /dev/null +++ b/lib/MC/WasmObjectWriter.cpp @@ -0,0 +1,1149 @@ +//===- 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/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 "llvm/Support/Wasm.h" +#include <vector> + +using namespace llvm; + +#undef DEBUG_TYPE +#define DEBUG_TYPE "reloc-info" + +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; +}; + +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; + + // Fixups for call_indirect type indices. + std::vector<WasmRelocationEntry> TypeIndexFixups; + + // Index values to use for fixing up call_indirect type indices. + std::vector<uint32_t> TypeIndexFixupTypes; + + // TargetObjectWriter wrappers. + bool is64Bit() const { return TargetObjectWriter->is64Bit(); } + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const { + return TargetObjectWriter->getRelocType(Ctx, Target, Fixup, IsPCRel); + } + + void startSection(SectionBookkeeping &Section, unsigned SectionId, + const char *Name = nullptr); + void endSection(SectionBookkeeping &Section); + +public: + WasmObjectWriter(MCWasmObjectTargetWriter *MOTW, raw_pwrite_stream &OS) + : MCObjectWriter(OS, /*IsLittleEndian=*/true), TargetObjectWriter(MOTW) {} + +private: + void reset() override { + MCObjectWriter::reset(); + } + + ~WasmObjectWriter() override; + + void writeHeader(const MCAssembler &Asm); + + void writeValueType(wasm::ValType Ty) { + encodeSLEB128(int32_t(Ty), getStream()); + } + + void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout, + const MCFragment *Fragment, const MCFixup &Fixup, + MCValue Target, bool &IsPCRel, + uint64_t &FixedValue) override; + + void executePostLayoutBinding(MCAssembler &Asm, + const MCAsmLayout &Layout) override; + + void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override; +}; +} // end anonymous namespace + +WasmObjectWriter::~WasmObjectWriter() {} + +// Return the padding size to write a 32-bit value into a 5-byte ULEB128. +static unsigned PaddingFor5ByteULEB128(uint32_t X) { + return X == 0 ? 4 : (4u - (31u - countLeadingZeros(X)) / 7u); +} + +// Return the padding size to write a 32-bit value into a 5-byte SLEB128. +static unsigned PaddingFor5ByteSLEB128(int32_t X) { + return 5 - getSLEB128Size(X); +} + +// 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"); + + 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) { + encodeULEB128(strlen(Name), getStream()); + writeBytes(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"); + + unsigned Padding = PaddingFor5ByteULEB128(Size); + + // 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, Padding); + 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, + bool &IsPCRel, uint64_t &FixedValue) { + MCSectionWasm &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; + + bool ViaWeakRef = false; + if (SymA && SymA->isVariable()) { + const MCExpr *Expr = SymA->getVariableValue(); + if (const auto *Inner = dyn_cast<MCSymbolRefExpr>(Expr)) { + if (Inner->getKind() == MCSymbolRefExpr::VK_WEAKREF) { + SymA = cast<MCSymbolWasm>(&Inner->getSymbol()); + ViaWeakRef = true; + } + } + } + + // 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) { + if (ViaWeakRef) + llvm_unreachable("weakref used in reloc not yet implemented"); + else + SymA->setUsedInReloc(); + } + + if (RefA) { + if (RefA->getKind() == MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX) { + assert(C == 0); + WasmRelocationEntry Rec(FixupOffset, SymA, C, + wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB, + &FixupSection); + TypeIndexFixups.push_back(Rec); + return; + } + } + + unsigned Type = getRelocType(Ctx, Target, Fixup, IsPCRel); + + WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); + + if (FixupSection.hasInstructions()) + CodeRelocations.push_back(Rec); + else + DataRelocations.push_back(Rec); +} + +namespace { + +// 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 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; +}; + +} // end anonymous namespace + +// 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 Padding = PaddingFor5ByteULEB128(X); + unsigned SizeLen = encodeULEB128(X, Buffer, Padding); + 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 Padding = PaddingFor5ByteSLEB128(X); + unsigned SizeLen = encodeSLEB128(X, Buffer, Padding); + 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); +} + +// 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. +static uint32_t ProvisionalValue(const WasmRelocationEntry &RelEntry) { + const MCSymbolWasm *Sym = RelEntry.Symbol; + + // For undefined symbols, use a hopefully invalid value. + if (!Sym->isDefined(false)) + return UINT32_MAX; + + MCSectionWasm &Section = + cast<MCSectionWasm>(RelEntry.Symbol->getSection(false)); + uint64_t Address = Section.getSectionOffset() + RelEntry.Addend; + + // Ignore overflow. LLVM allows address arithmetic to silently wrap. + uint32_t Value = Address; + + return Value; +} + +// Apply the portions of the relocation records that we can handle ourselves +// directly. +static void ApplyRelocations( + ArrayRef<WasmRelocationEntry> Relocations, + raw_pwrite_stream &Stream, + DenseMap<const MCSymbolWasm *, uint32_t> &SymbolIndices, + uint64_t ContentsOffset) +{ + for (const WasmRelocationEntry &RelEntry : Relocations) { + uint64_t Offset = ContentsOffset + + RelEntry.FixupSection->getSectionOffset() + + RelEntry.Offset; + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WritePatchableLEB(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WritePatchableSLEB(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: { + uint32_t Value = ProvisionalValue(RelEntry); + + WritePatchableSLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: { + uint32_t Value = ProvisionalValue(RelEntry); + + WritePatchableLEB(Stream, Value, Offset); + break; + } + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: { + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + assert(RelEntry.Addend == 0); + + WriteI32(Stream, Index, Offset); + break; + } + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: { + uint32_t Value = ProvisionalValue(RelEntry); + + WriteI32(Stream, Value, Offset); + break; + } + default: + break; + } + } +} + +// Write out the portions of the relocation records that the linker will +// need to handle. +static void WriteRelocations( + ArrayRef<WasmRelocationEntry> Relocations, + raw_pwrite_stream &Stream, + DenseMap<const MCSymbolWasm *, uint32_t> &SymbolIndices) +{ + for (const WasmRelocationEntry RelEntry : Relocations) { + encodeULEB128(RelEntry.Type, Stream); + + uint64_t Offset = RelEntry.Offset + + RelEntry.FixupSection->getSectionOffset(); + uint32_t Index = SymbolIndices[RelEntry.Symbol]; + int64_t Addend = RelEntry.Addend; + + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + encodeULEB128(Offset, Stream); + encodeULEB128(Index, Stream); + assert(Addend == 0 && "addends not supported for functions"); + break; + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + encodeULEB128(Offset, Stream); + encodeULEB128(Index, Stream); + encodeSLEB128(Addend, Stream); + break; + default: + llvm_unreachable("unsupported relocation type"); + } + } +} + +// Write out the the type relocation records that the linker will +// need to handle. +static void WriteTypeRelocations( + ArrayRef<WasmRelocationEntry> TypeIndexFixups, + ArrayRef<uint32_t> TypeIndexFixupTypes, + raw_pwrite_stream &Stream) +{ + for (size_t i = 0, e = TypeIndexFixups.size(); i < e; ++i) { + const WasmRelocationEntry &Fixup = TypeIndexFixups[i]; + uint32_t Type = TypeIndexFixupTypes[i]; + + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); + assert(Fixup.Addend == 0); + + uint64_t Offset = Fixup.Offset + + Fixup.FixupSection->getSectionOffset(); + + encodeULEB128(Fixup.Type, Stream); + encodeULEB128(Offset, Stream); + encodeULEB128(Type, Stream); + } +} + +void WasmObjectWriter::writeObject(MCAssembler &Asm, + const MCAsmLayout &Layout) { + MCContext &Ctx = Asm.getContext(); + wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32; + + // Collect information from the available symbols. + DenseMap<WasmFunctionType, int32_t, WasmFunctionTypeDenseMapInfo> + FunctionTypeIndices; + SmallVector<WasmFunctionType, 4> FunctionTypes; + SmallVector<WasmFunction, 4> Functions; + SmallVector<uint32_t, 4> TableElems; + SmallVector<WasmGlobal, 4> Globals; + SmallVector<WasmImport, 4> Imports; + SmallVector<WasmExport, 4> Exports; + DenseMap<const MCSymbolWasm *, uint32_t> SymbolIndices; + SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken; + unsigned NumFuncImports = 0; + unsigned NumGlobalImports = 0; + SmallVector<char, 0> DataBytes; + uint32_t StackPointerGlobal = 0; + bool HasStackPointer = false; + + // Populate the IsAddressTaken set. + for (WasmRelocationEntry RelEntry : CodeRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + for (WasmRelocationEntry RelEntry : DataRelocations) { + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + IsAddressTaken.insert(RelEntry.Symbol); + break; + default: + break; + } + } + + // Populate the Imports set. + for (const MCSymbol &S : Asm.symbols()) { + const auto &WS = static_cast<const MCSymbolWasm &>(S); + int32_t Type; + + if (WS.isFunction()) { + // Prepare the function's type, if we haven't seen it yet. + WasmFunctionType F; + F.Returns = WS.getReturns(); + F.Params = WS.getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + Type = Pair.first->second; + } else { + Type = int32_t(PtrType); + } + + // If the symbol is not defined in this translation unit, import it. + if (!WS.isTemporary() && !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 = Type; + SymbolIndices[&WS] = NumFuncImports; + ++NumFuncImports; + } else { + Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Import.Type = Type; + 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", 0, 0); + 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 MCDataFragment &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", 0, 0); + 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 MCDataFragment &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(); + } + + // Handle defined 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); + unsigned Index; + if (WS.isFunction()) { + // Prepare the function's type, if we haven't seen it yet. + WasmFunctionType F; + F.Returns = WS.getReturns(); + F.Params = WS.getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + int32_t Type = Pair.first->second; + + if (WS.isDefined(/*SetUsed=*/false)) { + // A definition. Take the next available index. + Index = NumFuncImports + Functions.size(); + + // Prepare the function. + WasmFunction Func; + Func.Type = Type; + Func.Sym = &WS; + SymbolIndices[&WS] = Index; + Functions.push_back(Func); + } else { + // An import; the index was assigned above. + Index = SymbolIndices.find(&WS)->second; + } + + // If needed, prepare the function to be called indirectly. + if (IsAddressTaken.count(&WS)) + TableElems.push_back(Index); + } else { + // For now, ignore temporary non-function symbols. + if (S.isTemporary()) + continue; + + if (WS.getOffset() != 0) + report_fatal_error("data sections must contain one variable each"); + if (!WS.getSize()) + report_fatal_error("data symbols must have a size set with .size"); + + int64_t Size = 0; + if (!WS.getSize()->evaluateAsAbsolute(Size, Layout)) + report_fatal_error(".size expression must be evaluatable"); + + if (WS.isDefined(false)) { + MCSectionWasm &DataSection = + static_cast<MCSectionWasm &>(WS.getSection()); + + if (uint64_t(Size) != Layout.getSectionFileSize(&DataSection)) + report_fatal_error("data sections must contain at most one variable"); + + DataBytes.resize(alignTo(DataBytes.size(), DataSection.getAlignment())); + + DataSection.setSectionOffset(DataBytes.size()); + + for (MCSection::iterator I = DataSection.begin(), E = DataSection.end(); + I != E; ++I) { + const MCFragment &Frag = *I; + if (Frag.hasInstructions()) + report_fatal_error("only data supported in data sections"); + + if (const MCAlignFragment *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 (const MCFillFragment *Fill = + dyn_cast<MCFillFragment>(&Frag)) { + DataBytes.insert(DataBytes.end(), Size, Fill->getValue()); + } else { + const MCDataFragment &DataFrag = cast<MCDataFragment>(Frag); + const SmallVectorImpl<char> &Contents = DataFrag.getContents(); + + DataBytes.insert(DataBytes.end(), Contents.begin(), Contents.end()); + } + } + + // For each external global, prepare a corresponding wasm global + // holding its address. + if (WS.isExternal()) { + Index = NumGlobalImports + Globals.size(); + + WasmGlobal Global; + Global.Type = PtrType; + Global.IsMutable = false; + Global.HasImport = false; + Global.InitialValue = DataSection.getSectionOffset(); + Global.ImportIndex = 0; + SymbolIndices[&WS] = Index; + Globals.push_back(Global); + } + } + } + + // If the symbol is visible outside this translation unit, export it. + if (WS.isExternal()) { + assert(WS.isDefined(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; + + Exports.push_back(Export); + } + } + + // Add types for indirect function calls. + for (const WasmRelocationEntry &Fixup : TypeIndexFixups) { + assert(Fixup.Addend == 0); + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); + + WasmFunctionType F; + F.Returns = Fixup.Symbol->getReturns(); + F.Params = Fixup.Symbol->getParams(); + auto Pair = + FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); + if (Pair.second) + FunctionTypes.push_back(F); + + TypeIndexFixupTypes.push_back(Pair.first->second); + } + + // Write out the Wasm header. + writeHeader(Asm); + + SectionBookkeeping Section; + + // === Type Section ========================================================= + if (!FunctionTypes.empty()) { + startSection(Section, wasm::WASM_SEC_TYPE); + + encodeULEB128(FunctionTypes.size(), getStream()); + + for (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); + } + + // === Import Section ======================================================== + if (!Imports.empty()) { + startSection(Section, wasm::WASM_SEC_IMPORT); + + encodeULEB128(Imports.size(), getStream()); + for (const WasmImport &Import : Imports) { + StringRef ModuleName = Import.ModuleName; + encodeULEB128(ModuleName.size(), getStream()); + writeBytes(ModuleName); + + StringRef FieldName = Import.FieldName; + encodeULEB128(FieldName.size(), getStream()); + writeBytes(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); + } + + // === Function Section ====================================================== + if (!Functions.empty()) { + startSection(Section, wasm::WASM_SEC_FUNCTION); + + encodeULEB128(Functions.size(), getStream()); + for (const WasmFunction &Func : Functions) + encodeULEB128(Func.Type, getStream()); + + endSection(Section); + } + + // === Table Section ========================================================= + // 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. + startSection(Section, wasm::WASM_SEC_TABLE); + + // The number of tables, fixed to 1 for now. + encodeULEB128(1, getStream()); + + encodeSLEB128(wasm::WASM_TYPE_ANYFUNC, getStream()); + + encodeULEB128(0, getStream()); // flags + encodeULEB128(TableElems.size(), getStream()); // initial + + endSection(Section); + + // === Memory Section ======================================================== + // 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. + startSection(Section, wasm::WASM_SEC_MEMORY); + + encodeULEB128(1, getStream()); // number of memory spaces + + encodeULEB128(0, getStream()); // flags + encodeULEB128(DataBytes.size(), getStream()); // initial + + endSection(Section); + + // === Global Section ======================================================== + if (!Globals.empty()) { + 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); + } + + // === Export Section ======================================================== + if (!Exports.empty()) { + startSection(Section, wasm::WASM_SEC_EXPORT); + + encodeULEB128(Exports.size(), getStream()); + for (const WasmExport &Export : Exports) { + encodeULEB128(Export.FieldName.size(), getStream()); + writeBytes(Export.FieldName); + + encodeSLEB128(Export.Kind, getStream()); + + encodeULEB128(Export.Index, getStream()); + } + + endSection(Section); + } + +#if 0 // TODO: Start Section + if (HaveStartFunction) { + // === Start Section ========================================================= + startSection(Section, wasm::WASM_SEC_START); + + encodeSLEB128(StartFunction, getStream()); + + endSection(Section); + } +#endif + + // === Elem Section ========================================================== + if (!TableElems.empty()) { + 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); + } + + // === Code Section ========================================================== + if (!Functions.empty()) { + startSection(Section, wasm::WASM_SEC_CODE); + + encodeULEB128(Functions.size(), getStream()); + + for (const WasmFunction &Func : Functions) { + MCSectionWasm &FuncSection = + static_cast<MCSectionWasm &>(Func.Sym->getSection()); + + if (Func.Sym->isVariable()) + report_fatal_error("weak symbols not supported yet"); + + if (Func.Sym->getOffset() != 0) + report_fatal_error("function sections must contain one function each"); + + if (!Func.Sym->getSize()) + report_fatal_error("function symbols must have a size set with .size"); + + 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 the type index fixups for call_indirect etc. instructions. + for (size_t i = 0, e = TypeIndexFixups.size(); i < e; ++i) { + uint32_t Type = TypeIndexFixupTypes[i]; + unsigned Padding = PaddingFor5ByteULEB128(Type); + + const WasmRelocationEntry &Fixup = TypeIndexFixups[i]; + assert(Fixup.Addend == 0); + assert(Fixup.Type == wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB); + uint64_t Offset = Fixup.Offset + + Fixup.FixupSection->getSectionOffset(); + + uint8_t Buffer[16]; + unsigned SizeLen = encodeULEB128(Type, Buffer, Padding); + assert(SizeLen == 5); + getStream().pwrite((char *)Buffer, SizeLen, + Section.ContentsOffset + Offset); + } + + // Apply fixups. + ApplyRelocations(CodeRelocations, getStream(), SymbolIndices, + Section.ContentsOffset); + + endSection(Section); + } + + // === Data Section ========================================================== + if (!DataBytes.empty()) { + startSection(Section, wasm::WASM_SEC_DATA); + + encodeULEB128(1, getStream()); // count + encodeULEB128(0, getStream()); // memory index + write8(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(0, getStream()); // offset + write8(wasm::WASM_OPCODE_END); + encodeULEB128(DataBytes.size(), getStream()); // size + writeBytes(DataBytes); // data + + // Apply fixups. + ApplyRelocations(DataRelocations, getStream(), SymbolIndices, + Section.ContentsOffset); + + endSection(Section); + } + + // === Name Section ========================================================== + uint32_t TotalFunctions = NumFuncImports + Functions.size(); + if (TotalFunctions != 0) { + 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()); + encodeULEB128(Import.FieldName.size(), getStream()); + writeBytes(Import.FieldName); + ++Index; + } + } + for (const WasmFunction &Func : Functions) { + encodeULEB128(Index, getStream()); + encodeULEB128(Func.Sym->getName().size(), getStream()); + writeBytes(Func.Sym->getName()); + ++Index; + } + + endSection(SubSection); + endSection(Section); + } + + // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md + // for descriptions of the reloc sections. + + // === Code Reloc Section ==================================================== + if (!CodeRelocations.empty()) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.CODE"); + + encodeULEB128(wasm::WASM_SEC_CODE, getStream()); + + encodeULEB128(CodeRelocations.size(), getStream()); + + WriteRelocations(CodeRelocations, getStream(), SymbolIndices); + WriteTypeRelocations(TypeIndexFixups, TypeIndexFixupTypes, getStream()); + + endSection(Section); + } + + // === Data Reloc Section ==================================================== + if (!DataRelocations.empty()) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "reloc.DATA"); + + encodeULEB128(wasm::WASM_SEC_DATA, getStream()); + + encodeULEB128(DataRelocations.size(), getStream()); + + WriteRelocations(DataRelocations, getStream(), SymbolIndices); + + endSection(Section); + } + + // === Linking Metadata Section ============================================== + if (HasStackPointer) { + startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); + + encodeULEB128(1, getStream()); // count + + encodeULEB128(wasm::WASM_STACK_POINTER, getStream()); // type + encodeULEB128(StackPointerGlobal, getStream()); // id + + endSection(Section); + } + + // TODO: Translate the .comment section to the output. + + // TODO: Translate debug sections to the output. +} + +MCObjectWriter *llvm::createWasmObjectWriter(MCWasmObjectTargetWriter *MOTW, + raw_pwrite_stream &OS) { + return new WasmObjectWriter(MOTW, OS); +} |