[llvm] [lld] Dylink symbols lld (PR #75242)

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 12 12:30:19 PST 2023


https://github.com/sbc100 created https://github.com/llvm/llvm-project/pull/75242

None

>From 731d4e212b44df9d6ed41069f4a81ad23d42a1e4 Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc at chromium.org>
Date: Tue, 12 Dec 2023 11:23:59 -0800
Subject: [PATCH 1/2] [WebAssembly] Add symbol information for shared libraries

The current (experimental) spec for WebAssembly shared libraries does
not include a full symbol table like the object format.  This change
extracts symbol information from the normal wasm exports.

This is the first step in having the linker report undefined symbols
when linking with shared libraries.  The current behaviour is to ignore
all undefined symbols when linking with `-pie` or `-shared`.
---
 llvm/include/llvm/Object/Wasm.h               |  1 -
 llvm/lib/Object/WasmObjectFile.cpp            | 50 ++++++++++++-
 llvm/test/tools/llvm-nm/wasm/dylink.yaml      | 69 ++++++++++++++++++
 .../wasm/dylink-symbol-table.yaml             | 70 +++++++++++++++++++
 4 files changed, 186 insertions(+), 4 deletions(-)
 create mode 100644 llvm/test/tools/llvm-nm/wasm/dylink.yaml
 create mode 100644 llvm/test/tools/llvm-objdump/wasm/dylink-symbol-table.yaml

diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h
index dfab4c68d18f12..6b8edb90e144b5 100644
--- a/llvm/include/llvm/Object/Wasm.h
+++ b/llvm/include/llvm/Object/Wasm.h
@@ -144,7 +144,6 @@ class WasmObjectFile : public ObjectFile {
   ArrayRef<wasm::WasmGlobal> globals() const { return Globals; }
   ArrayRef<wasm::WasmTag> tags() const { return Tags; }
   ArrayRef<wasm::WasmExport> exports() const { return Exports; }
-  ArrayRef<WasmSymbol> syms() const { return Symbols; }
   const wasm::WasmLinkingData &linkingData() const { return LinkingData; }
   uint32_t getNumberOfSymbols() const { return Symbols.size(); }
   ArrayRef<wasm::WasmElemSegment> elements() const { return ElemSegments; }
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 168fb57935d6d2..05bd7302c49711 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -599,6 +599,10 @@ Error WasmObjectFile::parseLinkingSection(ReadContext &Ctx) {
 
 Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
   uint32_t Count = readVaruint32(Ctx);
+  // Clear out any symbol information that was derived from the exports
+  // section.
+  LinkingData.SymbolTable.clear();
+  Symbols.clear();
   LinkingData.SymbolTable.reserve(Count);
   Symbols.reserve(Count);
   StringSet<> SymbolNames;
@@ -1290,37 +1294,75 @@ Error WasmObjectFile::parseGlobalSection(ReadContext &Ctx) {
 Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
   uint32_t Count = readVaruint32(Ctx);
   Exports.reserve(Count);
+  LinkingData.SymbolTable.reserve(Count);
+  Symbols.reserve(Count);
   for (uint32_t I = 0; I < Count; I++) {
     wasm::WasmExport Ex;
     Ex.Name = readString(Ctx);
     Ex.Kind = readUint8(Ctx);
     Ex.Index = readVaruint32(Ctx);
+    const wasm::WasmSignature *Signature = nullptr;
+    const wasm::WasmGlobalType *GlobalType = nullptr;
+    const wasm::WasmTableType *TableType = nullptr;
+    wasm::WasmSymbolInfo Info;
+    Info.Name = Ex.Name;
+    Info.Flags = 0;
     switch (Ex.Kind) {
-    case wasm::WASM_EXTERNAL_FUNCTION:
-
+    case wasm::WASM_EXTERNAL_FUNCTION: {
       if (!isDefinedFunctionIndex(Ex.Index))
         return make_error<GenericBinaryError>("invalid function export",
                                               object_error::parse_failed);
       getDefinedFunction(Ex.Index).ExportName = Ex.Name;
+      Info.Kind = wasm::WASM_SYMBOL_TYPE_FUNCTION;
+      Info.ElementIndex = Ex.Index;
+      unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
+      wasm::WasmFunction &Function = Functions[FuncIndex];
+      Signature = &Signatures[Function.SigIndex];
       break;
-    case wasm::WASM_EXTERNAL_GLOBAL:
+    }
+    case wasm::WASM_EXTERNAL_GLOBAL: {
       if (!isValidGlobalIndex(Ex.Index))
         return make_error<GenericBinaryError>("invalid global export",
                                               object_error::parse_failed);
+      Info.Kind = wasm::WASM_SYMBOL_TYPE_DATA;
+      uint64_t Offset = 0;
+      if (isDefinedGlobalIndex(Ex.Index)) {
+        auto Global = getDefinedGlobal(Ex.Index);
+        if (!Global.InitExpr.Extended) {
+          auto Inst = Global.InitExpr.Inst;
+          if (Inst.Opcode == wasm::WASM_OPCODE_I32_CONST) {
+            Offset = Inst.Value.Int32;
+          } else if (Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) {
+            Offset = Inst.Value.Int64;
+          }
+        }
+      }
+      Info.DataRef = wasm::WasmDataReference{0, Offset, 0};
       break;
+    }
     case wasm::WASM_EXTERNAL_TAG:
       if (!isValidTagIndex(Ex.Index))
         return make_error<GenericBinaryError>("invalid tag export",
                                               object_error::parse_failed);
+      Info.Kind = wasm::WASM_SYMBOL_TYPE_TAG;
+      Info.ElementIndex = Ex.Index;
       break;
     case wasm::WASM_EXTERNAL_MEMORY:
+      break;
     case wasm::WASM_EXTERNAL_TABLE:
+      Info.Kind = wasm::WASM_SYMBOL_TYPE_TABLE;
       break;
     default:
       return make_error<GenericBinaryError>("unexpected export kind",
                                             object_error::parse_failed);
     }
     Exports.push_back(Ex);
+    if (Ex.Kind != wasm::WASM_EXTERNAL_MEMORY) {
+      LinkingData.SymbolTable.emplace_back(Info);
+      Symbols.emplace_back(LinkingData.SymbolTable.back(), GlobalType,
+                           TableType, Signature);
+      LLVM_DEBUG(dbgs() << "Adding symbol: " << Symbols.back() << "\n");
+    }
   }
   if (Ctx.Ptr != Ctx.End)
     return make_error<GenericBinaryError>("export section ended prematurely",
@@ -1644,6 +1686,8 @@ uint64_t WasmObjectFile::getWasmSymbolValue(const WasmSymbol &Sym) const {
       return Segment.Offset.Inst.Value.Int32 + Sym.Info.DataRef.Offset;
     } else if (Segment.Offset.Inst.Opcode == wasm::WASM_OPCODE_I64_CONST) {
       return Segment.Offset.Inst.Value.Int64 + Sym.Info.DataRef.Offset;
+    } else if (Segment.Offset.Inst.Opcode == wasm::WASM_OPCODE_GLOBAL_GET) {
+      return Sym.Info.DataRef.Offset;
     } else {
       llvm_unreachable("unknown init expr opcode");
     }
diff --git a/llvm/test/tools/llvm-nm/wasm/dylink.yaml b/llvm/test/tools/llvm-nm/wasm/dylink.yaml
new file mode 100644
index 00000000000000..2a8654526789a4
--- /dev/null
+++ b/llvm/test/tools/llvm-nm/wasm/dylink.yaml
@@ -0,0 +1,69 @@
+# RUN: yaml2obj %s -o %t.so
+# RUN: llvm-nm %t.so | FileCheck %s
+#
+# CHECK: 00000001 T my_func_export
+# CHECK: 0000002a D my_global_export
+
+--- !WASM
+FileHeader:
+  Version:         0x1
+Sections:
+  - Type:            CUSTOM
+    Name:            dylink.0
+    MemorySize:      15
+    MemoryAlignment: 0
+    TableSize:       0
+    TableAlignment:  0
+    Needed:          []
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:      []
+        ReturnTypes:     []
+  - Type:            IMPORT
+    Imports:
+      - Module:          env
+        Field:           foo
+        Kind:            FUNCTION
+        SigIndex:        0
+      - Module:          env
+        Field:           bar
+        Kind:            GLOBAL
+        GlobalType:      I32
+        GlobalMutable:   true
+      - Module:          env
+        Field:           memory
+        Kind:            MEMORY
+        Memory:
+          Minimum:         0x1
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0 ]
+  - Type:            GLOBAL
+    Globals:
+      - Index:           1
+        Mutable:         false
+        Type:            I32
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           42
+  - Type:            EXPORT
+    Exports:
+      - Name:            my_func_export
+        Kind:            FUNCTION
+        Index:           1
+      - Name:            my_global_export
+        Kind:            GLOBAL
+        Index:           1
+  - Type:            CODE
+    Functions:
+      - Index:           1
+        Locals:
+        Body:            00
+  - Type:            DATA
+    Segments:
+      - SectionOffset:   0
+        InitFlags:       0
+        Offset:
+          Opcode:          I32_CONST
+          Value:           0
+        Content:         ''
diff --git a/llvm/test/tools/llvm-objdump/wasm/dylink-symbol-table.yaml b/llvm/test/tools/llvm-objdump/wasm/dylink-symbol-table.yaml
new file mode 100644
index 00000000000000..9c1e90a2d89675
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/wasm/dylink-symbol-table.yaml
@@ -0,0 +1,70 @@
+# RUN: yaml2obj %s -o %t.so
+# RUN: llvm-objdump -t %t.so | FileCheck %s
+#
+# CHECK:      SYMBOL TABLE:
+# CHECK-NEXT: 00000001 g F CODE my_func_export
+# CHECK-NEXT: 0000002a g O DATA my_global_export
+
+--- !WASM
+FileHeader:
+  Version:         0x1
+Sections:
+  - Type:            CUSTOM
+    Name:            dylink.0
+    MemorySize:      15
+    MemoryAlignment: 0
+    TableSize:       0
+    TableAlignment:  0
+    Needed:          []
+  - Type:            TYPE
+    Signatures:
+      - Index:           0
+        ParamTypes:      []
+        ReturnTypes:     []
+  - Type:            IMPORT
+    Imports:
+      - Module:          env
+        Field:           foo
+        Kind:            FUNCTION
+        SigIndex:        0
+      - Module:          env
+        Field:           bar
+        Kind:            GLOBAL
+        GlobalType:      I32
+        GlobalMutable:   true
+      - Module:          env
+        Field:           memory
+        Kind:            MEMORY
+        Memory:
+          Minimum:         0x1
+  - Type:            FUNCTION
+    FunctionTypes:   [ 0 ]
+  - Type:            GLOBAL
+    Globals:
+      - Index:           1
+        Mutable:         false
+        Type:            I32
+        InitExpr:
+          Opcode:          I32_CONST
+          Value:           42
+  - Type:            EXPORT
+    Exports:
+      - Name:            my_func_export
+        Kind:            FUNCTION
+        Index:           1
+      - Name:            my_global_export
+        Kind:            GLOBAL
+        Index:           1
+  - Type:            CODE
+    Functions:
+      - Index:           1
+        Locals:
+        Body:            00
+  - Type:            DATA
+    Segments:
+      - SectionOffset:   0
+        InitFlags:       0
+        Offset:
+          Opcode:          I32_CONST
+          Value:           0
+        Content:         ''

>From 77e038cf19be9584b8e3aa17997ce14dbb96adba 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 2/2] [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.

The old behaviour is still possible via the
`--unresolved-symbols=import-dynamic flag.

The rationale here is that changing the semantics like this is
reasonable since `-pie` and `-shared` are both still experimental will
warn as such unless `--experimental-pic` is passed.

Depends on (and currently includes)
https://github.com/llvm/llvm-project/pull/75238
---
 lld/test/wasm/Inputs/ret32.s       |  1 -
 lld/test/wasm/dylink.s             | 30 ++++++++++++
 lld/test/wasm/emit-relocs.s        |  2 +-
 lld/test/wasm/libsearch.s          | 62 ++++++++++++------------
 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/wasm/Driver.cpp                |  1 -
 lld/wasm/InputFiles.cpp            | 43 +++++++++++++++--
 lld/wasm/InputFiles.h              | 29 ++++++++----
 lld/wasm/MarkLive.cpp              |  5 +-
 lld/wasm/Relocations.cpp           | 22 +++++++--
 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 +++-
 23 files changed, 301 insertions(+), 80 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/libsearch.s b/lld/test/wasm/libsearch.s
index 23336510748ce8..f321b859a8a179 100644
--- a/lld/test/wasm/libsearch.s
+++ b/lld/test/wasm/libsearch.s
@@ -32,66 +32,66 @@
 
 // Should use explicitly specified static library
 // Also ensure that we accept -L <arg>
-// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o -L %t.dir -l:libls.a
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t4 %t.o -L %t.dir -l:libls.a
+// RUN: llvm-readobj --symbols %t4 | FileCheck --check-prefix=STATIC %s
 // STATIC: Symbols [
 // STATIC: Name: _static
 
 // Should use explicitly specified dynamic library
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -l:libls.so
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t5 %t.o -L%t.dir -l:libls.so
+// RUN: llvm-readobj --symbols %t5 | FileCheck --check-prefix=DYNAMIC %s
 // DYNAMIC: Symbols [
 // DYNAMIC-NOT: Name: _static
 
 // Should prefer static to dynamic when linking regular executable.
-// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t6 %t.o -L%t.dir -lls
+// RUN: llvm-readobj --symbols %t6 | FileCheck --check-prefix=STATIC %s
 
 // Should prefer dynamic when linking PIE.
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t7 %t.o -L%t.dir -lls
+// RUN: llvm-readobj --symbols %t7 | FileCheck --check-prefix=DYNAMIC %s
 
 // Check for library search order
 // RUN: mkdir -p %t.dir2
 // RUN: cp %t.dir/libls.a %t.dir2
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir2 -L%t.dir -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t8 %t.o -L%t.dir2 -L%t.dir -lls
+// RUN: llvm-readobj --symbols %t8 | FileCheck --check-prefix=STATIC %s
 
 // -L can be placed after -l
-// RUN: wasm-ld -o %t3 %t.o -lls -L%t.dir
+// RUN: wasm-ld -o %t8 %t.o -lls -L%t.dir
 
 // Check long forms as well
-// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o --library-path=%t.dir --library=ls
-// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t3 %t.o --library-path %t.dir --library ls
+// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t8 %t.o --library-path=%t.dir --library=ls
+// RUN: wasm-ld --emit-relocs --no-gc-sections -o %t8 %t.o --library-path %t.dir --library ls
 
 // Should not search for dynamic libraries if -Bstatic is specified
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t10 %t.o -L%t.dir -Bstatic -lls
+// RUN: llvm-readobj --symbols %t10 | FileCheck --check-prefix=STATIC %s
 // RUN: not wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o /dev/null %t.o -L%t.dir -Bstatic -lls2 2>&1 \
 // RUN:   | FileCheck --check-prefix=NOLIB2 %s
 // NOLIB2: unable to find library -lls2
 
 // -Bdynamic should restore default behaviour
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -Bdynamic -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t11 %t.o -L%t.dir -Bstatic -Bdynamic -lls
+// RUN: llvm-readobj --symbols %t11 | FileCheck --check-prefix=DYNAMIC %s
 
 // -Bstatic and -Bdynamic should affect only libraries which follow them
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -lls -Bstatic -Bdynamic
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -lls -Bdynamic
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t12 %t.o -L%t.dir -lls -Bstatic -Bdynamic
+// RUN: llvm-readobj --symbols %t12 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t13 %t.o -L%t.dir -Bstatic -lls -Bdynamic
+// RUN: llvm-readobj --symbols %t13 | FileCheck --check-prefix=STATIC %s
 
 // Check aliases as well
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -dn -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -non_shared -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -static -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=STATIC %s
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -dy -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
-// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t3 %t.o -L%t.dir -Bstatic -call_shared -lls
-// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t13 %t.o -L%t.dir -dn -lls
+// RUN: llvm-readobj --symbols %t13 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t14 %t.o -L%t.dir -non_shared -lls
+// RUN: llvm-readobj --symbols %t14 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t15 %t.o -L%t.dir -static -lls
+// RUN: llvm-readobj --symbols %t15 | FileCheck --check-prefix=STATIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t16 %t.o -L%t.dir -Bstatic -dy -lls
+// RUN: llvm-readobj --symbols %t16 | FileCheck --check-prefix=DYNAMIC %s
+// RUN: wasm-ld -pie --experimental-pic --emit-relocs --no-gc-sections -o %t17 %t.o -L%t.dir -Bstatic -call_shared -lls
+// RUN: llvm-readobj --symbols %t17 | FileCheck --check-prefix=DYNAMIC %s
 
 .globl _start, _bar
 _start:
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/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index f8b0fc357bd90c..c68fe33a14e29d 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -606,7 +606,6 @@ static void setConfigs() {
       config->memoryImport =
           std::pair<llvm::StringRef, llvm::StringRef>(defaultModule, memoryName);
     }
-    config->importUndefined = true;
   }
 
   // If neither export-memory nor import-memory is specified, default to
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..d8a99b18f26b72 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 importSymbol =
+        sym->isShared() ||
         (sym->isUndefined() &&
-         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
+         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic);
+
+    if (config->isPic && importSymbol) {
       switch (reloc.Type) {
       case R_WASM_TABLE_INDEX_SLEB:
       case R_WASM_TABLE_INDEX_SLEB64:
@@ -153,17 +158,24 @@ void scanRelocations(InputChunk *chunk) {
       case R_WASM_MEMORY_ADDR_LEB64:
         // Certain relocation types can't be used when building PIC output,
         // since they would require absolute symbol addresses at link time.
-        error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
+        error(toString(file) + ": relocation " +
+              relocTypeToString(reloc.Type) +
               " cannot be used against symbol `" + toString(*sym) +
               "`; recompile with -fPIC");
         break;
+      }
+    }
+
+
+    if (importSymbol) {
+      switch (reloc.Type) {
       case R_WASM_TABLE_INDEX_I32:
       case R_WASM_TABLE_INDEX_I64:
       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 0576bf2907e49c..68957036c51741 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -722,6 +722,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)
@@ -779,8 +781,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()) {
@@ -808,7 +813,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);
   }
@@ -819,7 +823,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