[lld] [lld][WebAssembly] Report undefined symbols by default -shared/-pie builds (PR #75242)
Sam Clegg via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 20 12:21:27 PST 2023
https://github.com/sbc100 updated https://github.com/llvm/llvm-project/pull/75242
>From 65c0942366cb1268cc0c7e2890c7918cacdce4aa Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc at chromium.org>
Date: Tue, 28 Nov 2023 11:52:17 -0800
Subject: [PATCH] [lld][WebAssembly] Report undefined symbols by default
-shared/-pie builds
Previously we would ignore/import all undefined symbols when using
`-shared` or `-pie`. With this change we now track symbol in shared
libraries and report undefined symbols by default.
This can be a breaking change for any users of dyanmic linking.
However, the old behaviour is still available using
`--unresolved-symbols=import-dynamic`. This rationale for allowing this
type of breaking change is that `-pie` and `-shared` are both still
experimental will warn as such, unless `--experimental-pic` is passed.
See https://github.com/emscripten-core/emscripten/issues/18198
---
lld/test/wasm/Inputs/ret32.s | 1 -
lld/test/wasm/dylink.s | 30 ++++++++++++
lld/test/wasm/emit-relocs.s | 2 +-
lld/test/wasm/pie.s | 6 +--
lld/test/wasm/shared-needed.s | 30 ++++++++----
lld/test/wasm/shared.s | 6 +--
lld/test/wasm/shared64.s | 2 +-
lld/test/wasm/signature-mismatch.s | 2 +-
lld/test/wasm/tag-section.ll | 2 +-
lld/test/wasm/undef-shared.s | 12 +++++
lld/test/wasm/undefined-data.s | 2 +-
lld/test/wasm/unresolved-symbols.s | 2 +-
lld/wasm/InputFiles.cpp | 43 +++++++++++++++--
lld/wasm/InputFiles.h | 29 ++++++++----
lld/wasm/MarkLive.cpp | 5 +-
lld/wasm/Relocations.cpp | 13 +++--
lld/wasm/SymbolTable.cpp | 76 ++++++++++++++++++++++++++++++
lld/wasm/SymbolTable.h | 3 ++
lld/wasm/Symbols.cpp | 7 ++-
lld/wasm/Symbols.h | 26 +++++++++-
lld/wasm/SyntheticSections.cpp | 4 +-
lld/wasm/Writer.cpp | 8 +++-
22 files changed, 263 insertions(+), 48 deletions(-)
create mode 100644 lld/test/wasm/dylink.s
create mode 100644 lld/test/wasm/undef-shared.s
diff --git a/lld/test/wasm/Inputs/ret32.s b/lld/test/wasm/Inputs/ret32.s
index 5233455917e670..009f28c8cc9b88 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 00000000000000..5326025edee018
--- /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 at 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 91de6116164f72..bd136ba810b5ef 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 887377043e555c..21eac792073187 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 12c4597190a3b5..0176da832d2b19 100644
--- a/lld/test/wasm/shared-needed.s
+++ b/lld/test/wasm/shared-needed.s
@@ -1,17 +1,27 @@
# 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
+
+# With 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 +34,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 +44,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 a26f00163fea70..5b40d4ebee7abc 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 3401faed8610c1..fba73f187679a6 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 5d305efca24649..e9da3073dde87c 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 4decdb58f952a9..20823c72c65116 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 00000000000000..4c270880ef531d
--- /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 d63b667c4ea38b..5e2a41606612ab 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 5de54d76c6de81..ebb5acc6876c9d 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 96ac1e1610dd3b..6ef4769beed695 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<UndefinedData>(sym) || sym->isUndefWeak())
+ if (isa<UndefinedData>(sym) || sym->isShared() || sym->isUndefWeak())
return 0;
auto D = cast<DefinedData>(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<Binary> bin = CHECK(createBinary(mb), toString(this));
@@ -399,13 +428,18 @@ void ObjFile::parse(bool ignoreComdats) {
auto *obj = dyn_cast<WasmObjectFile>(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<FunctionSymbol>(symbols[index]));
return cast<FunctionSymbol>(symbols[index]);
}
diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h
index d9a8b530660324..df6d4ef7d05629 100644
--- a/lld/wasm/InputFiles.h
+++ b/lld/wasm/InputFiles.h
@@ -100,23 +100,33 @@ class ArchiveFile : public InputFile {
llvm::DenseSet<uint64_t> 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<WasmObjectFile> 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<WasmObjectFile> 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 a59a80ad2cc3a7..43b484e3aacc03 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<ObjFile>(file));
+ if (auto obj = dyn_cast<ObjFile>(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 ce41cdcb3e07f4..5d9916c5a87441 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 76370525c37199..d3c45691fa0f31 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<SharedFile>(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<SharedFunctionSymbol>(sym, name, flags, file, sig);
+ };
+
+ if (wasInserted) {
+ replaceSym(s);
+ return s;
+ }
+
+ auto existingFunction = dyn_cast<FunctionSymbol>(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<UndefinedFunction>(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<SharedData>(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 59eda1c0b6740c..52214496c2bb2d 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 2adf72b6965ca8..6da453c263446a 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 34fff4b962bdcc..05c93159798077 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 3a9c147161a2d3..5adb0023451d6a 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<UndefinedData>(sym));
+ assert(isa<UndefinedData>(sym) || isa<SharedData>(sym));
continue;
}
writeU8(os, opcode_ptr_add, "ADD");
@@ -519,7 +519,7 @@ void GlobalSection::writeBody() {
else if (auto *f = dyn_cast<FunctionSymbol>(sym))
initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
else {
- assert(isa<UndefinedData>(sym));
+ assert(isa<UndefinedData>(sym) || isa<SharedData>(sym));
initExpr = intConst(0, is64);
}
writeInitExpr(os, initExpr);
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 805018c58dccb4..81275ecd02fcbd 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<SharedFunctionSymbol>(sym) || sym->isShared())
+ continue;
StringRef name = sym->getName();
+ LLVM_DEBUG(dbgs() << "Export: " << name << "\n");
WasmExport export_;
if (auto *f = dyn_cast<DefinedFunction>(sym)) {
if (std::optional<StringRef> 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) {
More information about the llvm-commits
mailing list