[lld] ef8c913 - [WebAssembly] Allow import and export of TLS symbols between DSOs

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 14 06:47:54 PDT 2021


Author: Sam Clegg
Date: 2021-09-14T06:47:37-07:00
New Revision: ef8c9135efcb3847fc0e5bbdb55eae18751090df

URL: https://github.com/llvm/llvm-project/commit/ef8c9135efcb3847fc0e5bbdb55eae18751090df
DIFF: https://github.com/llvm/llvm-project/commit/ef8c9135efcb3847fc0e5bbdb55eae18751090df.diff

LOG: [WebAssembly] Allow import and export of TLS symbols between DSOs

We previously had a limitation that TLS variables could not
be exported (and therefore could also not be imported).  This
change removed that limitation.

Differential Revision: https://reviews.llvm.org/D108877

Added: 
    

Modified: 
    lld/test/wasm/pie.ll
    lld/test/wasm/shared-needed.s
    lld/test/wasm/shared.s
    lld/test/wasm/shared64.s
    lld/test/wasm/tls-export.s
    lld/test/wasm/tls-non-shared-memory.s
    lld/wasm/Relocations.cpp
    lld/wasm/Symbols.cpp
    lld/wasm/Symbols.h
    lld/wasm/SyntheticSections.cpp
    lld/wasm/SyntheticSections.h
    lld/wasm/Writer.cpp
    llvm/include/llvm/BinaryFormat/Wasm.h
    llvm/include/llvm/MC/MCExpr.h
    llvm/include/llvm/ObjectYAML/WasmYAML.h
    llvm/lib/MC/MCExpr.cpp
    llvm/lib/MC/MCWasmStreamer.cpp
    llvm/lib/MC/WasmObjectWriter.cpp
    llvm/lib/Object/WasmObjectFile.cpp
    llvm/lib/ObjectYAML/WasmYAML.cpp
    llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
    llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
    llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
    llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
    llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
    llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
    llvm/test/MC/WebAssembly/tls.s
    llvm/tools/obj2yaml/wasm2yaml.cpp

Removed: 
    lld/test/wasm/tls-import.s


################################################################################
diff  --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll
index ee2f02777fda9..0dba0aab02d6f 100644
--- a/lld/test/wasm/pie.ll
+++ b/lld/test/wasm/pie.ll
@@ -91,7 +91,7 @@ declare void @external_func()
 ; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM
 
 ; SHMEM:         - Type:            CODE
-; SHMEM:           - Index:           6
+; SHMEM:           - Index:           7
 ; SHMEM-NEXT:        Locals:          []
 ; SHMEM-NEXT:        Body:            100310050B
 
@@ -109,11 +109,13 @@ declare void @external_func()
 ; SHMEM-NEXT:      - Index:           5
 ; SHMEM-NEXT:        Name:            __wasm_apply_global_relocs
 ; SHMEM-NEXT:      - Index:           6
-; SHMEM-NEXT:        Name:            __wasm_start
+; SHMEM-NEXT:        Name:            __wasm_apply_global_tls_relocs
 ; SHMEM-NEXT:      - Index:           7
-; SHMEM-NEXT:        Name:            foo
+; SHMEM-NEXT:        Name:            __wasm_start
 ; SHMEM-NEXT:      - Index:           8
-; SHMEM-NEXT:        Name:            get_data_address
+; SHMEM-NEXT:        Name:            foo
 ; SHMEM-NEXT:      - Index:           9
+; SHMEM-NEXT:        Name:            get_data_address
+; SHMEM-NEXT:      - Index:           10
 ; SHMEM-NEXT:        Name:            _start
 

diff  --git a/lld/test/wasm/shared-needed.s b/lld/test/wasm/shared-needed.s
index 12c4597190a3b..10ac40855d240 100644
--- a/lld/test/wasm/shared-needed.s
+++ b/lld/test/wasm/shared-needed.s
@@ -29,6 +29,7 @@ data:
 # SO1-NEXT:     TableSize:       0
 # SO1-NEXT:     TableAlignment:  0
 # SO1-NEXT:     Needed:          []
+# SO1-NEXT:     ExportInfo:      []
 # SO1-NEXT:   - Type:            TYPE
 
 # SO2:      Sections:
@@ -40,4 +41,5 @@ data:
 # SO2-NEXT:     TableAlignment:  0
 # SO2-NEXT:     Needed:
 # SO2-NEXT:       - shared-needed.s.tmp1.so
+# SO2-NEXT:     ExportInfo:      []
 # SO2-NEXT:   - Type:            TYPE

diff  --git a/lld/test/wasm/shared.s b/lld/test/wasm/shared.s
index 7861485470ffa..a3f82cebad2b8 100644
--- a/lld/test/wasm/shared.s
+++ b/lld/test/wasm/shared.s
@@ -133,6 +133,7 @@ get_local_func_address:
 # CHECK-NEXT:     TableSize:       2
 # CHECK-NEXT:     TableAlignment:  0
 # CHECK-NEXT:     Needed:          []
