Index: include/llvm/BinaryFormat/Wasm.h =================================================================== --- include/llvm/BinaryFormat/Wasm.h +++ include/llvm/BinaryFormat/Wasm.h @@ -176,6 +176,11 @@ // Linking metadata kinds. enum : unsigned { WASM_STACK_POINTER = 0x1, + WASM_SYMBOL_INFO = 0x2, +}; + +enum : unsigned { + WASM_SYMBOL_FLAG_WEAK = 0x1, }; #define WASM_RELOC(name, value) name = value, Index: include/llvm/MC/MCWasmObjectWriter.h =================================================================== --- include/llvm/MC/MCWasmObjectWriter.h +++ include/llvm/MC/MCWasmObjectWriter.h @@ -13,6 +13,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/MC/MCValue.h" +#include "llvm/MC/MCSymbolWasm.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/raw_ostream.h" #include Index: include/llvm/Object/Wasm.h =================================================================== --- include/llvm/Object/Wasm.h +++ include/llvm/Object/Wasm.h @@ -48,11 +48,20 @@ StringRef Name; SymbolType Type; uint32_t Section; + uint32_t Flags = 0; // For imports, this is an import index // For exports, this is an export index // For debug names, this is a function index uint32_t ElementIndex; + + void print(raw_ostream &Out) const { + Out << "Name=" << Name << ", Type=" << static_cast(Type) << ", Flags=" << Flags; + } + + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(dbgs()); } + #endif }; class WasmSection { @@ -66,7 +75,18 @@ std::vector Relocations; // Relocations for this section }; +struct WasmExportSym { + wasm::WasmExport Export; + uint32_t Symbol; +}; + +struct WasmImportSym { + wasm::WasmImport Import; + uint32_t Symbol; +}; + class WasmObjectFile : public ObjectFile { + public: WasmObjectFile(MemoryBufferRef Object, Error &Err); @@ -80,11 +100,11 @@ const std::vector& types() const { return Signatures; } const std::vector& functionTypes() const { return FunctionTypes; } - const std::vector& imports() const { return Imports; } + const std::vector& imports() const { return Imports; } const std::vector& tables() const { return Tables; } const std::vector& memories() const { return Memories; } const std::vector& globals() const { return Globals; } - const std::vector& exports() const { return Exports; } + const std::vector& exports() const { return Exports; } uint32_t getNumberOfSymbols() const { return Symbols.size(); @@ -159,6 +179,7 @@ WasmSection* findCustomSectionByName(StringRef Name); WasmSection* findSectionByType(uint32_t Type); + BasicSymbolRef makeSymbolRef(uint32_t Index) const; const uint8_t *getPtr(size_t Offset) const; Error parseSection(WasmSection &Sec); @@ -180,6 +201,7 @@ // Custom section types Error parseNameSection(const uint8_t *Ptr, const uint8_t *End); + Error parseLinkingSection(const uint8_t *Ptr, const uint8_t *End); Error parseRelocSection(StringRef Name, const uint8_t *Ptr, const uint8_t *End); @@ -190,8 +212,8 @@ std::vector Tables; std::vector Memories; std::vector Globals; - std::vector Imports; - std::vector Exports; + std::vector Imports; + std::vector Exports; std::vector ElemSegments; std::vector DataSegments; std::vector Symbols; @@ -201,6 +223,13 @@ }; } // end namespace object + +inline raw_ostream &operator<<(raw_ostream &OS, + const object::WasmSymbol &Sym) { + Sym.print(OS); + return OS; +} + } // end namespace llvm #endif // LLVM_OBJECT_WASM_H Index: lib/MC/MCWasmStreamer.cpp =================================================================== --- lib/MC/MCWasmStreamer.cpp +++ lib/MC/MCWasmStreamer.cpp @@ -99,15 +99,24 @@ case MCSA_Invalid: case MCSA_IndirectSymbol: return false; + + case MCSA_WeakReference: + case MCSA_Weak: + Symbol->setExternal(true); + break; + case MCSA_Global: Symbol->setExternal(true); break; + case MCSA_ELF_TypeFunction: Symbol->setIsFunction(true); break; + case MCSA_ELF_TypeObject: Symbol->setIsFunction(false); break; + default: // unrecognized directive return false; Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -154,11 +154,20 @@ void print(raw_ostream &Out) const { Out << "Off=" << Offset << ", Sym=" << Symbol->getName() << ", Addend=" << Addend - << ", Type=" << Type << ", FixupSection=" << FixupSection << "\n"; + << ", Type=" << Type << ", FixupSection=" << FixupSection; } - void dump() const { print(errs()); } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { print(dbgs()); } +#endif }; +inline raw_ostream &operator<<(raw_ostream &OS, + const WasmRelocationEntry &Rel) { + Rel.print(OS); + return OS; +} + class WasmObjectWriter : public MCObjectWriter { /// Helper struct for containing some precomputed information on symbols. struct WasmSymbolData { @@ -251,7 +260,8 @@ uint32_t NumFuncImports); void writeCodeRelocSection(); void writeDataRelocSection(uint64_t DataSectionHeaderSize); - void writeLinkingMetaDataSection(bool HasStackPointer, + void writeLinkingMetaDataSection(ArrayRef WeakExports, + bool HasStackPointer, uint32_t StackPointerGlobal); void applyRelocations(ArrayRef Relocations, @@ -283,6 +293,7 @@ assert((Name != nullptr) == (SectionId == wasm::WASM_SEC_CUSTOM) && "Only custom sections can have names"); + DEBUG(dbgs() << "startSection " << SectionId << ": " << Name << "\n"); encodeULEB128(SectionId, getStream()); Section.SizeOffset = getStream().tell(); @@ -308,6 +319,7 @@ if (uint32_t(Size) != Size) 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 @@ -410,6 +422,7 @@ assert(SymA); WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection); + DEBUG(dbgs() << "WasmReloc: " << Rec << "\n"); if (FixupSection.hasInstructions()) CodeRelocations.push_back(Rec); @@ -454,7 +467,7 @@ const MCSymbolWasm *Sym = RelEntry.Symbol; // For undefined symbols, use a hopefully invalid value. - if (!Sym->isDefined(false)) + if (!Sym->isDefined(/*SetUsed=*/false)) return UINT32_MAX; MCSectionWasm &Section = @@ -751,15 +764,6 @@ MCSectionWasm &FuncSection = static_cast(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"); @@ -873,22 +877,38 @@ } void WasmObjectWriter::writeLinkingMetaDataSection( - bool HasStackPointer, uint32_t StackPointerGlobal) { - if (!HasStackPointer) + ArrayRef WeakExports, bool HasStackPointer, + uint32_t StackPointerGlobal) { + if (!HasStackPointer && WeakExports.empty()) return; + SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); + SectionBookkeeping SubSection; - encodeULEB128(1, getStream()); // count + if (HasStackPointer) { + startSection(SubSection, wasm::WASM_STACK_POINTER); + encodeULEB128(StackPointerGlobal, getStream()); // id + endSection(SubSection); + } - encodeULEB128(wasm::WASM_STACK_POINTER, getStream()); // type - encodeULEB128(StackPointerGlobal, getStream()); // id + if (WeakExports.size() != 0) { + startSection(SubSection, wasm::WASM_SYMBOL_INFO); + encodeULEB128(WeakExports.size(), getStream()); + for (uint32_t Export: WeakExports) { + write8(0); // 1 - Import, 0 = Export + encodeULEB128(Export, getStream()); // Import or Export index + encodeULEB128(wasm::WASM_SYMBOL_FLAG_WEAK, getStream()); + } + endSection(SubSection); + } endSection(Section); } void WasmObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) { + DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); MCContext &Ctx = Asm.getContext(); wasm::ValType PtrType = is64Bit() ? wasm::ValType::I64 : wasm::ValType::I32; @@ -899,6 +919,7 @@ SmallVector Globals; SmallVector Imports; SmallVector Exports; + SmallVector WeakExports; SmallPtrSet IsAddressTaken; unsigned NumFuncImports = 0; unsigned NumGlobalImports = 0; @@ -907,7 +928,7 @@ bool HasStackPointer = false; // Populate the IsAddressTaken set. - for (WasmRelocationEntry RelEntry : CodeRelocations) { + for (const WasmRelocationEntry &RelEntry : CodeRelocations) { switch (RelEntry.Type) { case wasm::R_WEBASSEMBLY_TABLE_INDEX_SLEB: case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_SLEB: @@ -917,7 +938,7 @@ break; } } - for (WasmRelocationEntry RelEntry : DataRelocations) { + for (const WasmRelocationEntry &RelEntry : DataRelocations) { switch (RelEntry.Type) { case wasm::R_WEBASSEMBLY_TABLE_INDEX_I32: case wasm::R_WEBASSEMBLY_GLOBAL_ADDR_I32: @@ -1054,6 +1075,8 @@ continue; const auto &WS = static_cast(S); unsigned Index; + DEBUG(dbgs() << "MCSymbolWasm: " << WS << "\n"); + if (WS.isFunction()) { // Prepare the function's type, if we haven't seen it yet. WasmFunctionType F; @@ -1067,16 +1090,36 @@ 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); + if (WS.isVariable()) { + // A weak reference to another symbol. + const MCExpr *Expr = WS.getVariableValue(); + auto *Inner = dyn_cast(Expr); + const MCSymbolWasm *ResolvedSym = cast(&Inner->getSymbol()); + Index = SymbolIndices.find(ResolvedSym)->second; + DEBUG(dbgs() << "Resolved weak symbol: " << WS << " -> " << Index + << "\n"); + } else { + if (WS.getOffset() != 0) + report_fatal_error( + "function sections must contain one function each"); + + if (WS.getSize() == 0) + report_fatal_error( + "function symbols must have a size set with .size"); + + // A definition. Take the next available index. + Index = NumFuncImports + Functions.size(); + + // Prepare the function. + WasmFunction Func; + Func.Type = Type; + Func.Sym = &WS; + SymbolIndices[&WS] = Index; + Functions.push_back(Func); + } } else { + // Should be no such thing as weak undefined symbol + assert(!WS.isVariable()); // An import; the index was assigned above. Index = SymbolIndices.find(&WS)->second; } @@ -1090,7 +1133,10 @@ if (WS.isTemporary() && !WS.getSize()) continue; - if (WS.isDefined(false)) { + if (WS.isVariable()) + report_fatal_error("weak data symbols not supported yet: " + WS.getName()); + + if (WS.isDefined(/*SetUsed=*/false)) { if (WS.getOffset() != 0) report_fatal_error("data sections must contain one variable each: " + WS.getName()); @@ -1156,7 +1202,8 @@ // If the symbol is visible outside this translation unit, export it. if (WS.isExternal()) { - assert(WS.isDefined(false)); + DEBUG(dbgs() << "Exporting: " << WS << " -> " << Index << "\n"); + assert(WS.isDefined(/*SetUsed=*/false)); WasmExport Export; Export.FieldName = WS.getName(); Export.Index = Index; @@ -1166,6 +1213,10 @@ else Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; + if (WS.isVariable()) { + DEBUG(dbgs() << "Weak Export: " << WS << "\n"); + WeakExports.push_back(Exports.size()); + } Exports.push_back(Export); } } @@ -1203,7 +1254,7 @@ writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); writeDataRelocSection(DataSectionHeaderSize); - writeLinkingMetaDataSection(HasStackPointer, StackPointerGlobal); + writeLinkingMetaDataSection(WeakExports, HasStackPointer, StackPointerGlobal); // TODO: Translate the .comment section to the output. // TODO: Translate debug sections to the output. Index: lib/Object/WasmObjectFile.cpp =================================================================== --- lib/Object/WasmObjectFile.cpp +++ lib/Object/WasmObjectFile.cpp @@ -28,6 +28,8 @@ #include #include +#define DEBUG_TYPE "wasm-object" + using namespace llvm; using namespace object; @@ -256,6 +258,7 @@ while (Ptr < End) { uint8_t Type = readVarint7(Ptr); uint32_t Size = readVaruint32(Ptr); + const uint8_t *SubSectionEnd = Ptr + Size; switch (Type) { case wasm::WASM_NAMES_FUNCTION: { uint32_t Count = readVaruint32(Ptr); @@ -275,6 +278,9 @@ Ptr += Size; break; } + if (Ptr != SubSectionEnd) + return make_error("Name sub-section ended prematurely", + object_error::parse_failed); } if (Ptr != End) @@ -283,6 +289,51 @@ return Error::success(); } +Error WasmObjectFile::parseLinkingSection(const uint8_t *Ptr, + const uint8_t *End) { + while (Ptr < End) { + uint8_t Type = readVarint7(Ptr); + uint32_t Size = readVaruint32(Ptr); + const uint8_t *SubSectionEnd = Ptr + Size; + switch (Type) { + case wasm::WASM_SYMBOL_INFO: { + uint32_t Count = readVaruint32(Ptr); + while (Count--) { + uint8_t Import = readVaruint1(Ptr); + uint32_t Index = readVaruint32(Ptr); + if ((Import && Index >= Imports.size()) || + (!Import && Index >= Exports.size())) { + return make_error("Invalid symbol index", + object_error::parse_failed); + } + uint32_t Flags = readVaruint32(Ptr); + if (Import) { + Symbols[Imports[Index].Symbol].Flags = Flags; + DEBUG(dbgs() << "Set symbol flags: " + << Symbols[Imports[Index].Symbol] << "\n"); + } else { + Symbols[Exports[Index].Symbol].Flags = Flags; + DEBUG(dbgs() << "Set symbol flags: " + << Symbols[Exports[Index].Symbol] << "\n"); + } + } + break; + } + case wasm::WASM_STACK_POINTER: + default: + Ptr += Size; + break; + } + if (Ptr != SubSectionEnd) + return make_error( + "Linking sub-section ended prematurely", object_error::parse_failed); + } + if (Ptr != End) + return make_error("Linking section ended prematurely", + object_error::parse_failed); + return Error::success(); +} + WasmSection* WasmObjectFile::findCustomSectionByName(StringRef Name) { for (WasmSection& Section : Sections) { if (Section.Type == wasm::WASM_SEC_CUSTOM && Section.Name == Name) @@ -351,6 +402,9 @@ if (Sec.Name == "name") { if (Error Err = parseNameSection(Ptr, End)) return Err; + } else if (Sec.Name == "linking") { + if (Error Err = parseLinkingSection(Ptr, End)) + return Err; } else if (Sec.Name.startswith("reloc.")) { if (Error Err = parseRelocSection(Sec.Name, Ptr, End)) return Err; @@ -395,28 +449,30 @@ uint32_t Count = readVaruint32(Ptr); Imports.reserve(Count); for (uint32_t i = 0; i < Count; i++) { - wasm::WasmImport Im; - Im.Module = readString(Ptr); - Im.Field = readString(Ptr); - Im.Kind = readUint8(Ptr); - switch (Im.Kind) { + WasmImportSym Im; + Im.Import.Module = readString(Ptr); + Im.Import.Field = readString(Ptr); + Im.Import.Kind = readUint8(Ptr); + switch (Im.Import.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: - Im.SigIndex = readVaruint32(Ptr); - Symbols.emplace_back(Im.Field, WasmSymbol::SymbolType::FUNCTION_IMPORT, + Im.Import.SigIndex = readVaruint32(Ptr); + Symbols.emplace_back(Im.Import.Field, WasmSymbol::SymbolType::FUNCTION_IMPORT, Sections.size(), i); + Im.Symbol = Symbols.size() - 1; break; case wasm::WASM_EXTERNAL_GLOBAL: - Im.Global.Type = readVarint7(Ptr); - Im.Global.Mutable = readVaruint1(Ptr); - Symbols.emplace_back(Im.Field, WasmSymbol::SymbolType::GLOBAL_IMPORT, + Im.Import.Global.Type = readVarint7(Ptr); + Im.Import.Global.Mutable = readVaruint1(Ptr); + Symbols.emplace_back(Im.Import.Field, WasmSymbol::SymbolType::GLOBAL_IMPORT, Sections.size(), i); + Im.Symbol = Symbols.size() - 1; break; case wasm::WASM_EXTERNAL_MEMORY: - Im.Memory = readLimits(Ptr); + Im.Import.Memory = readLimits(Ptr); break; case wasm::WASM_EXTERNAL_TABLE: - Im.Table = readTable(Ptr); - if (Im.Table.ElemType != wasm::WASM_TYPE_ANYFUNC) { + Im.Import.Table = readTable(Ptr); + if (Im.Import.Table.ElemType != wasm::WASM_TYPE_ANYFUNC) { return make_error("Invalid table element type", object_error::parse_failed); } @@ -494,19 +550,21 @@ uint32_t Count = readVaruint32(Ptr); Exports.reserve(Count); for (uint32_t i = 0; i < Count; i++) { - wasm::WasmExport Ex; - Ex.Name = readString(Ptr); - Ex.Kind = readUint8(Ptr); - Ex.Index = readVaruint32(Ptr); - Exports.push_back(Ex); - switch (Ex.Kind) { + WasmExportSym Ex; + Ex.Export.Name = readString(Ptr); + Ex.Export.Kind = readUint8(Ptr); + Ex.Export.Index = readVaruint32(Ptr); + switch (Ex.Export.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: - Symbols.emplace_back(Ex.Name, WasmSymbol::SymbolType::FUNCTION_EXPORT, + Symbols.emplace_back(Ex.Export.Name, WasmSymbol::SymbolType::FUNCTION_EXPORT, Sections.size(), i); + Ex.Symbol = Symbols.size() - 1; + DEBUG(dbgs() << "Adding export with sym: " << Symbols[Ex.Symbol] << "\n"); break; case wasm::WASM_EXTERNAL_GLOBAL: - Symbols.emplace_back(Ex.Name, WasmSymbol::SymbolType::GLOBAL_EXPORT, + Symbols.emplace_back(Ex.Export.Name, WasmSymbol::SymbolType::GLOBAL_EXPORT, Sections.size(), i); + Ex.Symbol = Symbols.size() - 1; break; case wasm::WASM_EXTERNAL_MEMORY: case wasm::WASM_EXTERNAL_TABLE: @@ -515,6 +573,7 @@ return make_error( "Unexpected export kind", object_error::parse_failed); } + Exports.push_back(Ex); } if (Ptr != End) return make_error("Export section ended prematurely", @@ -622,6 +681,11 @@ uint32_t Result = SymbolRef::SF_None; const WasmSymbol &Sym = getWasmSymbol(Symb); + DEBUG(dbgs() << "getSymbolFlags: ptr=" << &Sym << " " << Sym << "\n"); + if (Sym.Flags & wasm::WASM_SYMBOL_FLAG_WEAK) + Result |= SymbolRef::SF_Weak; + + switch (Sym.Type) { case WasmSymbol::SymbolType::FUNCTION_IMPORT: Result |= SymbolRef::SF_Undefined | SymbolRef::SF_Executable; @@ -631,6 +695,7 @@ break; case WasmSymbol::SymbolType::DEBUG_FUNCTION_NAME: Result |= SymbolRef::SF_Executable; + Result |= SymbolRef::SF_FormatSpecific; break; case WasmSymbol::SymbolType::GLOBAL_IMPORT: Result |= SymbolRef::SF_Undefined; @@ -643,16 +708,18 @@ return Result; } -basic_symbol_iterator WasmObjectFile::symbol_begin() const { +BasicSymbolRef WasmObjectFile::makeSymbolRef(uint32_t Index) const { DataRefImpl Ref; - Ref.d.a = 0; + Ref.d.a = Index; return BasicSymbolRef(Ref, this); } +basic_symbol_iterator WasmObjectFile::symbol_begin() const { + return makeSymbolRef(0); +} + basic_symbol_iterator WasmObjectFile::symbol_end() const { - DataRefImpl Ref; - Ref.d.a = Symbols.size(); - return BasicSymbolRef(Ref, this); + return makeSymbolRef(Symbols.size()); } const WasmSymbol &WasmObjectFile::getWasmSymbol(const DataRefImpl &Symb) const { @@ -664,8 +731,7 @@ } Expected WasmObjectFile::getSymbolName(DataRefImpl Symb) const { - const WasmSymbol &Sym = getWasmSymbol(Symb); - return Sym.Name; + return getWasmSymbol(Symb).Name; } Expected WasmObjectFile::getSymbolAddress(DataRefImpl Symb) const { @@ -673,8 +739,7 @@ } uint64_t WasmObjectFile::getSymbolValueImpl(DataRefImpl Symb) const { - const WasmSymbol &Sym = getWasmSymbol(Symb); - return Sym.ElementIndex; + return getWasmSymbol(Symb).ElementIndex; } uint32_t WasmObjectFile::getSymbolAlignment(DataRefImpl Symb) const { Index: tools/obj2yaml/wasm2yaml.cpp =================================================================== --- tools/obj2yaml/wasm2yaml.cpp +++ tools/obj2yaml/wasm2yaml.cpp @@ -100,22 +100,22 @@ auto ImportSec = make_unique(); for (auto &Import : Obj.imports()) { WasmYAML::Import Im; - Im.Module = Import.Module; - Im.Field = Import.Field; - Im.Kind = Import.Kind; + Im.Module = Import.Import.Module; + Im.Field = Import.Import.Field; + Im.Kind = Import.Import.Kind; switch (Im.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: - Im.SigIndex = Import.SigIndex; + Im.SigIndex = Import.Import.SigIndex; break; case wasm::WASM_EXTERNAL_GLOBAL: - Im.GlobalImport.Type = Import.Global.Type; - Im.GlobalImport.Mutable = Import.Global.Mutable; + Im.GlobalImport.Type = Import.Import.Global.Type; + Im.GlobalImport.Mutable = Import.Import.Global.Mutable; break; case wasm::WASM_EXTERNAL_TABLE: - Im.TableImport = make_table(Import.Table); + Im.TableImport = make_table(Import.Import.Table); break; case wasm::WASM_EXTERNAL_MEMORY: - Im.Memory = make_limits(Import.Memory); + Im.Memory = make_limits(Import.Import.Memory); break; } ImportSec->Imports.push_back(Im); @@ -169,9 +169,9 @@ auto ExportSec = make_unique(); for (auto &Export : Obj.exports()) { WasmYAML::Export Ex; - Ex.Name = Export.Name; - Ex.Kind = Export.Kind; - Ex.Index = Export.Index; + Ex.Name = Export.Export.Name; + Ex.Kind = Export.Export.Kind; + Ex.Index = Export.Export.Index; ExportSec->Exports.push_back(Ex); } S = std::move(ExportSec);