Mercurial > hg > CbC > CbC_llvm
diff lib/MC/WasmObjectWriter.cpp @ 134:3a76565eade5 LLVM5.0.1
update 5.0.1
author | mir3636 |
---|---|
date | Sat, 17 Feb 2018 09:57:20 +0900 |
parents | 803732b1fca8 |
children | c2174574ed3a |
line wrap: on
line diff
--- a/lib/MC/WasmObjectWriter.cpp Fri Feb 16 19:10:49 2018 +0900 +++ b/lib/MC/WasmObjectWriter.cpp Sat Feb 17 09:57:20 2018 +0900 @@ -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" @@ -40,6 +38,10 @@ namespace { +// Went we ceate the indirect function table we start at 1, so that there is +// and emtpy slot at 0 and therefore calling a null function pointer will trap. +static const uint32_t kInitialTableOffset = 1; + // 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. @@ -109,34 +111,24 @@ 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::WasmGlobalType Type; + uint64_t InitialValue; }; -// 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 item which is part of a COMDAT. For each data +// segment or function which is in the COMDAT, there is a corresponding +// WasmComdatEntry. +struct WasmComdatEntry { + unsigned Kind; + uint32_t Index; }; // Information about a single relocation. @@ -183,15 +175,6 @@ #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; @@ -206,7 +189,7 @@ 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; + DenseMap<const MCSymbolWasm *, uint32_t> TableIndices; // Maps function/global symbols to the function/global index space. DenseMap<const MCSymbolWasm *, uint32_t> SymbolIndices; @@ -214,6 +197,7 @@ FunctionTypeIndices; SmallVector<WasmFunctionType, 4> FunctionTypes; SmallVector<WasmGlobal, 4> Globals; + unsigned NumFunctionImports = 0; unsigned NumGlobalImports = 0; // TargetObjectWriter wrappers. @@ -232,19 +216,20 @@ : MCObjectWriter(OS, /*IsLittleEndian=*/true), TargetObjectWriter(std::move(MOTW)) {} -private: ~WasmObjectWriter() override; +private: void reset() override { CodeRelocations.clear(); DataRelocations.clear(); TypeIndices.clear(); SymbolIndices.clear(); - IndirectSymbolIndices.clear(); + TableIndices.clear(); FunctionTypeIndices.clear(); FunctionTypes.clear(); Globals.clear(); MCObjectWriter::reset(); + NumFunctionImports = 0; NumGlobalImports = 0; } @@ -269,25 +254,22 @@ } void writeTypeSection(ArrayRef<WasmFunctionType> FunctionTypes); - void writeImportSection(ArrayRef<WasmImport> Imports); + void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint32_t DataSize, + uint32_t NumElements); void writeFunctionSection(ArrayRef<WasmFunction> Functions); - void writeTableSection(uint32_t NumElements); - void writeMemorySection(uint32_t DataSize); void writeGlobalSection(); - void writeExportSection(ArrayRef<WasmExport> Exports); + void writeExportSection(ArrayRef<wasm::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); + ArrayRef<std::pair<StringRef, uint32_t>> SymbolFlags, + ArrayRef<std::pair<uint16_t, uint32_t>> InitFuncs, + const std::map<StringRef, std::vector<WasmComdatEntry>>& Comdats); uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations, @@ -369,6 +351,10 @@ 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"); @@ -483,25 +469,43 @@ } // 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. +// by RelEntry. This value isn't used by the static linker; it just serves +// to make the object format more readable and more likely to be directly +// useable. 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; + switch (RelEntry.Type) { + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: { + // Provisional value is table address of the resolved symbol itself + const MCSymbolWasm *Sym = ResolveSymbol(*RelEntry.Symbol); + assert(Sym->isFunction()); + return TableIndices[Sym]; + } + case wasm::R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case wasm::R_WEBASSEMBLY_TYPE_INDEX_LEB: + case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + // Provisional value is function/type/global index itself + return getRelocationIndexValue(RelEntry); + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: { + // Provisional value is address of the global + const MCSymbolWasm *Sym = ResolveSymbol(*RelEntry.Symbol); + // For undefined symbols, use zero + if (!Sym->isDefined()) + return 0; - uint32_t GlobalIndex = SymbolIndices[Sym]; - const WasmGlobal& Global = Globals[GlobalIndex - NumGlobalImports]; - uint64_t Address = Global.InitialValue + 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; - - return Value; + // Ignore overflow. LLVM allows address arithmetic to silently wrap. + return Address; + } + default: + llvm_unreachable("invalid relocation type"); + } } static void addData(SmallVectorImpl<char> &DataBytes, @@ -526,7 +530,10 @@ Align->getMaxBytesToEmit()); DataBytes.resize(Size, Value); } else if (auto *Fill = dyn_cast<MCFillFragment>(&Frag)) { - DataBytes.insert(DataBytes.end(), Fill->getSize(), Fill->getValue()); + int64_t Size; + if (!Fill->getSize().evaluateAsAbsolute(Size)) + llvm_unreachable("The fill should be an assembler constant"); + DataBytes.insert(DataBytes.end(), Size, Fill->getValue()); } else { const auto &DataFrag = cast<MCDataFragment>(Frag); const SmallVectorImpl<char> &Contents = DataFrag.getContents(); @@ -545,32 +552,19 @@ 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: +uint32_t +WasmObjectWriter::getRelocationIndexValue(const WasmRelocationEntry &RelEntry) { + if (RelEntry.Type == 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"); } + + if (!SymbolIndices.count(RelEntry.Symbol)) + report_fatal_error("symbol not found in function/global index space: " + + RelEntry.Symbol->getName()); + return SymbolIndices[RelEntry.Symbol]; } // Apply the portions of the relocation records that we can handle ourselves @@ -584,35 +578,23 @@ RelEntry.Offset; DEBUG(dbgs() << "applyRelocation: " << RelEntry << "\n"); + uint32_t Value = getProvisionalValue(RelEntry); + 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); + case wasm::R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: + WritePatchableLEB(Stream, Value, Offset); break; - } - case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: { - uint32_t Index = getRelocationIndexValue(RelEntry); - WriteI32(Stream, Index, Offset); + case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: + WriteI32(Stream, Value, Offset); break; - } - case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: { - uint32_t Value = getProvisionalValue(RelEntry); + case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: + case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: 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"); } @@ -661,27 +643,39 @@ endSection(Section); } -void WasmObjectWriter::writeImportSection(ArrayRef<WasmImport> Imports) { +void WasmObjectWriter::writeImportSection(ArrayRef<wasm::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); encodeULEB128(Imports.size(), getStream()); - for (const WasmImport &Import : Imports) { - writeString(Import.ModuleName); - writeString(Import.FieldName); - + for (const wasm::WasmImport &Import : Imports) { + writeString(Import.Module); + writeString(Import.Field); encodeULEB128(Import.Kind, getStream()); switch (Import.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: - encodeULEB128(Import.Type, getStream()); + encodeULEB128(Import.SigIndex, getStream()); break; case wasm::WASM_EXTERNAL_GLOBAL: - encodeSLEB128(int32_t(Import.Type), getStream()); - encodeULEB128(0, getStream()); // mutability + encodeSLEB128(Import.Global.Type, getStream()); + encodeULEB128(uint32_t(Import.Global.Mutable), getStream()); + break; + case wasm::WASM_EXTERNAL_MEMORY: + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumPages, getStream()); // initial + break; + case wasm::WASM_EXTERNAL_TABLE: + encodeSLEB128(Import.Table.ElemType, getStream()); + encodeULEB128(0, getStream()); // flags + encodeULEB128(NumElements, getStream()); // initial break; default: llvm_unreachable("unsupported import kind"); @@ -705,39 +699,6 @@ 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; @@ -747,25 +708,18 @@ encodeULEB128(Globals.size(), getStream()); for (const WasmGlobal &Global : Globals) { - writeValueType(Global.Type); - write8(Global.IsMutable); + writeValueType(static_cast<wasm::ValType>(Global.Type.Type)); + write8(Global.Type.Mutable); - 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_I32_CONST); + encodeSLEB128(Global.InitialValue, getStream()); write8(wasm::WASM_OPCODE_END); } endSection(Section); } -void WasmObjectWriter::writeExportSection(ArrayRef<WasmExport> Exports) { +void WasmObjectWriter::writeExportSection(ArrayRef<wasm::WasmExport> Exports) { if (Exports.empty()) return; @@ -773,8 +727,8 @@ startSection(Section, wasm::WASM_SEC_EXPORT); encodeULEB128(Exports.size(), getStream()); - for (const WasmExport &Export : Exports) { - writeString(Export.FieldName); + for (const wasm::WasmExport &Export : Exports) { + writeString(Export.Name); encodeSLEB128(Export.Kind, getStream()); encodeULEB128(Export.Index, getStream()); } @@ -794,7 +748,7 @@ // init expr for starting offset write8(wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(0, getStream()); + encodeSLEB128(kInitialTableOffset, getStream()); write8(wasm::WASM_OPCODE_END); encodeULEB128(TableElems.size(), getStream()); @@ -858,38 +812,6 @@ 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. @@ -928,18 +850,13 @@ void WasmObjectWriter::writeLinkingMetaDataSection( ArrayRef<WasmDataSegment> Segments, uint32_t DataSize, - SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags, - bool HasStackPointer, uint32_t StackPointerGlobal) { + ArrayRef<std::pair<StringRef, uint32_t>> SymbolFlags, + ArrayRef<std::pair<uint16_t, uint32_t>> InitFuncs, + const std::map<StringRef, std::vector<WasmComdatEntry>>& Comdats) { 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()); @@ -967,6 +884,31 @@ 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); + } + + if (Comdats.size()) { + startSection(SubSection, wasm::WASM_COMDAT_INFO); + encodeULEB128(Comdats.size(), getStream()); + for (const auto &C : Comdats) { + writeString(C.first); + encodeULEB128(0, getStream()); // flags for future use + encodeULEB128(C.second.size(), getStream()); + for (const WasmComdatEntry &Entry : C.second) { + encodeULEB128(Entry.Kind, getStream()); + encodeULEB128(Entry.Index, getStream()); + } + } + endSection(SubSection); + } + endSection(Section); } @@ -999,67 +941,75 @@ const MCAsmLayout &Layout) { DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); MCContext &Ctx = Asm.getContext(); - wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32; + int32_t PtrType = is64Bit() ? wasm::WASM_TYPE_I64 : wasm::WASM_TYPE_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<wasm::WasmImport, 4> Imports; + SmallVector<wasm::WasmExport, 4> Exports; SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags; - SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken; - unsigned NumFuncImports = 0; + SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs; + std::map<StringRef, std::vector<WasmComdatEntry>> Comdats; 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; - } - } + // 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")); + wasm::WasmImport MemImport; + MemImport.Module = MemorySym->getModuleName(); + MemImport.Field = 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")); + wasm::WasmImport TableImport; + TableImport.Module = TableSym->getModuleName(); + TableImport.Field = TableSym->getName(); + TableImport.Kind = wasm::WASM_EXTERNAL_TABLE; + TableImport.Table.ElemType = 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); - if (WS.isTemporary()) - continue; - + // Register types for all functions, including those with private linkage + // (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.isDefined() && !WS.isComdat()) || + WS.isVariable()) { + wasm::WasmImport Import; + Import.Module = WS.getModuleName(); + Import.Field = WS.getName(); if (WS.isFunction()) { Import.Kind = wasm::WASM_EXTERNAL_FUNCTION; - Import.Type = getFunctionType(WS); - SymbolIndices[&WS] = NumFuncImports; - ++NumFuncImports; + Import.SigIndex = getFunctionType(WS); + SymbolIndices[&WS] = NumFunctionImports; + ++NumFunctionImports; } else { Import.Kind = wasm::WASM_EXTERNAL_GLOBAL; - Import.Type = int32_t(PtrType); + Import.Global.Type = PtrType; + // If this global is the stack pointer, make it mutable. + if (WS.getName() == "__stack_pointer") + Import.Global.Mutable = true; + else + Import.Global.Mutable = false; + SymbolIndices[&WS] = NumGlobalImports; ++NumGlobalImports; } @@ -1068,89 +1018,16 @@ } } - // 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; + // .init_array sections are handled specially elsewhere. + if (cast<MCSectionWasm>(Sec).getSectionName().startswith(".init_array")) + continue; + + uint32_t SegmentIndex = DataSegments.size(); DataSize = alignTo(DataSize, Section.getAlignment()); DataSegments.emplace_back(); WasmDataSegment &Segment = DataSegments.back(); @@ -1162,6 +1039,11 @@ Segment.Flags = 0; DataSize += Segment.Data.size(); Section.setMemoryOffset(Segment.Offset); + + if (const MCSymbolWasm *C = Section.getGroup()) { + Comdats[C->getName()].emplace_back( + WasmComdatEntry{wasm::WASM_COMDAT_DATA, SegmentIndex}); + } } // Handle regular defined and undefined symbols. @@ -1173,14 +1055,19 @@ const auto &WS = static_cast<const MCSymbolWasm &>(S); DEBUG(dbgs() << "MCSymbol: '" << S << "'" - << " isDefined=" << S.isDefined() << " isExternal=" - << S.isExternal() << " isTemporary=" << S.isTemporary() + << " isDefined=" << S.isDefined() + << " isExternal=" << S.isExternal() + << " isTemporary=" << S.isTemporary() << " isFunction=" << WS.isFunction() << " isWeak=" << WS.isWeak() + << " isHidden=" << WS.isHidden() << " isVariable=" << WS.isVariable() << "\n"); - if (WS.isWeak()) - SymbolFlags.emplace_back(WS.getName(), wasm::WASM_SYMBOL_BINDING_WEAK); + 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; @@ -1188,7 +1075,7 @@ unsigned Index; if (WS.isFunction()) { - if (WS.isDefined(/*SetUsed=*/false)) { + if (WS.isDefined()) { if (WS.getOffset() != 0) report_fatal_error( "function sections must contain one function each"); @@ -1198,7 +1085,7 @@ "function symbols must have a size set with .size"); // A definition. Take the next available index. - Index = NumFuncImports + Functions.size(); + Index = NumFunctionImports + Functions.size(); // Prepare the function. WasmFunction Func; @@ -1212,18 +1099,11 @@ } 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 { + } else { if (WS.isTemporary() && !WS.getSize()) continue; - if (!WS.isDefined(/*SetUsed=*/false)) + if (!WS.isDefined()) continue; if (!WS.getSize()) @@ -1238,22 +1118,21 @@ // address. For externals these will also be named exports. Index = NumGlobalImports + Globals.size(); auto &DataSection = static_cast<MCSectionWasm &>(WS.getSection()); + assert(DataSection.isWasmData()); WasmGlobal Global; - Global.Type = PtrType; - Global.IsMutable = false; - Global.HasImport = false; + Global.Type.Type = PtrType; + Global.Type.Mutable = 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(); + if (WS.isDefined()) { + wasm::WasmExport Export; + Export.Name = WS.getName(); Export.Index = Index; if (WS.isFunction()) Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; @@ -1261,8 +1140,16 @@ 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); + + if (WS.isFunction()) { + auto &Section = static_cast<MCSectionWasm &>(WS.getSection()); + if (const MCSymbolWasm *C = Section.getGroup()) + Comdats[C->getName()].emplace_back( + WasmComdatEntry{wasm::WASM_COMDAT_FUNCTION, Index}); + } } } @@ -1273,7 +1160,7 @@ if (!S.isVariable()) continue; - assert(S.isDefined(/*SetUsed=*/false)); + assert(S.isDefined()); // Find the target symbol of this weak alias and export that index const auto &WS = static_cast<const MCSymbolWasm &>(S); @@ -1283,9 +1170,8 @@ uint32_t Index = SymbolIndices.find(ResolvedSym)->second; DEBUG(dbgs() << " -> index:" << Index << "\n"); - SymbolIndices[&WS] = Index; - WasmExport Export; - Export.FieldName = WS.getName(); + wasm::WasmExport Export; + Export.Name = WS.getName(); Export.Index = Index; if (WS.isFunction()) Export.Kind = wasm::WASM_EXTERNAL_FUNCTION; @@ -1298,33 +1184,99 @@ 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) + { + auto HandleReloc = [&](const WasmRelocationEntry &Rel) { + // Functions referenced by a relocation need to put in the table. This is + // purely to make the object file's provisional values readable, and is + // ignored by the linker, which re-calculates the relocations itself. + if (Rel.Type != wasm::R_WEBASSEMBLY_TABLE_INDEX_I32 && + Rel.Type != wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB) + return; + assert(Rel.Symbol->isFunction()); + const MCSymbolWasm &WS = *ResolveSymbol(*Rel.Symbol); + uint32_t SymbolIndex = SymbolIndices.find(&WS)->second; + uint32_t TableIndex = TableElems.size() + kInitialTableOffset; + if (TableIndices.try_emplace(&WS, TableIndex).second) { + DEBUG(dbgs() << " -> adding " << WS.getName() + << " to table: " << TableIndex << "\n"); + TableElems.push_back(SymbolIndex); + registerFunctionType(WS); + } + }; + + for (const WasmRelocationEntry &RelEntry : CodeRelocations) + HandleReloc(RelEntry); + for (const WasmRelocationEntry &RelEntry : DataRelocations) + HandleReloc(RelEntry); + } + + // 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; - - registerFunctionType(*Fixup.Symbol); + 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(DataSize); + // 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); writeDataSection(DataSegments); - writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); writeDataRelocSection(); writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, - HasStackPointer, StackPointerGlobal); + InitFuncs, Comdats); // TODO: Translate the .comment section to the output. // TODO: Translate debug sections to the output. @@ -1333,8 +1285,5 @@ 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)); + return llvm::make_unique<WasmObjectWriter>(std::move(MOTW), OS); }