+# CHECK-NEXT:     ExportInfo:      []
 # CHECK-NEXT:   - Type:            TYPE
 
 # check for import of __table_base and __memory_base globals

diff  --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s
index 86d5a521ab334..42fc8298a6da4 100644
--- a/lld/test/wasm/shared64.s
+++ b/lld/test/wasm/shared64.s
@@ -134,6 +134,7 @@ get_local_func_address:
 # CHECK-NEXT:     TableSize:       2
 # CHECK-NEXT:     TableAlignment:  0
 # CHECK-NEXT:     Needed:          []
+# CHECK-NEXT:     ExportInfo:      []
 # CHECK-NEXT:   - Type:            TYPE
 
 # check for import of __table_base and __memory_base globals

diff  --git a/lld/test/wasm/tls-export.s b/lld/test/wasm/tls-export.s
index b8a36aa55aa1b..619f9d2df312a 100644
--- a/lld/test/wasm/tls-export.s
+++ b/lld/test/wasm/tls-export.s
@@ -1,10 +1,6 @@
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: wasm-ld -no-gc-sections --shared-memory --no-entry -o %t.wasm %t.o
-# RUN: not wasm-ld --shared-memory --no-entry --export=tls1 -o %t.wasm %t.o 2>&1 | FileCheck %s
-# With --export-all we ignore TLS symbols so we don't expect an error here
-# RUN: wasm-ld --shared-memory --no-entry --export-all -o %t.wasm %t.o
-
-# CHECK: error: TLS symbols cannot yet be exported: `tls1`
+# RUN: wasm-ld -shared --experimental-pic -o %t.so %t.o
+# RUN: obj2yaml %t.so | FileCheck %s
 
 .section  .tdata.tls1,"",@
 .globl  tls1
@@ -24,3 +20,26 @@ tls1:
   .int8 43
   .int8 15
   .ascii "mutable-globals"
+
+#      CHECK:    ExportInfo:
+# CHECK-NEXT:      - Name:            tls1
+# CHECK-NEXT:        Flags:           [ TLS ]
+# CHECK-NEXT:  - Type:            TYPE
+
+#      CHECK:  - Type:            GLOBAL
+# CHECK-NEXT:    Globals:
+# CHECK-NEXT:      - Index:           2
+# CHECK-NEXT:        Type:            I32
+# CHECK-NEXT:        Mutable:         false
+# CHECK-NEXT:        InitExpr:
+# CHECK-NEXT:          Opcode:          I32_CONST
+# CHECK-NEXT:          Value:           0
+
+#      CHECK:  - Type:            EXPORT
+# CHECK-NEXT:    Exports:
+# CHECK-NEXT:      - Name:            __wasm_call_ctors
+# CHECK-NEXT:        Kind:            FUNCTION
+# CHECK-NEXT:        Index:           0
+# CHECK-NEXT:      - Name:            tls1
+# CHECK-NEXT:        Kind:            GLOBAL
+# CHECK-NEXT:        Index:           2

diff  --git a/lld/test/wasm/tls-import.s b/lld/test/wasm/tls-import.s
deleted file mode 100644
index cce45f5fbaac5..0000000000000
--- a/lld/test/wasm/tls-import.s
+++ /dev/null
@@ -1,23 +0,0 @@
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
-# RUN: not wasm-ld -shared --experimental-pic --shared-memory -o %t.wasm %t.o 2>&1 | FileCheck %s
-
-# CHECK: error: {{.*}}.o: TLS symbol is undefined, but TLS symbols cannot yet be imported: `foo`
-
-.globl _start
-_start:
-  .functype _start () -> ()
-  i32.const foo at TLSREL
-  drop
-  end_function
-
-.section  .custom_section.target_features,"",@
-  .int8 3
-  .int8 43
-  .int8 7
-  .ascii  "atomics"
-  .int8 43
-  .int8 11
-  .ascii  "bulk-memory"
-  .int8 43
-  .int8 15
-  .ascii "mutable-globals"

diff  --git a/lld/test/wasm/tls-non-shared-memory.s b/lld/test/wasm/tls-non-shared-memory.s
index 38f95e5310b77..222db777df29c 100644
--- a/lld/test/wasm/tls-non-shared-memory.s
+++ b/lld/test/wasm/tls-non-shared-memory.s
@@ -1,5 +1,5 @@
 # Test that linking without shared memory causes __tls_base to be
-# internalized
+# internalized.
 
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
 
@@ -13,6 +13,12 @@ get_tls1:
   i32.add
   end_function
 
+.globl get_tls1_got
+get_tls1_got:
+  .functype get_tls1_got () -> (i32)
+  global.get tls1 at GOT@TLS
+  end_function
+
 .section  .data.no_tls,"",@
 .globl  no_tls
 .p2align  2
