diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-12-18 20:10:56 +0000 |
commit | 044eb2f6afba375a914ac9d8024f8f5142bb912e (patch) | |
tree | 1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /lib/MC/WasmObjectWriter.cpp | |
parent | eb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff) |
Notes
Diffstat (limited to 'lib/MC/WasmObjectWriter.cpp')
-rw-r--r-- | lib/MC/WasmObjectWriter.cpp | 648 |
1 files changed, 374 insertions, 274 deletions
diff --git a/lib/MC/WasmObjectWriter.cpp b/lib/MC/WasmObjectWriter.cpp index 0d31f65c49d9f..6e76c5fac35f3 100644 --- a/lib/MC/WasmObjectWriter.cpp +++ b/lib/MC/WasmObjectWriter.cpp @@ -15,13 +15,11 @@ #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" @@ -96,12 +94,26 @@ struct WasmFunctionTypeDenseMapInfo { } }; +// 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; + bool IsMutable; }; // A wasm function to be written into the function section. @@ -142,9 +154,9 @@ struct WasmRelocationEntry { bool hasAddend() const { switch (Type) { - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + 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; @@ -153,7 +165,8 @@ struct WasmRelocationEntry { void print(raw_ostream &Out) const { Out << "Off=" << Offset << ", Sym=" << *Symbol << ", Addend=" << Addend - << ", Type=" << Type << ", FixupSection=" << FixupSection; + << ", Type=" << Type + << ", FixupSection=" << FixupSection->getSectionName(); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) @@ -199,6 +212,8 @@ class WasmObjectWriter : public MCObjectWriter { 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(); } @@ -211,8 +226,10 @@ class WasmObjectWriter : public MCObjectWriter { void endSection(SectionBookkeeping &Section); public: - WasmObjectWriter(MCWasmObjectTargetWriter *MOTW, raw_pwrite_stream &OS) - : MCObjectWriter(OS, /*IsLittleEndian=*/true), TargetObjectWriter(MOTW) {} + WasmObjectWriter(std::unique_ptr<MCWasmObjectTargetWriter> MOTW, + raw_pwrite_stream &OS) + : MCObjectWriter(OS, /*IsLittleEndian=*/true), + TargetObjectWriter(std::move(MOTW)) {} private: ~WasmObjectWriter() override; @@ -225,7 +242,9 @@ private: IndirectSymbolIndices.clear(); FunctionTypeIndices.clear(); FunctionTypes.clear(); + Globals.clear(); MCObjectWriter::reset(); + NumGlobalImports = 0; } void writeHeader(const MCAssembler &Asm); @@ -248,33 +267,31 @@ private: encodeSLEB128(int32_t(Ty), getStream()); } - void writeTypeSection(const SmallVector<WasmFunctionType, 4> &FunctionTypes); - void writeImportSection(const SmallVector<WasmImport, 4> &Imports); - void writeFunctionSection(const SmallVector<WasmFunction, 4> &Functions); - void writeTableSection(uint32_t NumElements); - void writeMemorySection(const SmallVector<char, 0> &DataBytes); - void writeGlobalSection(const SmallVector<WasmGlobal, 4> &Globals); - void writeExportSection(const SmallVector<WasmExport, 4> &Exports); - void writeElemSection(const SmallVector<uint32_t, 4> &TableElems); + void writeTypeSection(ArrayRef<WasmFunctionType> FunctionTypes); + void writeImportSection(ArrayRef<WasmImport> Imports, uint32_t DataSize, + uint32_t NumElements); + void writeFunctionSection(ArrayRef<WasmFunction> Functions); + void writeGlobalSection(); + void writeExportSection(ArrayRef<WasmExport> Exports); + void writeElemSection(ArrayRef<uint32_t> TableElems); void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout, - const SmallVector<WasmFunction, 4> &Functions); - uint64_t - writeDataSection(const SmallVector<char, 0> &DataBytes); - void writeNameSection(const SmallVector<WasmFunction, 4> &Functions, - const SmallVector<WasmImport, 4> &Imports, + ArrayRef<WasmFunction> Functions); + void writeDataSection(ArrayRef<WasmDataSegment> Segments); + void writeNameSection(ArrayRef<WasmFunction> Functions, + ArrayRef<WasmImport> Imports, uint32_t NumFuncImports); void writeCodeRelocSection(); - void writeDataRelocSection(uint64_t DataSectionHeaderSize); - void writeLinkingMetaDataSection(uint32_t DataSize, uint32_t DataAlignment, - ArrayRef<StringRef> WeakSymbols, - bool HasStackPointer, - uint32_t StackPointerGlobal); + void writeDataRelocSection(); + void writeLinkingMetaDataSection( + ArrayRef<WasmDataSegment> Segments, uint32_t DataSize, + const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags, + const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs); + uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations, uint64_t ContentsOffset); - void writeRelocations(ArrayRef<WasmRelocationEntry> Relocations, - uint64_t HeaderSize); + void writeRelocations(ArrayRef<WasmRelocationEntry> Relocations); uint32_t getRelocationIndexValue(const WasmRelocationEntry &RelEntry); uint32_t getFunctionType(const MCSymbolWasm& Symbol); uint32_t registerFunctionType(const MCSymbolWasm& Symbol); @@ -284,16 +301,6 @@ private: 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, @@ -328,12 +335,11 @@ void WasmObjectWriter::endSection(SectionBookkeeping &Section) { report_fatal_error("section size does not fit in a uint32_t"); DEBUG(dbgs() << "endSection size=" << Size << "\n"); - 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); + unsigned SizeLen = encodeULEB128(Size, Buffer, 5); assert(SizeLen == 5); getStream().pwrite((char *)Buffer, SizeLen, Section.SizeOffset); } @@ -361,6 +367,10 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm, uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); MCContext &Ctx = Asm.getContext(); + // The .init_array isn't translated as data, so don't do relocations in it. + if (FixupSection.getSectionName().startswith(".init_array")) + return; + if (const MCSymbolRefExpr *RefB = Target.getSymB()) { assert(RefB->getKind() == MCSymbolRefExpr::VK_None && "Should not have constructed this"); @@ -429,10 +439,13 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm, WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); DEBUG(dbgs() << "WasmReloc: " << Rec << "\n"); - if (FixupSection.hasInstructions()) - CodeRelocations.push_back(Rec); - else + 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 @@ -440,8 +453,7 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm, 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); + unsigned SizeLen = encodeULEB128(X, Buffer, 5); assert(SizeLen == 5); Stream.pwrite((char *)Buffer, SizeLen, Offset); } @@ -451,8 +463,7 @@ WritePatchableLEB(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { 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); + unsigned SizeLen = encodeSLEB128(X, Buffer, 5); assert(SizeLen == 5); Stream.pwrite((char *)Buffer, SizeLen, Offset); } @@ -464,19 +475,30 @@ static void WriteI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) { 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. -static uint32_t ProvisionalValue(const WasmRelocationEntry &RelEntry) { - const MCSymbolWasm *Sym = RelEntry.Symbol; +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; - const auto &Section = cast<MCSectionWasm>(RelEntry.Symbol->getSection(false)); - uint64_t Address = Section.getSectionOffset() + RelEntry.Addend; + 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; @@ -484,6 +506,47 @@ static uint32_t ProvisionalValue(const WasmRelocationEntry &RelEntry) { 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) { @@ -495,9 +558,9 @@ uint32_t WasmObjectWriter::getRelocationIndexValue( return IndirectSymbolIndices[RelEntry.Symbol]; case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + 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()); @@ -537,18 +600,18 @@ void WasmObjectWriter::applyRelocations( WriteI32(Stream, Index, Offset); break; } - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: { - uint32_t Value = ProvisionalValue(RelEntry); + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: { + uint32_t Value = getProvisionalValue(RelEntry); WritePatchableSLEB(Stream, Value, Offset); break; } - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_LEB: { - uint32_t Value = ProvisionalValue(RelEntry); + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: { + uint32_t Value = getProvisionalValue(RelEntry); WritePatchableLEB(Stream, Value, Offset); break; } - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: { - uint32_t Value = ProvisionalValue(RelEntry); + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: { + uint32_t Value = getProvisionalValue(RelEntry); WriteI32(Stream, Value, Offset); break; } @@ -561,12 +624,12 @@ void WasmObjectWriter::applyRelocations( // Write out the portions of the relocation records that the linker will // need to handle. void WasmObjectWriter::writeRelocations( - ArrayRef<WasmRelocationEntry> Relocations, uint64_t HeaderSize) { + ArrayRef<WasmRelocationEntry> Relocations) { raw_pwrite_stream &Stream = getStream(); for (const WasmRelocationEntry& RelEntry : Relocations) { uint64_t Offset = RelEntry.Offset + - RelEntry.FixupSection->getSectionOffset() + HeaderSize; + RelEntry.FixupSection->getSectionOffset(); uint32_t Index = getRelocationIndexValue(RelEntry); encodeULEB128(RelEntry.Type, Stream); @@ -578,7 +641,7 @@ void WasmObjectWriter::writeRelocations( } void WasmObjectWriter::writeTypeSection( - const SmallVector<WasmFunctionType, 4> &FunctionTypes) { + ArrayRef<WasmFunctionType> FunctionTypes) { if (FunctionTypes.empty()) return; @@ -600,12 +663,14 @@ void WasmObjectWriter::writeTypeSection( endSection(Section); } - -void WasmObjectWriter::writeImportSection( - const SmallVector<WasmImport, 4> &Imports) { +void WasmObjectWriter::writeImportSection(ArrayRef<WasmImport> Imports, + uint32_t DataSize, + uint32_t NumElements) { if (Imports.empty()) return; + uint32_t NumPages = (DataSize + wasm::WasmPageSize - 1) / wasm::WasmPageSize; + SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_IMPORT); @@ -622,7 +687,16 @@ void WasmObjectWriter::writeImportSection( break; case wasm::WASM_EXTERNAL_GLOBAL: encodeSLEB128(int32_t(Import.Type), getStream()); - encodeULEB128(0, getStream()); // mutability + encodeULEB128(int32_t(Import.IsMutable), getStream()); + break; + case wasm::WASM_EXTERNAL_MEMORY: + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumPages, getStream()); // initial + break; + case wasm::WASM_EXTERNAL_TABLE: + encodeSLEB128(int32_t(Import.Type), getStream()); + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumElements, getStream()); // initial break; default: llvm_unreachable("unsupported import kind"); @@ -632,8 +706,7 @@ void WasmObjectWriter::writeImportSection( endSection(Section); } -void WasmObjectWriter::writeFunctionSection( - const SmallVector<WasmFunction, 4> &Functions) { +void WasmObjectWriter::writeFunctionSection(ArrayRef<WasmFunction> Functions) { if (Functions.empty()) return; @@ -647,43 +720,7 @@ void WasmObjectWriter::writeFunctionSection( 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( - const SmallVector<char, 0> &DataBytes) { - // 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 = - (DataBytes.size() + 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( - const SmallVector<WasmGlobal, 4> &Globals) { +void WasmObjectWriter::writeGlobalSection() { if (Globals.empty()) return; @@ -710,8 +747,7 @@ void WasmObjectWriter::writeGlobalSection( endSection(Section); } -void WasmObjectWriter::writeExportSection( - const SmallVector<WasmExport, 4> &Exports) { +void WasmObjectWriter::writeExportSection(ArrayRef<WasmExport> Exports) { if (Exports.empty()) return; @@ -728,8 +764,7 @@ void WasmObjectWriter::writeExportSection( endSection(Section); } -void WasmObjectWriter::writeElemSection( - const SmallVector<uint32_t, 4> &TableElems) { +void WasmObjectWriter::writeElemSection(ArrayRef<uint32_t> TableElems) { if (TableElems.empty()) return; @@ -751,9 +786,9 @@ void WasmObjectWriter::writeElemSection( endSection(Section); } -void WasmObjectWriter::writeCodeSection( - const MCAssembler &Asm, const MCAsmLayout &Layout, - const SmallVector<WasmFunction, 4> &Functions) { +void WasmObjectWriter::writeCodeSection(const MCAssembler &Asm, + const MCAsmLayout &Layout, + ArrayRef<WasmFunction> Functions) { if (Functions.empty()) return; @@ -770,9 +805,7 @@ void WasmObjectWriter::writeCodeSection( report_fatal_error(".size expression must be evaluatable"); encodeULEB128(Size, getStream()); - FuncSection.setSectionOffset(getStream().tell() - Section.ContentsOffset); - Asm.writeSectionData(&FuncSection, Layout); } @@ -782,33 +815,34 @@ void WasmObjectWriter::writeCodeSection( endSection(Section); } -uint64_t WasmObjectWriter::writeDataSection( - const SmallVector<char, 0> &DataBytes) { - if (DataBytes.empty()) - return 0; +void WasmObjectWriter::writeDataSection(ArrayRef<WasmDataSegment> Segments) { + if (Segments.empty()) + return; SectionBookkeeping Section; 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 - uint32_t HeaderSize = getStream().tell() - Section.ContentsOffset; - writeBytes(DataBytes); // 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 + HeaderSize); + applyRelocations(DataRelocations, Section.ContentsOffset); endSection(Section); - return HeaderSize; } void WasmObjectWriter::writeNameSection( - const SmallVector<WasmFunction, 4> &Functions, - const SmallVector<WasmImport, 4> &Imports, + ArrayRef<WasmFunction> Functions, + ArrayRef<WasmImport> Imports, unsigned NumFuncImports) { uint32_t TotalFunctions = NumFuncImports + Functions.size(); if (TotalFunctions == 0) @@ -851,12 +885,12 @@ void WasmObjectWriter::writeCodeRelocSection() { encodeULEB128(wasm::WASM_SEC_CODE, getStream()); encodeULEB128(CodeRelocations.size(), getStream()); - writeRelocations(CodeRelocations, 0); + writeRelocations(CodeRelocations); endSection(Section); } -void WasmObjectWriter::writeDataRelocSection(uint64_t DataSectionHeaderSize) { +void WasmObjectWriter::writeDataRelocSection() { // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md // for descriptions of the reloc sections. @@ -869,30 +903,25 @@ void WasmObjectWriter::writeDataRelocSection(uint64_t DataSectionHeaderSize) { encodeULEB128(wasm::WASM_SEC_DATA, getStream()); encodeULEB128(DataRelocations.size(), getStream()); - writeRelocations(DataRelocations, DataSectionHeaderSize); + writeRelocations(DataRelocations); endSection(Section); } void WasmObjectWriter::writeLinkingMetaDataSection( - uint32_t DataSize, uint32_t DataAlignment, ArrayRef<StringRef> WeakSymbols, - bool HasStackPointer, uint32_t StackPointerGlobal) { + ArrayRef<WasmDataSegment> Segments, uint32_t DataSize, + const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags, + const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs) { 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 (WeakSymbols.size() != 0) { + if (SymbolFlags.size() != 0) { startSection(SubSection, wasm::WASM_SYMBOL_INFO); - encodeULEB128(WeakSymbols.size(), getStream()); - for (const StringRef Export: WeakSymbols) { - writeString(Export); - encodeULEB128(wasm::WASM_SYMBOL_FLAG_WEAK, getStream()); + encodeULEB128(SymbolFlags.size(), getStream()); + for (auto Pair: SymbolFlags) { + writeString(Pair.first); + encodeULEB128(Pair.second, getStream()); } endSection(SubSection); } @@ -901,9 +930,26 @@ void WasmObjectWriter::writeLinkingMetaDataSection( startSection(SubSection, wasm::WASM_DATA_SIZE); encodeULEB128(DataSize, getStream()); endSection(SubSection); + } - startSection(SubSection, wasm::WASM_DATA_ALIGNMENT); - encodeULEB128(DataAlignment, getStream()); + 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); + } + + if (!InitFuncs.empty()) { + startSection(SubSection, wasm::WASM_INIT_FUNCS); + encodeULEB128(InitFuncs.size(), getStream()); + for (auto &StartFunc : InitFuncs) { + encodeULEB128(StartFunc.first, getStream()); // priority + encodeULEB128(StartFunc.second, getStream()); // function index + } endSection(SubSection); } @@ -920,16 +966,9 @@ uint32_t WasmObjectWriter::registerFunctionType(const MCSymbolWasm& Symbol) { assert(Symbol.isFunction()); WasmFunctionType F; - if (Symbol.isVariable()) { - const MCExpr *Expr = Symbol.getVariableValue(); - auto *Inner = cast<MCSymbolRefExpr>(Expr); - const auto *ResolvedSym = cast<MCSymbolWasm>(&Inner->getSymbol()); - F.Returns = ResolvedSym->getReturns(); - F.Params = ResolvedSym->getParams(); - } else { - F.Returns = Symbol.getReturns(); - F.Params = Symbol.getParams(); - } + const MCSymbolWasm* ResolvedSym = ResolveSymbol(Symbol); + F.Returns = ResolvedSym->getReturns(); + F.Params = ResolvedSym->getParams(); auto Pair = FunctionTypeIndices.insert(std::make_pair(F, FunctionTypes.size())); @@ -951,23 +990,20 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, // Collect information from the available symbols. SmallVector<WasmFunction, 4> Functions; SmallVector<uint32_t, 4> TableElems; - SmallVector<WasmGlobal, 4> Globals; SmallVector<WasmImport, 4> Imports; SmallVector<WasmExport, 4> Exports; - SmallVector<StringRef, 4> WeakSymbols; + SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags; + SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs; SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken; unsigned NumFuncImports = 0; - unsigned NumGlobalImports = 0; - SmallVector<char, 0> DataBytes; - uint32_t DataAlignment = 1; - uint32_t StackPointerGlobal = 0; - bool HasStackPointer = false; + SmallVector<WasmDataSegment, 4> DataSegments; + uint32_t DataSize = 0; // Populate the IsAddressTaken set. for (const WasmRelocationEntry &RelEntry : CodeRelocations) { switch (RelEntry.Type) { case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: IsAddressTaken.insert(RelEntry.Symbol); break; default: @@ -977,7 +1013,7 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, for (const WasmRelocationEntry &RelEntry : DataRelocations) { switch (RelEntry.Type) { case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: - case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: IsAddressTaken.insert(RelEntry.Symbol); break; default: @@ -985,42 +1021,11 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, } } - // Populate the Imports set. - 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) || WS.isVariable()) { - 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", 0, 0); + 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"); @@ -1074,23 +1079,91 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, } } - // 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 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 now, always emit the memory import, 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. + MCSymbolWasm *MemorySym = + cast<MCSymbolWasm>(Ctx.getOrCreateSymbol("__linear_memory")); + WasmImport MemImport; + MemImport.ModuleName = MemorySym->getModuleName(); + MemImport.FieldName = MemorySym->getName(); + MemImport.Kind = wasm::WASM_EXTERNAL_MEMORY; + Imports.push_back(MemImport); + + // 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. + MCSymbolWasm *TableSym = + cast<MCSymbolWasm>(Ctx.getOrCreateSymbol("__indirect_function_table")); + WasmImport TableImport; + TableImport.ModuleName = TableSym->getModuleName(); + TableImport.FieldName = TableSym->getName(); + TableImport.Kind = wasm::WASM_EXTERNAL_TABLE; + TableImport.Type = wasm::WASM_TYPE_ANYFUNC; + Imports.push_back(TableImport); + + // Populate FunctionTypeIndices and Imports. + for (const MCSymbol &S : Asm.symbols()) { + const auto &WS = static_cast<const MCSymbolWasm &>(S); + + // Register types for all functions, including those with private linkage + // (making them + // because wasm always needs a type signature. + if (WS.isFunction()) + registerFunctionType(WS); + + if (WS.isTemporary()) + continue; + + // 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); + Import.IsMutable = false; + SymbolIndices[&WS] = NumGlobalImports; + + // If this global is the stack pointer, make it mutable and remember it + // so that we can emit metadata for it. + if (WS.getName() == "__stack_pointer") + Import.IsMutable = true; + + ++NumGlobalImports; + } + + Imports.push_back(Import); + } + } + + for (MCSection &Sec : Asm) { + auto &Section = static_cast<MCSectionWasm &>(Sec); + if (!Section.isWasmData()) + continue; + + // .init_array sections are handled specially elsewhere. + if (cast<MCSectionWasm>(Sec).getSectionName().startswith(".init_array")) + 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. @@ -1106,10 +1179,14 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, << S.isExternal() << " isTemporary=" << S.isTemporary() << " isFunction=" << WS.isFunction() << " isWeak=" << WS.isWeak() + << " isHidden=" << WS.isHidden() << " isVariable=" << WS.isVariable() << "\n"); - if (WS.isWeak()) - WeakSymbols.push_back(WS.getName()); + if (WS.isWeak() || WS.isHidden()) { + uint32_t Flags = (WS.isWeak() ? wasm::WASM_SYMBOL_BINDING_WEAK : 0) | + (WS.isHidden() ? wasm::WASM_SYMBOL_VISIBILITY_HIDDEN : 0); + SymbolFlags.emplace_back(WS.getName(), Flags); + } if (WS.isVariable()) continue; @@ -1155,9 +1232,6 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, if (!WS.isDefined(/*SetUsed=*/false)) continue; - if (WS.getOffset() != 0) - report_fatal_error("data sections must contain one variable each: " + - WS.getName()); if (!WS.getSize()) report_fatal_error("data symbols must have a size set with .size: " + WS.getName()); @@ -1166,49 +1240,16 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, if (!WS.getSize()->evaluateAsAbsolute(Size, Layout)) report_fatal_error(".size expression must be evaluatable"); - auto &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())); - DataAlignment = std::max(DataAlignment, DataSection.getAlignment()); - - DataSection.setSectionOffset(DataBytes.size()); - - 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()); - } - } - // 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.getSectionOffset(); + Global.InitialValue = DataSection.getMemoryOffset() + Layout.getSymbolOffset(WS); Global.ImportIndex = 0; SymbolIndices[&WS] = Index; DEBUG(dbgs() << " -> global index: " << Index << "\n"); @@ -1216,7 +1257,7 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, } // If the symbol is visible outside this translation unit, export it. - if ((WS.isExternal() && WS.isDefined(/*SetUsed=*/false))) { + if (WS.isDefined(/*SetUsed=*/false)) { WasmExport Export; Export.FieldName = WS.getName(); Export.Index = Index; @@ -1226,6 +1267,8 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, 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); } } @@ -1235,18 +1278,18 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, for (const MCSymbol &S : Asm.symbols()) { if (!S.isVariable()) continue; + assert(S.isDefined(/*SetUsed=*/false)); - const auto &WS = static_cast<const MCSymbolWasm &>(S); // Find the target symbol of this weak alias and export that index - const MCExpr *Expr = WS.getVariableValue(); - auto *Inner = cast<MCSymbolRefExpr>(Expr); - const auto *ResolvedSym = cast<MCSymbolWasm>(&Inner->getSymbol()); + 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; @@ -1256,6 +1299,9 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, 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. @@ -1266,30 +1312,84 @@ void WasmObjectWriter::writeObject(MCAssembler &Asm, registerFunctionType(*Fixup.Symbol); } + // Translate .init_array section contents into start functions. + for (const MCSection &S : Asm) { + const auto &WS = static_cast<const MCSectionWasm &>(S); + if (WS.getSectionName().startswith(".fini_array")) + report_fatal_error(".fini_array sections are unsupported"); + if (!WS.getSectionName().startswith(".init_array")) + continue; + if (WS.getFragmentList().empty()) + continue; + if (WS.getFragmentList().size() != 2) + report_fatal_error("only one .init_array section fragment supported"); + const MCFragment &AlignFrag = *WS.begin(); + if (AlignFrag.getKind() != MCFragment::FT_Align) + report_fatal_error(".init_array section should be aligned"); + if (cast<MCAlignFragment>(AlignFrag).getAlignment() != (is64Bit() ? 8 : 4)) + report_fatal_error(".init_array section should be aligned for pointers"); + const MCFragment &Frag = *std::next(WS.begin()); + if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data) + report_fatal_error("only data supported in .init_array section"); + uint16_t Priority = UINT16_MAX; + if (WS.getSectionName().size() != 11) { + if (WS.getSectionName()[11] != '.') + report_fatal_error(".init_array section priority should start with '.'"); + if (WS.getSectionName().substr(12).getAsInteger(10, Priority)) + report_fatal_error("invalid .init_array section priority"); + } + const auto &DataFrag = cast<MCDataFragment>(Frag); + 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; ++p) { + if (*p != 0) + report_fatal_error("non-symbolic data in .init_array section"); + } + for (const MCFixup &Fixup : DataFrag.getFixups()) { + assert(Fixup.getKind() == MCFixup::getKindForSize(is64Bit() ? 8 : 4, false)); + const MCExpr *Expr = Fixup.getValue(); + auto *Sym = dyn_cast<MCSymbolRefExpr>(Expr); + if (!Sym) + report_fatal_error("fixups in .init_array should be symbol references"); + if (Sym->getKind() != MCSymbolRefExpr::VK_WebAssembly_FUNCTION) + report_fatal_error("symbols in .init_array should be for functions"); + auto I = SymbolIndices.find(cast<MCSymbolWasm>(&Sym->getSymbol())); + if (I == SymbolIndices.end()) + report_fatal_error("symbols in .init_array should be defined"); + uint32_t Index = I->second; + InitFuncs.push_back(std::make_pair(Priority, Index)); + } + } + // Write out the Wasm header. writeHeader(Asm); writeTypeSection(FunctionTypes); - writeImportSection(Imports); + writeImportSection(Imports, DataSize, TableElems.size()); writeFunctionSection(Functions); - writeTableSection(TableElems.size()); - writeMemorySection(DataBytes); - writeGlobalSection(Globals); + // Skip the "table" section; we import the table instead. + // Skip the "memory" section; we import the memory instead. + writeGlobalSection(); writeExportSection(Exports); - // TODO: Start Section writeElemSection(TableElems); writeCodeSection(Asm, Layout, Functions); - uint64_t DataSectionHeaderSize = writeDataSection(DataBytes); + writeDataSection(DataSegments); writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); - writeDataRelocSection(DataSectionHeaderSize); - writeLinkingMetaDataSection(DataBytes.size(), DataAlignment, WeakSymbols, HasStackPointer, StackPointerGlobal); + writeDataRelocSection(); + writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, + InitFuncs); // 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); +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)); } |