diff --git a/lld/test/wasm/shared-weak-undefined.s b/lld/test/wasm/shared-weak-undefined.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/shared-weak-undefined.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o +# RUN: obj2yaml %t.wasm | FileCheck %s +# RUN: llvm-objdump -d %t.wasm | FileCheck %s -check-prefix=ASM + +# Verify the weak undefined symbols are marked as such in the +# dylink section. + +.weak weak_func +.functype weak_func () -> (i32) + +.globl call_weak +call_weak: +# ASM: : + .functype call_weak () -> (i32) + call weak_func +# ASM: 10 80 80 80 80 00 call 0 + end_function +# ASM-NEXT: 0b end + +# CHECK: Sections: +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: dylink.0 +# CHECK-NEXT: MemorySize: 0 +# CHECK-NEXT: MemoryAlignment: 0 +# CHECK-NEXT: TableSize: 0 +# CHECK-NEXT: TableAlignment: 0 +# CHECK-NEXT: Needed: [] +# CHECK-NEXT: ImportInfo: +# CHECK-NEXT: - Module: env +# CHECK-NEXT: Field: weak_func +# CHECK-NEXT: Flags: [ BINDING_WEAK, UNDEFINED ] diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -74,15 +74,22 @@ sub.writeTo(os); } - // Under certain circumstances we need to include extra information about the - // exports we are providing to the dynamic linker. Currently this is only the - // case for TLS symbols where the exported value is relative to __tls_base - // rather than __memory_base. + // Under certain circumstances we need to include extra information about our + // exports and/or imports to the dynamic linker. + // For exports we need to notify the linker when an export is TLS since the + // exported value is relative to __tls_base rather than __memory_base. + // For imports we need to notify the dynamic linker when an import is weak + // so that knows not to report an error for such symbols. + std::vector importInfo; std::vector exportInfo; for (const Symbol *sym : symtab->getSymbols()) { - if (sym->isExported() && sym->isLive() && sym->isTLS() && - isa(sym)) { - exportInfo.push_back(sym); + if (sym->isLive()) { + if (sym->isExported() && sym->isTLS() && isa(sym)) { + exportInfo.push_back(sym); + } + if (sym->isUndefWeak()) { + importInfo.push_back(sym); + } } } @@ -104,6 +111,22 @@ sub.writeTo(os); } + + if (!importInfo.empty()) { + SubSection sub(WASM_DYLINK_IMPORT_INFO); + writeUleb128(sub.os, importInfo.size(), "num imports"); + + for (const Symbol *sym : importInfo) { + LLVM_DEBUG(llvm::dbgs() << "imports info: " << toString(*sym) << "\n"); + StringRef module = sym->importModule.getValueOr(defaultModule); + StringRef name = sym->importName.getValueOr(sym->getName()); + writeStr(sub.os, module, "import module"); + writeStr(sub.os, name, "import name"); + writeUleb128(sub.os, sym->flags, "sym flags"); + } + + sub.writeTo(os); + } } uint32_t TypeSection::registerType(const WasmSignature &sig) { diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -36,7 +36,13 @@ uint32_t Version; }; -struct WasmDylinkExport { +struct WasmDylinkImportInfo { + StringRef Module; + StringRef Field; + uint32_t Flags; +}; + +struct WasmDylinkExportInfo { StringRef Name; uint32_t Flags; }; @@ -47,7 +53,8 @@ uint32_t TableSize; // Table size in elements uint32_t TableAlignment; // P2 alignment of table std::vector Needed; // Shared library dependencies - std::vector ExportInfo; // Shared library dependencies + std::vector ImportInfo; + std::vector ExportInfo; }; struct WasmProducerInfo { @@ -346,6 +353,7 @@ WASM_DYLINK_MEM_INFO = 0x1, WASM_DYLINK_NEEDED = 0x2, WASM_DYLINK_EXPORT_INFO = 0x3, + WASM_DYLINK_IMPORT_INFO = 0x4, }; // Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h --- a/llvm/include/llvm/ObjectYAML/WasmYAML.h +++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h @@ -193,7 +193,13 @@ yaml::BinaryRef Payload; }; -struct DylinkExport { +struct DylinkImportInfo { + StringRef Module; + StringRef Field; + SymbolFlags Flags; +}; + +struct DylinkExportInfo { StringRef Name; SymbolFlags Flags; }; @@ -211,7 +217,8 @@ uint32_t TableSize; uint32_t TableAlignment; std::vector Needed; - std::vector ExportInfo; + std::vector ImportInfo; + std::vector ExportInfo; }; struct NameSection : CustomSection { @@ -425,7 +432,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ComdatEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Comdat) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExport) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkImportInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExportInfo) namespace llvm { namespace yaml { @@ -570,8 +578,12 @@ static void enumeration(IO &IO, WasmYAML::RelocType &Kind); }; -template <> struct MappingTraits { - static void mapping(IO &IO, WasmYAML::DylinkExport &Export); +template <> struct MappingTraits { + static void mapping(IO &IO, WasmYAML::DylinkImportInfo &Info); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, WasmYAML::DylinkExportInfo &Info); }; } // end namespace yaml diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -391,6 +391,14 @@ } break; } + case wasm::WASM_DYLINK_IMPORT_INFO: { + uint32_t Count = readVaruint32(Ctx); + while (Count--) { + DylinkInfo.ImportInfo.push_back( + {readString(Ctx), readString(Ctx), readVaruint32(Ctx)}); + } + break; + } default: LLVM_DEBUG(dbgs() << "unknown dylink.0 sub-section: " << Type << "\n"); Ctx.Ptr += Size; diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -55,6 +55,7 @@ IO.mapRequired("TableSize", Section.TableSize); IO.mapRequired("TableAlignment", Section.TableAlignment); IO.mapRequired("Needed", Section.Needed); + IO.mapOptional("ImportInfo", Section.ImportInfo); IO.mapOptional("ExportInfo", Section.ExportInfo); } @@ -524,10 +525,17 @@ } } -void MappingTraits::mapping( - IO &IO, WasmYAML::DylinkExport &Export) { - IO.mapRequired("Name", Export.Name); - IO.mapRequired("Flags", Export.Flags); +void MappingTraits::mapping( + IO &IO, WasmYAML::DylinkImportInfo &Info) { + IO.mapRequired("Module", Info.Module); + IO.mapRequired("Field", Info.Field); + IO.mapRequired("Flags", Info.Flags); +} + +void MappingTraits::mapping( + IO &IO, WasmYAML::DylinkExportInfo &Info) { + IO.mapRequired("Name", Info.Name); + IO.mapRequired("Flags", Info.Flags); } void ScalarBitSetTraits::bitset( diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp --- a/llvm/tools/obj2yaml/wasm2yaml.cpp +++ b/llvm/tools/obj2yaml/wasm2yaml.cpp @@ -60,6 +60,8 @@ DylinkSec->TableSize = Info.TableSize; DylinkSec->TableAlignment = Info.TableAlignment; DylinkSec->Needed = Info.Needed; + for (const auto &Imp : Info.ImportInfo) + DylinkSec->ImportInfo.push_back({Imp.Module, Imp.Field, Imp.Flags}); for (const auto &Exp : Info.ExportInfo) DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags}); CustomSec = std::move(DylinkSec);