diff --git a/lld/test/wasm/Inputs/ret32.s b/lld/test/wasm/Inputs/ret32.s index 5233455917e67..009f28c8cc9b8 100644 --- a/lld/test/wasm/Inputs/ret32.s +++ b/lld/test/wasm/Inputs/ret32.s @@ -1,4 +1,3 @@ - .hidden ret32 .globl ret32 ret32: .functype ret32 (f32) -> (i32) diff --git a/lld/test/wasm/dylink.s b/lld/test/wasm/dylink.s new file mode 100644 index 0000000000000..5326025edee01 --- /dev/null +++ b/lld/test/wasm/dylink.s @@ -0,0 +1,30 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/ret32.s -o %t.ret32.o +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %p/Inputs/libsearch-dyn.s -o %t.dyn.o +# RUN: wasm-ld --experimental-pic -shared %t.ret32.o %t.dyn.o -o %t.lib.so +# RUN: not wasm-ld --experimental-pic -pie -o %t.wasm %t.o 2>&1 | FileCheck --check-prefix=ERROR %s +# RUN: wasm-ld --experimental-pic -pie -o %t.wasm %t.o %t.lib.so +# RUN: obj2yaml %t.wasm | FileCheck %s + +# ERROR: error: {{.*}}: undefined symbol: ret32 +.functype ret32 (f32) -> (i32) + +.globl _start +_start: + .functype _start () -> () + f32.const 0.0 + call ret32 + drop + i32.const _dynamic@GOT + drop + end_function + +# 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: - {{.*}}.lib.so diff --git a/lld/test/wasm/emit-relocs.s b/lld/test/wasm/emit-relocs.s index 91de6116164f7..bd136ba810b5e 100644 --- a/lld/test/wasm/emit-relocs.s +++ b/lld/test/wasm/emit-relocs.s @@ -54,7 +54,7 @@ foo: # CHECK-NEXT: - Index: 1 # CHECK-NEXT: Kind: FUNCTION # CHECK-NEXT: Name: ret32 -# CHECK-NEXT: Flags: [ VISIBILITY_HIDDEN ] +# CHECK-NEXT: Flags: [ ] # CHECK-NEXT: Function: 1 # CHECK-NEXT: - Index: 2 # CHECK-NEXT: Kind: DATA diff --git a/lld/test/wasm/pie.s b/lld/test/wasm/pie.s index 887377043e555..21eac79207318 100644 --- a/lld/test/wasm/pie.s +++ b/lld/test/wasm/pie.s @@ -1,6 +1,6 @@ # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten -o %t.o %s # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-emscripten %S/Inputs/internal_func.s -o %t.internal_func.o -# RUN: wasm-ld --no-gc-sections --experimental-pic -pie -o %t.wasm %t.o %t.internal_func.o +# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --unresolved-symbols=import-dynamic -o %t.wasm %t.o %t.internal_func.o # RUN: obj2yaml %t.wasm | FileCheck %s # RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DISASSEM @@ -150,7 +150,7 @@ _start: # instruction in the InitExpr. We also, therefore, do not need these globals # to be mutable. -# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --extra-features=extended-const -o %t.extended.wasm %t.o %t.internal_func.o +# RUN: wasm-ld --no-gc-sections --experimental-pic -pie --unresolved-symbols=import-dynamic --extra-features=extended-const -o %t.extended.wasm %t.o %t.internal_func.o # RUN: obj2yaml %t.extended.wasm | FileCheck %s --check-prefix=EXTENDED-CONST # EXTENDED-CONST-NOT: __wasm_apply_global_relocs @@ -207,7 +207,7 @@ _start: # to be generated along with __wasm_start as the start # function. -# RUN: wasm-ld --no-gc-sections --shared-memory --experimental-pic -pie -o %t.shmem.wasm %t.o %t.internal_func.o +# RUN: wasm-ld --no-gc-sections --shared-memory --experimental-pic -pie --unresolved-symbols=import-dynamic -o %t.shmem.wasm %t.o %t.internal_func.o # RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM # RUN: llvm-objdump --disassemble-symbols=__wasm_start --no-show-raw-insn --no-leading-addr %t.shmem.wasm | FileCheck %s --check-prefix DISASSEM-SHMEM diff --git a/lld/test/wasm/shared-needed.s b/lld/test/wasm/shared-needed.s index 12c4597190a3b..c79c56e185abd 100644 --- a/lld/test/wasm/shared-needed.s +++ b/lld/test/wasm/shared-needed.s @@ -1,17 +1,28 @@ # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/ret32.s -o %t.ret32.o -# RUN: wasm-ld -shared --experimental-pic -o %t1.so %t.o -# RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1 +# RUN: wasm-ld -shared --experimental-pic -o %t.ret32.so %t.ret32.o +# RUN: obj2yaml %t.ret32.so | FileCheck %s -check-prefix=SO1 + +# Without linking against the ret32.so shared object we expect and undefined +# symbol error + +# RUN: not wasm-ld -shared --experimental-pic -o %t.so %t.o 2>&1 | FileCheck %s --check-prefix=ERROR +# ERROR: undefined symbol: ret32 + +# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o %t.ret32.so +# RUN: obj2yaml %t.so | FileCheck %s -check-prefix=SO2 -# RUN: wasm-ld -shared --experimental-pic -o %t2.so %t1.so %t.ret32.o -# RUN: obj2yaml %t2.so | FileCheck %s -check-prefix=SO2 .globl foo .globl data +.functype ret32 (f32) -> (i32) + foo: - .functype foo () -> () + .functype foo (f32) -> (i32) + local.get 0 + call ret32 end_function .section .data,"",@ @@ -24,8 +35,8 @@ data: # SO1: Sections: # SO1-NEXT: - Type: CUSTOM # SO1-NEXT: Name: dylink.0 -# SO1-NEXT: MemorySize: 4 -# SO1-NEXT: MemoryAlignment: 2 +# SO1-NEXT: MemorySize: 0 +# SO1-NEXT: MemoryAlignment: 0 # SO1-NEXT: TableSize: 0 # SO1-NEXT: TableAlignment: 0 # SO1-NEXT: Needed: [] @@ -34,10 +45,10 @@ data: # SO2: Sections: # SO2-NEXT: - Type: CUSTOM # SO2-NEXT: Name: dylink.0 -# SO2-NEXT: MemorySize: 0 -# SO2-NEXT: MemoryAlignment: 0 +# SO2-NEXT: MemorySize: 4 +# SO2-NEXT: MemoryAlignment: 2 # SO2-NEXT: TableSize: 0 # SO2-NEXT: TableAlignment: 0 # SO2-NEXT: Needed: -# SO2-NEXT: - shared-needed.s.tmp1.so +# SO2-NEXT: - shared-needed.s.tmp.ret32.so # SO2-NEXT: - Type: TYPE diff --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s index a26f00163fea7..5b40d4ebee7ab 100644 --- a/lld/test/wasm/shared.s +++ b/lld/test/wasm/shared.s @@ -1,5 +1,5 @@ # 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: wasm-ld --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o # RUN: obj2yaml %t.wasm | FileCheck %s # RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS @@ -7,8 +7,8 @@ # Linker-synthesized globals .globaltype __stack_pointer, i32 -.globaltype __table_base, i32, immutable -.globaltype __memory_base, i32, immutable +.globaltype __table_base, i32, immutable +.globaltype __memory_base, i32, immutable .section .data.data,"",@ data: diff --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s index 3401faed8610c..fba73f187679a 100644 --- a/lld/test/wasm/shared64.s +++ b/lld/test/wasm/shared64.s @@ -1,5 +1,5 @@ # RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s -# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o +# RUN: wasm-ld -mwasm64 --experimental-pic --unresolved-symbols=import-dynamic -shared -o %t.wasm %t.o # RUN: obj2yaml %t.wasm | FileCheck %s # RUN: llvm-objdump --disassemble-symbols=__wasm_call_ctors,__wasm_apply_data_relocs --no-show-raw-insn --no-leading-addr %t.wasm | FileCheck %s --check-prefixes DIS diff --git a/lld/test/wasm/signature-mismatch.s b/lld/test/wasm/signature-mismatch.s index 5d305efca2464..e9da3073dde87 100644 --- a/lld/test/wasm/signature-mismatch.s +++ b/lld/test/wasm/signature-mismatch.s @@ -84,7 +84,7 @@ ret32_address_main: # RELOC-NEXT: - Index: 1 # RELOC-NEXT: Kind: FUNCTION # RELOC-NEXT: Name: ret32 -# RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ] +# RELOC-NEXT: Flags: [ ] # RELOC-NEXT: Function: 2 # RELOC-NEXT: - Index: 2 # RELOC-NEXT: Kind: DATA diff --git a/lld/test/wasm/tag-section.ll b/lld/test/wasm/tag-section.ll index 4decdb58f952a..20823c72c6511 100644 --- a/lld/test/wasm/tag-section.ll +++ b/lld/test/wasm/tag-section.ll @@ -11,7 +11,7 @@ ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section1.ll -o %t1.o ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %p/Inputs/tag-section2.ll -o %t2.o ; RUN: llc -filetype=obj -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -relocation-model=pic %s -o %t.o -; RUN: wasm-ld --import-undefined --experimental-pic -pie -o %t.wasm %t.o %t1.o %t2.o +; RUN: wasm-ld --import-undefined --experimental-pic --unresolved-symbols=import-dynamic -pie -o %t.wasm %t.o %t1.o %t2.o ; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefix=PIC target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" diff --git a/lld/test/wasm/undef-shared.s b/lld/test/wasm/undef-shared.s new file mode 100644 index 0000000000000..4c270880ef531 --- /dev/null +++ b/lld/test/wasm/undef-shared.s @@ -0,0 +1,12 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o +# RUN: not wasm-ld --experimental-pic %t.o -o /dev/null -shared 2>&1 | FileCheck %s + +# CHECK: error: {{.*}}: undefined symbol: hidden +.global hidden +.hidden hidden + +.global foo +.section .data,"",@ +foo: + .int32 hidden + .size foo,4 diff --git a/lld/test/wasm/undefined-data.s b/lld/test/wasm/undefined-data.s index d63b667c4ea38..5e2a41606612a 100644 --- a/lld/test/wasm/undefined-data.s +++ b/lld/test/wasm/undefined-data.s @@ -1,7 +1,7 @@ # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s # RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF # RUN: wasm-ld --allow-undefined -o %t.wasm %t.o -# RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED +# RUN: not wasm-ld --experimental-pic -shared --unresolved-symbols=import-dynamic -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED .globl _start _start: diff --git a/lld/test/wasm/unresolved-symbols.s b/lld/test/wasm/unresolved-symbols.s index 5de54d76c6de8..ebb5acc6876c9 100644 --- a/lld/test/wasm/unresolved-symbols.s +++ b/lld/test/wasm/unresolved-symbols.s @@ -80,7 +80,7 @@ .functype get_func_addr () -> (i32) ## import-dynamic should fail due to incompatible relocations. -# RUN: not wasm-ld %t1.o -o %t5.wasm --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s +# RUN: not wasm-ld %t1.o -o %t5.wasm --experimental-pic --unresolved-symbols=import-dynamic 2>&1 | FileCheck -check-prefix=ERRNOPIC %s # ERRNOPIC: relocation R_WASM_MEMORY_ADDR_SLEB cannot be used against symbol `undef_data`; recompile with -fPIC # ERRNOPIC: relocation R_WASM_TABLE_INDEX_SLEB cannot be used against symbol `undef_func`; recompile with -fPIC diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index 96ac1e1610dd3..6ef4769beed69 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -178,7 +178,7 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, case R_WASM_MEMORY_ADDR_TLS_SLEB: case R_WASM_MEMORY_ADDR_TLS_SLEB64: case R_WASM_MEMORY_ADDR_LOCREL_I32: { - if (isa(sym) || sym->isUndefWeak()) + if (isa(sym) || sym->isShared() || sym->isUndefWeak()) return 0; auto D = cast(sym); uint64_t value = D->getVA() + reloc.Addend; @@ -391,7 +391,36 @@ static bool shouldMerge(const WasmSegment &seg) { return true; } -void ObjFile::parse(bool ignoreComdats) { +void SharedFile::parse() { + WasmFileBase::parse(); + assert(wasmObj->isSharedObject()); + + for (const SymbolRef &sym : wasmObj->symbols()) { + const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl()); + if (wasmSym.isDefined()) { + StringRef name = wasmSym.Info.Name; + uint32_t flags = wasmSym.Info.Flags; + Symbol *s; + LLVM_DEBUG(dbgs() << "shared symbol: " << name << "\n"); + switch (wasmSym.Info.Kind) { + case WASM_SYMBOL_TYPE_FUNCTION: + if (name == "__wasm_apply_data_relocs" || name == "__wasm_call_ctors") { + continue; + } + s = symtab->addSharedFunction(name, flags, this, wasmSym.Signature); + break; + case WASM_SYMBOL_TYPE_DATA: + s = symtab->addSharedData(name, flags, this); + break; + default: + continue; + } + symbols.push_back(s); + } + } +} + +void WasmFileBase::parse() { // Parse a memory buffer as a wasm file. LLVM_DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n"); std::unique_ptr bin = CHECK(createBinary(mb), toString(this)); @@ -399,13 +428,18 @@ void ObjFile::parse(bool ignoreComdats) { auto *obj = dyn_cast(bin.get()); if (!obj) fatal(toString(this) + ": not a wasm file"); - if (!obj->isRelocatableObject()) - fatal(toString(this) + ": not a relocatable wasm file"); bin.release(); wasmObj.reset(obj); checkArch(obj->getArch()); +} + +void ObjFile::parse(bool ignoreComdats) { + WasmFileBase::parse(); + + if (!wasmObj->isRelocatableObject()) + fatal(toString(this) + ": not a relocatable wasm file"); // Build up a map of function indices to table indices for use when // verifying the existing table index relocations @@ -548,6 +582,7 @@ bool ObjFile::isExcludedByComdat(const InputChunk *chunk) const { } FunctionSymbol *ObjFile::getFunctionSymbol(uint32_t index) const { + assert(isa(symbols[index])); return cast(symbols[index]); } diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h index d9a8b53066032..df6d4ef7d0562 100644 --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -100,23 +100,33 @@ class ArchiveFile : public InputFile { llvm::DenseSet seen; }; +class WasmFileBase : public InputFile { +public: + explicit WasmFileBase(Kind k, MemoryBufferRef m) : InputFile(k, m) {} + + // Returns the underlying wasm file. + const WasmObjectFile *getWasmObj() const { return wasmObj.get(); } + +protected: + void parse(); + std::unique_ptr wasmObj; +}; + // .o file (wasm object file) -class ObjFile : public InputFile { +class ObjFile : public WasmFileBase { public: explicit ObjFile(MemoryBufferRef m, StringRef archiveName) - : InputFile(ObjectKind, m) { + : WasmFileBase(ObjectKind, m) { this->archiveName = std::string(archiveName); // If this isn't part of an archive, it's eagerly linked, so mark it live. if (archiveName.empty()) markLive(); } - static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse(bool ignoreComdats = false); - // Returns the underlying wasm file. - const WasmObjectFile *getWasmObj() const { return wasmObj.get(); } + static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } uint32_t calcNewIndex(const WasmRelocation &reloc) const; uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, @@ -158,14 +168,15 @@ class ObjFile : public InputFile { bool isExcludedByComdat(const InputChunk *chunk) const; void addLegacyIndirectFunctionTableIfNeeded(uint32_t tableSymbolCount); - - std::unique_ptr wasmObj; }; // .so file. -class SharedFile : public InputFile { +class SharedFile : public WasmFileBase { public: - explicit SharedFile(MemoryBufferRef m) : InputFile(SharedKind, m) {} + explicit SharedFile(MemoryBufferRef m) : WasmFileBase(SharedKind, m) {} + + void parse(); + static bool classof(const InputFile *f) { return f->kind() == SharedKind; } }; diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp index a59a80ad2cc3a..43b484e3aacc0 100644 --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -60,12 +60,13 @@ void MarkLive::enqueue(Symbol *sym) { sym->markLive(); - // Mark ctor functions in the object that defines this symbol live. + // Mark as live the ctor functions in the object that defines this symbol. // The ctor functions are all referenced by the synthetic callCtors // function. However, this function does not contain relocations so we // have to manually mark the ctors as live. if (needInitFunctions) - enqueueInitFunctions(cast(file)); + if (auto obj = dyn_cast(file)) + enqueueInitFunctions(obj); if (InputChunk *chunk = sym->getChunk()) queue.push_back(chunk); diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp index ce41cdcb3e07f..5d9916c5a8744 100644 --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -19,6 +19,8 @@ using namespace llvm::wasm; namespace lld::wasm { static bool requiresGOTAccess(const Symbol *sym) { + if (sym->isShared()) + return true; if (!config->isPic && config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic) return false; @@ -141,9 +143,12 @@ void scanRelocations(InputChunk *chunk) { break; } - if (config->isPic || + bool shouldImport = + sym->isShared() || (sym->isUndefined() && - config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) { + config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic); + + if (shouldImport) { switch (reloc.Type) { case R_WASM_TABLE_INDEX_SLEB: case R_WASM_TABLE_INDEX_SLEB64: @@ -162,8 +167,8 @@ void scanRelocations(InputChunk *chunk) { case R_WASM_MEMORY_ADDR_I32: case R_WASM_MEMORY_ADDR_I64: // These relocation types are only present in the data section and - // will be converted into code by `generateRelocationCode`. This code - // requires the symbols to have GOT entries. + // will be converted into code by `generateRelocationCode`. This + // code requires the symbols to have GOT entries. if (requiresGOTAccess(sym)) addGOTEntry(sym); break; diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 76370525c3719..7fef757c1b125 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -34,6 +34,7 @@ void SymbolTable::addFile(InputFile *file, StringRef symName) { // .so file if (auto *f = dyn_cast(file)) { + f->parse(); sharedFiles.push_back(f); return; } @@ -305,6 +306,12 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile, return true; } + // Similarly with shared symbols + if (existing->isShared()) { + LLVM_DEBUG(dbgs() << "replacing existing shared symbol\n"); + return true; + } + // Neither symbol is week. They conflict. error("duplicate symbol: " + toString(*existing) + "\n>>> defined in " + toString(existing->getFile()) + "\n>>> defined in " + @@ -312,6 +319,75 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile, return true; } +Symbol *SymbolTable::addSharedFunction(StringRef name, uint32_t flags, + InputFile *file, + const WasmSignature *sig) { + LLVM_DEBUG(dbgs() << "addSharedFunction: " << name << " [" << toString(*sig) + << "]\n"); + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, file); + + auto replaceSym = [&](Symbol *sym) { + replaceSymbol(sym, name, flags, file, sig); + }; + + if (wasInserted) { + replaceSym(s); + return s; + } + + auto existingFunction = dyn_cast(s); + if (!existingFunction) { + reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION); + return s; + } + + // Shared symbols should never deplace locally-defined ones + if (s->isDefined()) { + return s; + } + + LLVM_DEBUG(dbgs() << "resolving existing undefined symbol: " << s->getName() + << "\n"); + + bool checkSig = true; + if (auto ud = dyn_cast(existingFunction)) + checkSig = ud->isCalledDirectly; + + if (checkSig && !signatureMatches(existingFunction, sig)) { + Symbol *variant; + if (getFunctionVariant(s, sig, file, &variant)) + // New variant, always replace + replaceSym(variant); + else + // Variant already exists, replace it + replaceSym(variant); + + // This variant we found take the place in the symbol table as the primary + // variant. + replace(name, variant); + return variant; + } + + replaceSym(s); + return s; +} + +Symbol *SymbolTable::addSharedData(StringRef name, uint32_t flags, + InputFile *file) { + LLVM_DEBUG(dbgs() << "addSharedData: " << name << "\n"); + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(name, file); + + if (wasInserted || s->isUndefined()) { + replaceSymbol(s, name, flags, file); + } + + return s; +} + Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags, InputFile *file, InputFunction *function) { diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h index 59eda1c0b6740..52214496c2bb2 100644 --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -50,6 +50,9 @@ class SymbolTable { void trace(StringRef name); + Symbol *addSharedFunction(StringRef name, uint32_t flags, InputFile *file, + const WasmSignature *sig); + Symbol *addSharedData(StringRef name, uint32_t flags, InputFile *file); Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file, InputFunction *function); Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file, diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 2adf72b6965ca..6da453c263446 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -67,6 +67,10 @@ std::string toString(wasm::Symbol::Kind kind) { return "SectionKind"; case wasm::Symbol::OutputSectionKind: return "OutputSectionKind"; + case wasm::Symbol::SharedFunctionKind: + return "SharedFunctionKind"; + case wasm::Symbol::SharedDataKind: + return "SharedDataKind"; } llvm_unreachable("invalid symbol kind"); } @@ -222,7 +226,8 @@ void Symbol::setHidden(bool isHidden) { } bool Symbol::isImported() const { - return isUndefined() && (importName.has_value() || forceImport); + return isShared() || + (isUndefined() && (importName.has_value() || forceImport)); } bool Symbol::isExported() const { diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index 34fff4b962bdc..05c9315979807 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -60,6 +60,8 @@ class Symbol { UndefinedTableKind, UndefinedTagKind, LazyKind, + SharedFunctionKind, + SharedDataKind, }; Kind kind() const { return symbolKind; } @@ -74,6 +76,9 @@ class Symbol { } bool isLazy() const { return symbolKind == LazyKind; } + bool isShared() const { + return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind; + } bool isLocal() const; bool isWeak() const; @@ -190,6 +195,7 @@ class FunctionSymbol : public Symbol { public: static bool classof(const Symbol *s) { return s->kind() == DefinedFunctionKind || + s->kind() == SharedFunctionKind || s->kind() == UndefinedFunctionKind; } @@ -285,7 +291,8 @@ class SectionSymbol : public Symbol { class DataSymbol : public Symbol { public: static bool classof(const Symbol *s) { - return s->kind() == DefinedDataKind || s->kind() == UndefinedDataKind; + return s->kind() == DefinedDataKind || s->kind() == UndefinedDataKind || + s->kind() == SharedDataKind; } protected: @@ -323,6 +330,12 @@ class DefinedData : public DataSymbol { uint64_t size = 0; }; +class SharedData : public DataSymbol { +public: + SharedData(StringRef name, uint32_t flags, InputFile *f) + : DataSymbol(name, SharedDataKind, flags, f) {} +}; + class UndefinedData : public DataSymbol { public: UndefinedData(StringRef name, uint32_t flags, InputFile *file = nullptr) @@ -486,6 +499,16 @@ class UndefinedTag : public TagSymbol { static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; } }; +class SharedFunctionSymbol : public FunctionSymbol { +public: + SharedFunctionSymbol(StringRef name, uint32_t flags, InputFile *file, + const WasmSignature *sig) + : FunctionSymbol(name, SharedFunctionKind, flags, file, sig) {} + static bool classof(const Symbol *s) { + return s->kind() == SharedFunctionKind; + } +}; + // LazySymbol represents a symbol that is not yet in the link, but we know where // to find it if needed. If the resolver finds both Undefined and Lazy for the // same name, it will ask the Lazy to load a file. @@ -640,6 +663,7 @@ union SymbolUnion { alignas(UndefinedGlobal) char i[sizeof(UndefinedGlobal)]; alignas(UndefinedTable) char j[sizeof(UndefinedTable)]; alignas(SectionSymbol) char k[sizeof(SectionSymbol)]; + alignas(SharedFunctionSymbol) char l[sizeof(SharedFunctionSymbol)]; }; // It is important to keep the size of SymbolUnion small for performance and diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp index 3a9c147161a2d..5adb0023451d6 100644 --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -449,7 +449,7 @@ void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const { writeU8(os, opcode_ptr_const, "CONST"); writeSleb128(os, f->getTableIndex(), "offset"); } else { - assert(isa(sym)); + assert(isa(sym) || isa(sym)); continue; } writeU8(os, opcode_ptr_add, "ADD"); @@ -519,7 +519,7 @@ void GlobalSection::writeBody() { else if (auto *f = dyn_cast(sym)) initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64); else { - assert(isa(sym)); + assert(isa(sym) || isa(sym)); initExpr = intConst(0, is64); } writeInitExpr(os, initExpr); diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 805018c58dccb..81275ecd02fcb 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -732,6 +732,8 @@ static bool shouldImport(Symbol *sym) { if (config->shared && sym->isWeak() && !sym->isUndefined() && !sym->isHidden()) return true; + if (sym->isShared()) + return true; if (!sym->isUndefined()) return false; if (sym->isWeak() && !config->relocatable && !config->isPic) @@ -789,8 +791,11 @@ void Writer::calculateExports() { continue; if (!sym->isLive()) continue; + if (isa(sym) || sym->isShared()) + continue; StringRef name = sym->getName(); + LLVM_DEBUG(dbgs() << "Export: " << name << "\n"); WasmExport export_; if (auto *f = dyn_cast(sym)) { if (std::optional exportName = f->function->getExportName()) { @@ -818,7 +823,6 @@ void Writer::calculateExports() { export_ = {name, WASM_EXTERNAL_TABLE, t->getTableNumber()}; } - LLVM_DEBUG(dbgs() << "Export: " << name << "\n"); out.exportSec->exports.push_back(export_); out.exportSec->exportedSymbols.push_back(sym); } @@ -829,7 +833,7 @@ void Writer::populateSymtab() { return; for (Symbol *sym : symtab->symbols()) - if (sym->isUsedInRegularObj && sym->isLive()) + if (sym->isUsedInRegularObj && sym->isLive() && !sym->isShared()) out.linkingSec->addToSymtab(sym); for (ObjFile *file : symtab->objectFiles) {