@@ -20,7 +26,7 @@ no_tls:
   .int32  42
   .size no_tls, 4
 
-.section  .tdata.tls1,"",@
+.section  .tdata.tls1,"T",@
 .globl  tls1
 .p2align  2
 tls1:
@@ -58,6 +64,13 @@ tls1:
 # CHECK-NEXT:         InitExpr:
 # CHECK-NEXT:           Opcode:          I32_CONST
 # CHECK-NEXT:           Value:           1024
+# GOT.data.internal.tls1
+# CHECK-NEXT:       - Index:           2
+# CHECK-NEXT:         Type:            I32
+# CHECK-NEXT:         Mutable:         false
+# CHECK-NEXT:         InitExpr:
+# CHECK-NEXT:           Opcode:          I32_CONST
+# CHECK-NEXT:           Value:           1024
 # CHECK-NEXT:   - Type:            EXPORT
 
 #      CHECK:  - Type:            DATA

diff  --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index 06d7f5d6752b5..a6128e2443fcb 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -139,12 +139,6 @@ void scanRelocations(InputChunk *chunk) {
     }
 
     if (config->isPic) {
-      if (sym->isTLS() && sym->isUndefined()) {
-        error(toString(file) +
-              ": TLS symbol is undefined, but TLS symbols cannot yet be "
-              "imported: `" +
-              toString(*sym) + "`");
-      }
       switch (reloc.Type) {
       case R_WASM_TABLE_INDEX_SLEB:
       case R_WASM_TABLE_INDEX_SLEB64:

diff  --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index fd13b70f8c6da..319d60c6a81d4 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -74,6 +74,7 @@ DefinedFunction *WasmSym::callDtors;
 DefinedFunction *WasmSym::initMemory;
 DefinedFunction *WasmSym::applyDataRelocs;
 DefinedFunction *WasmSym::applyGlobalRelocs;
+DefinedFunction *WasmSym::applyGlobalTLSRelocs;
 DefinedFunction *WasmSym::initTLS;
 DefinedFunction *WasmSym::startFunction;
 DefinedData *WasmSym::dsoHandle;

diff  --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 415881e0c1982..eebe4fd573c7e 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -552,10 +552,15 @@ struct WasmSym {
   static DefinedFunction *applyDataRelocs;
 
   // __wasm_apply_global_relocs
-  // Function that applies relocations to data segment post-instantiation.
+  // Function that applies relocations to wasm globals post-instantiation.
   // Unlike __wasm_apply_data_relocs this needs to run on every thread.
   static DefinedFunction *applyGlobalRelocs;
 
+  // __wasm_apply_global_tls_relocs
+  // Like applyGlobalRelocs but for globals that hold TLS addresess.  These
+  // must be delayed until __wasm_init_tls.
+  static DefinedFunction *applyGlobalTLSRelocs;
+
   // __wasm_init_tls
   // Function that allocates thread-local storage and initializes it.
   static DefinedFunction *initTLS;

diff  --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 3e6e9fe56d9f9..549af5b3b460a 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -73,6 +73,37 @@ void DylinkSection::writeBody() {
       writeStr(sub.os, llvm::sys::path::filename(so->getName()), "so name");
     sub.writeTo(os);
   }
+
+  // Under certain circumstances we need to include extra information about the
+  // exports we are providing to the dynamic linker.  Currently this is only the
+  // case for TLS symbols where the exported value is relative to __tls_base
+  // rather than __memory_base.
+  std::vector<const Symbol *> exportInfo;
+  for (const Symbol *sym : symtab->getSymbols()) {
+    if (sym->isExported() && sym->isLive() && sym->isTLS() &&
+        isa<DefinedData>(sym)) {
+      exportInfo.push_back(sym);
+    }
+  }
+
+  if (!exportInfo.empty()) {
+    SubSection sub(WASM_DYLINK_EXPORT_INFO);
+    writeUleb128(sub.os, exportInfo.size(), "num exports");
+
+    for (const Symbol *sym : exportInfo) {
+      LLVM_DEBUG(llvm::dbgs() << "export info: " << toString(*sym) << "\n");
+      StringRef name = sym->getName();
+      if (auto *f = dyn_cast<DefinedFunction>(sym)) {
+        if (Optional<StringRef> exportName = f->function->getExportName()) {
+          name = *exportName;
+        }
+      }
+      writeStr(sub.os, name, "sym name");
+      writeUleb128(sub.os, sym->flags, "sym flags");
+    }
+
+    sub.writeTo(os);
+  }
 }
 
 uint32_t TypeSection::registerType(const WasmSignature &sig) {
@@ -345,7 +376,7 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
   internalGotSymbols.push_back(sym);
 }
 
-void GlobalSection::generateRelocationCode(raw_ostream &os) const {
+void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
   bool is64 = config->is64.getValueOr(false);
   unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
                                    : WASM_OPCODE_I32_CONST;
@@ -353,10 +384,17 @@ void GlobalSection::generateRelocationCode(raw_ostream &os) const {
                                  : WASM_OPCODE_I32_ADD;
 
   for (const Symbol *sym : internalGotSymbols) {
+    if (TLS != sym->isTLS())
+      continue;
+
     if (auto *d = dyn_cast<DefinedData>(sym)) {
       // Get __memory_base
       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
-      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base");
+      if (sym->isTLS())
+        writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base");
+      else
+        writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
+                     "__memory_base");
 
       // Add the virtual address of the data symbol
       writeU8(os, opcode_ptr_const, "CONST");

diff  --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index d26f89b6fd5f1..421bbabe0e1b2 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -287,9 +287,9 @@ class GlobalSection : public SyntheticSection {
   // transform a `global.get` to an `i32.const`.
   void addInternalGOTEntry(Symbol *sym);
   bool needsRelocations() { return internalGotSymbols.size(); }
-  void generateRelocationCode(raw_ostream &os) const;
+  void generateRelocationCode(raw_ostream &os, bool TLS) const;
 
-  std::vector<const DefinedData *> dataAddressGlobals;
+  std::vector<DefinedData *> dataAddressGlobals;
   std::vector<InputGlobal *> inputGlobals;
   std::vector<Symbol *> internalGotSymbols;
 

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 2a81fd31bf7e3..41d26e98ec148 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -63,6 +63,7 @@ class Writer {
   void createStartFunction();
   void createApplyDataRelocationsFunction();
   void createApplyGlobalRelocationsFunction();
+  void createApplyGlobalTLSRelocationsFunction();
   void createCallCtorsFunction();
   void createInitTLSFunction();
   void createCommandExportWrappers();
@@ -639,12 +640,6 @@ void Writer::calculateExports() {
     } else if (auto *t = dyn_cast<DefinedTag>(sym)) {
       export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()};
     } else if (auto *d = dyn_cast<DefinedData>(sym)) {
-      if (sym->isTLS()) {
-        // We can't currenly export TLS data symbols.
-        if (sym->isExportedExplicit())
-          error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`");
-        continue;
-      }
       out.globalSec->dataAddressGlobals.push_back(d);
       export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
     } else {
@@ -991,6 +986,13 @@ void Writer::createSyntheticInitFunctions() {
           "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
           make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
       WasmSym::applyGlobalRelocs->markLive();
+      if (config->sharedMemory) {
+        WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction(
+            "__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+            make<SyntheticFunction>(nullSignature,
+                                    "__wasm_apply_global_tls_relocs"));
+        WasmSym::applyGlobalTLSRelocs->markLive();
+      }
     }
   }
 
@@ -1235,13 +1237,29 @@ void Writer::createApplyGlobalRelocationsFunction() {
   {
     raw_string_ostream os(bodyContent);
     writeUleb128(os, 0, "num locals");
-    out.globalSec->generateRelocationCode(os);
+    out.globalSec->generateRelocationCode(os, false);
     writeU8(os, WASM_OPCODE_END, "END");
   }
 
   createFunction(WasmSym::applyGlobalRelocs, bodyContent);
 }
 
+// Similar to createApplyGlobalRelocationsFunction but for
+// TLS symbols.  This cannot be run during the start function
+// but must be delayed until __wasm_init_tls is called.
+void Writer::createApplyGlobalTLSRelocationsFunction() {
+  // First write the body's contents to a string.
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    out.globalSec->generateRelocationCode(os, true);
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+
+  createFunction(WasmSym::applyGlobalTLSRelocs, bodyContent);
+}
+
 // Create synthetic "__wasm_call_ctors" function based on ctor functions
 // in input object.
 void Writer::createCallCtorsFunction() {
@@ -1352,6 +1370,12 @@ void Writer::createInitTLSFunction() {
       writeUleb128(os, tlsSeg->index, "segment index immediate");
       writeU8(os, 0, "memory index immediate");
     }
+
+    if (WasmSym::applyGlobalTLSRelocs) {
+      writeU8(os, WASM_OPCODE_CALL, "CALL");
+      writeUleb128(os, WasmSym::applyGlobalTLSRelocs->getFunctionIndex(),
+                   "function index");
+    }
     writeU8(os, WASM_OPCODE_END, "end function");
   }
 
@@ -1486,6 +1510,8 @@ void Writer::run() {
       createApplyDataRelocationsFunction();
     if (WasmSym::applyGlobalRelocs)
       createApplyGlobalRelocationsFunction();
+    if (WasmSym::applyGlobalTLSRelocs)
+      createApplyGlobalTLSRelocationsFunction();
     if (WasmSym::initMemory)
       createInitMemoryFunction();
     createStartFunction();

diff  --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 966dc1bbc886d..736280cfc602b 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -36,12 +36,18 @@ struct WasmObjectHeader {
   uint32_t Version;
 };
 
+struct WasmDylinkExport {
+  StringRef Name;
+  uint32_t Flags;
+};
+
 struct WasmDylinkInfo {
   uint32_t MemorySize; // Memory size in bytes
   uint32_t MemoryAlignment;  // P2 alignment of memory
   uint32_t TableSize;  // Table size in elements
   uint32_t TableAlignment;  // P2 alignment of table
   std::vector<StringRef> Needed; // Shared library dependencies
+  std::vector<WasmDylinkExport> ExportInfo; // Shared library dependencies
 };
 
 struct WasmProducerInfo {
@@ -345,6 +351,7 @@ enum : unsigned {
 enum : unsigned {
   WASM_DYLINK_MEM_INFO = 0x1,
   WASM_DYLINK_NEEDED = 0x2,
+  WASM_DYLINK_EXPORT_INFO = 0x3,
 };
 
 // Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO

diff  --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h
index 38cca2413e1eb..d16e917a2d5b4 100644
--- a/llvm/include/llvm/MC/MCExpr.h
+++ b/llvm/include/llvm/MC/MCExpr.h
@@ -328,6 +328,7 @@ class MCSymbolRefExpr : public MCExpr {
     VK_WASM_TLSREL,    // Memory address relative to __tls_base
     VK_WASM_MBREL,     // Memory address relative to __memory_base
     VK_WASM_TBREL,     // Table index relative to __table_base
+    VK_WASM_GOT_TLS,   // Wasm global index of TLS symbol.
 
     VK_AMDGPU_GOTPCREL32_LO, // symbol at gotpcrel32@lo
     VK_AMDGPU_GOTPCREL32_HI, // symbol at gotpcrel32@hi

diff  --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h
index 8b827bf7f408c..e1b48835dbd6f 100644
--- a/llvm/include/llvm/ObjectYAML/WasmYAML.h
+++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h
@@ -199,6 +199,11 @@ struct CustomSection : Section {
   yaml::BinaryRef Payload;
 };
 
+struct DylinkExport {
+  StringRef Name;
+  SymbolFlags Flags;
+};
+
 struct DylinkSection : CustomSection {
   DylinkSection() : CustomSection("dylink.0") {}
 
@@ -212,6 +217,7 @@ struct DylinkSection : CustomSection {
   uint32_t TableSize;
   uint32_t TableAlignment;
   std::vector<StringRef> Needed;
+  std::vector<DylinkExport> ExportInfo;
 };
 
 struct NameSection : CustomSection {
@@ -426,6 +432,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ComdatEntry)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Comdat)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Tag)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DylinkExport)
 
 namespace llvm {
 namespace yaml {
@@ -574,6 +581,10 @@ template <> struct MappingTraits<WasmYAML::Tag> {
   static void mapping(IO &IO, WasmYAML::Tag &Tag);
 };
 
+template <> struct MappingTraits<WasmYAML::DylinkExport> {
+  static void mapping(IO &IO, WasmYAML::DylinkExport &Export);
+};
+
 } // end namespace yaml
 } // end namespace llvm
 

diff  --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
index 84ec0f6bb57ba..bfe393e975cac 100644
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -358,6 +358,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_WASM_MBREL: return "MBREL";
   case VK_WASM_TLSREL: return "TLSREL";
   case VK_WASM_TBREL: return "TBREL";
+  case VK_WASM_GOT_TLS: return "GOT at TLS";
   case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32 at lo";
   case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32 at hi";
   case VK_AMDGPU_REL32_LO: return "rel32 at lo";
@@ -499,6 +500,7 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
     .Case("tbrel", VK_WASM_TBREL)
     .Case("mbrel", VK_WASM_MBREL)
     .Case("tlsrel", VK_WASM_TLSREL)
+    .Case("got at tls", VK_WASM_GOT_TLS)
     .Case("gotpcrel32 at lo", VK_AMDGPU_GOTPCREL32_LO)
     .Case("gotpcrel32 at hi", VK_AMDGPU_GOTPCREL32_HI)
     .Case("rel32 at lo", VK_AMDGPU_REL32_LO)

diff  --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp
index 47951bd5e8726..54e0b79f2b8ef 100644
--- a/llvm/lib/MC/MCWasmStreamer.cpp
+++ b/llvm/lib/MC/MCWasmStreamer.cpp
@@ -232,9 +232,14 @@ void MCWasmStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) {
 
   case MCExpr::SymbolRef: {
     const MCSymbolRefExpr &symRef = *cast<MCSymbolRefExpr>(expr);
-    if (symRef.getKind() == MCSymbolRefExpr::VK_WASM_TLSREL) {
+    switch (symRef.getKind()) {
+    case MCSymbolRefExpr::VK_WASM_TLSREL:
+    case MCSymbolRefExpr::VK_WASM_GOT_TLS:
       getAssembler().registerSymbol(symRef.getSymbol());
       cast<MCSymbolWasm>(symRef.getSymbol()).setTLS();
+      break;
+    default:
+      break;
     }
     break;
   }

diff  --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 2d16c3747180f..25400c31b95cf 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -565,8 +565,14 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
     SymA->setUsedInReloc();
   }
 
-  if (RefA->getKind() == MCSymbolRefExpr::VK_GOT)
+  switch (RefA->getKind()) {
+  case MCSymbolRefExpr::VK_GOT:
+  case MCSymbolRefExpr::VK_WASM_GOT_TLS:
     SymA->setUsedInGOT();
+    break;
+  default:
+    break;
+  }
 
   WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection);
   LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n");

diff  --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 3b1392e418edc..300d08fd39d7c 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -384,6 +384,13 @@ Error WasmObjectFile::parseDylink0Section(ReadContext &Ctx) {
         DylinkInfo.Needed.push_back(readString(Ctx));
       }
       break;
+    case wasm::WASM_DYLINK_EXPORT_INFO: {
+      uint32_t Count = readVaruint32(Ctx);
+      while (Count--) {
+        DylinkInfo.ExportInfo.push_back({readString(Ctx), readVaruint32(Ctx)});
+      }
+      break;
+    }
     default:
       return make_error<GenericBinaryError>("unknown dylink.0 sub-section",
                                             object_error::parse_failed);

diff  --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index b798987e28905..59da56891f66a 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -55,6 +55,7 @@ static void sectionMapping(IO &IO, WasmYAML::DylinkSection &Section) {
   IO.mapRequired("TableSize", Section.TableSize);
   IO.mapRequired("TableAlignment", Section.TableAlignment);
   IO.mapRequired("Needed", Section.Needed);
+  IO.mapRequired("ExportInfo", Section.ExportInfo);
 }
 
 static void sectionMapping(IO &IO, WasmYAML::NameSection &Section) {
@@ -531,6 +532,12 @@ void MappingTraits<WasmYAML::Tag>::mapping(IO &IO, WasmYAML::Tag &Tag) {
   IO.mapRequired("SigIndex", Tag.SigIndex);
 }
 
+void MappingTraits<WasmYAML::DylinkExport>::mapping(
+    IO &IO, WasmYAML::DylinkExport &Export) {
+  IO.mapRequired("Name", Export.Name);
+  IO.mapRequired("Flags", Export.Flags);
+}
+
 void ScalarBitSetTraits<WasmYAML::LimitFlags>::bitset(
     IO &IO, WasmYAML::LimitFlags &Value) {
 #define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X)

diff  --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
index ce8a15bfd9e3c..ea58292199383 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
@@ -155,8 +155,12 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
     break;
   case wasm::WASM_SYMBOL_TYPE_FUNCTION:
   case wasm::WASM_SYMBOL_TYPE_DATA:
-    if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) {
+    switch (SymRef->getKind()) {
+    case MCSymbolRefExpr::VK_GOT:
+    case MCSymbolRefExpr::VK_WASM_GOT_TLS:
       Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32;
+      return false;
+    default:
       break;
     }
     LLVM_FALLTHROUGH;

diff  --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
index 99defb42e3807..d07bfce9abc16 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
@@ -95,6 +95,9 @@ enum TOF {
   // platforms.
   MO_GOT,
 
+  // Same as MO_GOT but the address stored in the global is a TLS address.
+  MO_GOT_TLS,
+
   // On a symbol operand this indicates that the immediate is the symbol
   // address relative the __memory_base wasm global.
   // Only applicable to data symbols.

diff  --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index f67fab9467467..405712906c400 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -74,6 +74,7 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
 
   switch (Modifier) {
     case MCSymbolRefExpr::VK_GOT:
+    case MCSymbolRefExpr::VK_WASM_GOT_TLS:
       return wasm::R_WASM_GLOBAL_INDEX_LEB;
     case MCSymbolRefExpr::VK_WASM_TBREL:
       assert(SymA.isFunction());
@@ -88,7 +89,10 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(
                        : wasm::R_WASM_MEMORY_ADDR_REL_SLEB;
     case MCSymbolRefExpr::VK_WASM_TYPEINDEX:
       return wasm::R_WASM_TYPE_INDEX_LEB;
+    case MCSymbolRefExpr::VK_None:
+      break;
     default:
+      report_fatal_error("unknown VariantKind");
       break;
   }
 

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 622fdfb47b594..b5dad0257da44 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -1539,7 +1539,6 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
                                                  SelectionDAG &DAG) const {
   SDLoc DL(Op);
   const auto *GA = cast<GlobalAddressSDNode>(Op);
-  MVT PtrVT = getPointerTy(DAG.getDataLayout());
 
   MachineFunction &MF = DAG.getMachineFunction();
   if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
@@ -1561,21 +1560,43 @@ WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
                        false);
   }
 
-  auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
-                                     : WebAssembly::GLOBAL_GET_I32;
-  const char *BaseName = MF.createExternalSymbolName("__tls_base");
+  auto model = GV->getThreadLocalMode();
 
-  SDValue BaseAddr(
-      DAG.getMachineNode(GlobalGet, DL, PtrVT,
-                         DAG.getTargetExternalSymbol(BaseName, PtrVT)),
-      0);
+  // Unsupported TLS modes
+  assert(model != GlobalValue::NotThreadLocal);
+  assert(model != GlobalValue::InitialExecTLSModel);
 
-  SDValue TLSOffset = DAG.getTargetGlobalAddress(
-      GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
-  SDValue SymOffset =
-      DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
+  if (model == GlobalValue::LocalExecTLSModel ||
+      model == GlobalValue::LocalDynamicTLSModel ||
+      (model == GlobalValue::GeneralDynamicTLSModel &&
+       getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV))) {
+    // For DSO-local TLS variables we use offset from __tls_base
 
-  return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
+    MVT PtrVT = getPointerTy(DAG.getDataLayout());
+    auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64
+                                       : WebAssembly::GLOBAL_GET_I32;
+    const char *BaseName = MF.createExternalSymbolName("__tls_base");
+
+    SDValue BaseAddr(
+        DAG.getMachineNode(GlobalGet, DL, PtrVT,
+                           DAG.getTargetExternalSymbol(BaseName, PtrVT)),
+        0);
+
+    SDValue TLSOffset = DAG.getTargetGlobalAddress(
+        GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
+    SDValue SymOffset =
+        DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
+
+    return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
+  }
+
+  assert(model == GlobalValue::GeneralDynamicTLSModel);
+
+  EVT VT = Op.getValueType();
+  return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
+                     DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
+                                                GA->getOffset(),
+                                                WebAssemblyII::MO_GOT_TLS));
 }
 
 SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index f6adb279aa307..c61a9cd0b967f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -398,6 +398,11 @@ def : Pat<(i32 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
 def : Pat<(i64 (WebAssemblyWrapperREL tglobaltlsaddr:$addr)),
           (CONST_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
 
+def : Pat<(i32 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
+          (GLOBAL_GET_I32 tglobaltlsaddr:$addr)>, Requires<[HasAddr32]>;
+def : Pat<(i64 (WebAssemblyWrapper tglobaltlsaddr:$addr)),
+          (GLOBAL_GET_I64 tglobaltlsaddr:$addr)>, Requires<[HasAddr64]>;
+
 def : Pat<(i32 (WebAssemblyWrapper texternalsym:$addr)),
           (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC, HasAddr32]>;
 def : Pat<(i64 (WebAssemblyWrapper texternalsym:$addr)),

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index 4d5d6bd06b21e..8c6f57168341d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -102,6 +102,9 @@ MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
   switch (TargetFlags) {
     case WebAssemblyII::MO_NO_FLAG:
       break;
+    case WebAssemblyII::MO_GOT_TLS:
+      Kind = MCSymbolRefExpr::VK_WASM_GOT_TLS;
+      break;
     case WebAssemblyII::MO_GOT:
       Kind = MCSymbolRefExpr::VK_GOT;
       break;

diff  --git a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
index 285a44e9197ce..b6ede2bb97fe2 100644
--- a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
+++ b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll
@@ -20,6 +20,17 @@ define i32 @address_of_tls() {
   ret i32 ptrtoint(i32* @tls to i32)
 }
 
+; CHECK-LABEL: address_of_tls_external:
+; CHECK-NEXT: .functype  address_of_tls_external () -> (i32)
+define i32 @address_of_tls_external() {
+  ; TLS-DAG: global.get tls_external at GOT@TLS
+  ; TLS-NEXT: return
+
+  ; NO-TLS-NEXT: i32.const tls_external
+  ; NO-TLS-NEXT: return
+  ret i32 ptrtoint(i32* @tls_external to i32)
+}
+
 ; CHECK-LABEL: ptr_to_tls:
 ; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
 define i32* @ptr_to_tls() {
@@ -33,6 +44,17 @@ define i32* @ptr_to_tls() {
   ret i32* @tls
 }
 
+; CHECK-LABEL: ptr_to_tls_external:
+; CHECK-NEXT: .functype ptr_to_tls_external () -> (i32)
+define i32* @ptr_to_tls_external() {
+  ; TLS-DAG: global.get tls_external at GOT@TLS
+  ; TLS-NEXT: return
+
+  ; NO-TLS-NEXT: i32.const tls_external
+  ; NO-TLS-NEXT: return
+  ret i32* @tls_external
+}
+
 ; CHECK-LABEL: tls_load:
 ; CHECK-NEXT: .functype tls_load () -> (i32)
 define i32 @tls_load() {
@@ -49,6 +71,20 @@ define i32 @tls_load() {
   ret i32 %tmp
 }
 
+; CHECK-LABEL: tls_load_external:
+; CHECK-NEXT: .functype tls_load_external () -> (i32)
+define i32 @tls_load_external() {
+  ; TLS-DAG: global.get tls_external at GOT@TLS
+  ; TLS-NEXT: i32.load 0
+  ; TLS-NEXT: return
+
+  ; NO-TLS-NEXT: i32.const 0
+  ; NO-TLS-NEXT: i32.load tls_external
+  ; NO-TLS-NEXT: return
+  %tmp = load i32, i32* @tls_external, align 4
+  ret i32 %tmp
+}
+
 ; CHECK-LABEL: tls_store:
 ; CHECK-NEXT: .functype tls_store (i32) -> ()
 define void @tls_store(i32 %x) {
@@ -65,6 +101,20 @@ define void @tls_store(i32 %x) {
   ret void
 }
 
+; CHECK-LABEL: tls_store_external:
+; CHECK-NEXT: .functype tls_store_external (i32) -> ()
+define void @tls_store_external(i32 %x) {
+  ; TLS-DAG: global.get tls_external at GOT@TLS
+  ; TLS-NEXT: i32.store 0
+  ; TLS-NEXT: return
+
+  ; NO-TLS-NEXT: i32.const 0
+  ; NO-TLS-NEXT: i32.store tls_external
+  ; NO-TLS-NEXT: return
+  store i32 %x, i32* @tls_external, align 4
+  ret void
+}
+
 ; CHECK-LABEL: tls_size:
 ; CHECK-NEXT: .functype tls_size () -> (i32)
 define i32 @tls_size() {
@@ -111,6 +161,8 @@ define void @tls_base_write(i8** %output) {
 ; CHECK-NEXT: .int32 0
 @tls = internal thread_local global i32 0
 
+ at tls_external = external thread_local global i32, align 4
+
 declare i32 @llvm.wasm.tls.size.i32()
 declare i32 @llvm.wasm.tls.align.i32()
 declare i8* @llvm.wasm.tls.base()

diff  --git a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
index f863c70be64ad..583f15a4fabde 100644
--- a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
+++ b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll
@@ -16,6 +16,19 @@ define i32 @address_of_tls() {
   ret i32 ptrtoint(i32* @tls to i32)
 }
 
+; CHECK-LABEL: address_of_tls_external:
+; CHECK-NEXT: .functype  address_of_tls_external () -> (i32)
+define i32 @address_of_tls_external() {
+  ; TLS-DAG: global.get __tls_base
+  ; TLS-DAG: i32.const tls_external at TLSREL
+  ; TLS-NEXT: i32.add
+  ; TLS-NEXT: return
+
+  ; NO-TLS-NEXT: i32.const tls_external
+  ; NO-TLS-NEXT: return
+  ret i32 ptrtoint(i32* @tls_external to i32)
+}
+
 ; CHECK-LABEL: ptr_to_tls:
 ; CHECK-NEXT: .functype ptr_to_tls () -> (i32)
 define i32* @ptr_to_tls() {
@@ -78,4 +91,6 @@ define i32 @tls_size() {
 ; CHECK-NEXT: .int32 0
 @tls = internal thread_local(localexec) global i32 0
 
+ at tls_external = external thread_local(localexec) global i32, align 4
+
 declare i32 @llvm.wasm.tls.size.i32()

diff  --git a/llvm/test/MC/WebAssembly/tls.s b/llvm/test/MC/WebAssembly/tls.s
index fc7c7c1b28b4d..340cd5ac6a183 100644
--- a/llvm/test/MC/WebAssembly/tls.s
+++ b/llvm/test/MC/WebAssembly/tls.s
@@ -20,7 +20,7 @@ tls_store:
 
 tls_get_undefined:
   .functype tls_get_undefined (i32) -> (i32)
-  i32.const tls_undefined at TLSREL
+  global.get tls_undefined at GOT@TLS
   end_function
 
 .section .tls.foo,"T",@
@@ -43,6 +43,9 @@ tls2:
 # CHECK-OBJ-NEXT:      - Type:            R_WASM_MEMORY_ADDR_TLS_SLEB
 # CHECK-OBJ-NEXT:        Index:           2
 # CHECK-OBJ-NEXT:        Offset:          0xA
+# CHECK-OBJ-NEXT:      - Type:            R_WASM_GLOBAL_INDEX_LEB
+# CHECK-OBJ-NEXT:        Index:           4
+# CHECK-OBJ-NEXT:        Offset:          0x19
 
 #      CHECK-OBJ:  - Type:            CUSTOM
 # CHECK-OBJ-NEXT:    Name:            linking

diff  --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp
index 9555cb2dea5be..0cbf02118818f 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -60,6 +60,8 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
     DylinkSec->TableSize = Info.TableSize;
     DylinkSec->TableAlignment = Info.TableAlignment;
     DylinkSec->Needed = Info.Needed;
+    for (const auto &Exp : Info.ExportInfo)
+      DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags});
     CustomSec = std::move(DylinkSec);
   } else if (WasmSec.Name == "name") {
     std::unique_ptr<WasmYAML::NameSection> NameSec =


        


More information about the llvm-commits mailing list