Index: ELF/InputFiles.cpp
===================================================================
--- ELF/InputFiles.cpp
+++ ELF/InputFiles.cpp
@@ -1474,12 +1474,8 @@
   bool canOmitFromDynSym = objSym.canBeOmittedFromSymbolTable();
 
   int c = objSym.getComdatIndex();
-  if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
-    Undefined New(&f, name, binding, visibility, type);
-    if (canOmitFromDynSym)
-      New.exportDynamic = false;
-    return symtab->addSymbol(New);
-  }
+  if (objSym.isUndefined() || (c != -1 && !keptComdats[c]))
+    return symtab->addSymbol(Undefined{&f, name, binding, visibility, type});
 
   if (objSym.isCommon())
     return symtab->addSymbol(
@@ -1487,8 +1483,7 @@
                      objSym.getCommonAlignment(), objSym.getCommonSize()});
 
   Defined New(&f, name, binding, visibility, type, 0, 0, nullptr);
-  if (canOmitFromDynSym)
-    New.exportDynamic = false;
+  New.canOmitFromDynSym = canOmitFromDynSym;
   return symtab->addSymbol(New);
 }
 
Index: ELF/SymbolTable.cpp
===================================================================
--- ELF/SymbolTable.cpp
+++ ELF/SymbolTable.cpp
@@ -77,6 +77,7 @@
   sym->visibility = STV_DEFAULT;
   sym->isUsedInRegularObj = false;
   sym->exportDynamic = false;
+  sym->canOmitFromDynSym = false;
   sym->canInline = true;
   sym->scriptDefined = false;
   sym->partition = 1;
@@ -162,12 +163,8 @@
     else
       syms = findByVersion(ver);
 
-    for (Symbol *b : syms) {
-      if (!config->shared)
-        b->exportDynamic = true;
-      else if (b->includeInDynsym())
-        b->isPreemptible = true;
-    }
+    for (Symbol *b : syms)
+      b->exportDynamic = true;
   }
 }
 
Index: ELF/Symbols.h
===================================================================
--- ELF/Symbols.h
+++ ELF/Symbols.h
@@ -116,12 +116,15 @@
   // are unreferenced except by other bitcode objects.
   unsigned isUsedInRegularObj : 1;
 
-  // If this flag is true and the symbol has protected or default visibility, it
-  // will appear in .dynsym. This flag is set by interposable DSO symbols in
-  // executables, by most symbols in DSOs and executables built with
-  // --export-dynamic, and by dynamic lists.
+  // Used by Defined with protected or default visibility to track if it should
+  // appear in .dynsym. This flag is set by interposable DSO symbols (symbols
+  // with default visibility), and by dynamic lists.
   unsigned exportDynamic : 1;
 
+  // Stores the value of bitcode canBeOmittedFromSymbolTable(). Used as an
+  // optimization to omit some .dynsym entries.
+  unsigned canOmitFromDynSym : 1;
+
   // False if LTO shouldn't inline whatever this symbol points to. If a symbol
   // is overwritten after LTO, LTO shouldn't inline the symbol because it
   // doesn't know the final contents of the symbol.
@@ -211,12 +214,6 @@
   void fetch() const;
 
 private:
-  static bool isExportDynamic(Kind k, uint8_t visibility) {
-    if (k == SharedKind)
-      return visibility == llvm::ELF::STV_DEFAULT;
-    return config->shared || config->exportDynamic;
-  }
-
   void resolveUndefined(const Undefined &other);
   void resolveCommon(const CommonSymbol &other);
   void resolveDefined(const Defined &other);
@@ -233,10 +230,11 @@
       : file(file), nameData(name.data), nameSize(name.size), binding(binding),
         type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
         isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
-        exportDynamic(isExportDynamic(k, visibility)), canInline(false),
-        referenced(false), traced(false), needsPltAddr(false), isInIplt(false),
-        gotInIgot(false), isPreemptible(false), used(!config->gcSections),
-        needsTocRestore(false), scriptDefined(false) {}
+        exportDynamic(k == SharedKind && visibility == llvm::ELF::STV_DEFAULT),
+        canOmitFromDynSym(false), canInline(false), referenced(false),
+        traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false),
+        isPreemptible(false), used(!config->gcSections), needsTocRestore(false),
+        scriptDefined(false) {}
 
 public:
   // True the symbol should point to its PLT entry.
Index: ELF/Symbols.cpp
===================================================================
--- ELF/Symbols.cpp
+++ ELF/Symbols.cpp
@@ -295,7 +295,13 @@
   if (isUndefWeak() && config->pie && sharedFiles.empty())
     return false;
 
-  return isUndefined() || isShared() || exportDynamic;
+  if (isUndefined() || isShared() || exportDynamic)
+    return true;
+
+  // -shared and --export-dynamic default to export defined symbols. As an
+  // optimization, it can be omitted if lto canBeOmittedFromSymbolTable() is
+  // true.
+  return (config->shared || config->exportDynamic) && !canOmitFromDynSym;
 }
 
 // Print out a log message for --trace-symbol.
Index: ELF/Writer.cpp
===================================================================
--- ELF/Writer.cpp
+++ ELF/Writer.cpp
@@ -1653,13 +1653,13 @@
   if (!b.isDefined())
     return true;
 
-  // If we have a dynamic list it specifies which local symbols are preemptible.
-  if (config->hasDynamicList)
-    return false;
-
   if (!config->shared)
     return false;
 
+  // In a DSO, the dynamic list specifies preemptible symbols.
+  if (config->hasDynamicList)
+    return b.exportDynamic;
+
   // -Bsymbolic means that definitions are not preempted.
   if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
     return false;
@@ -1728,10 +1728,8 @@
   for (Partition &part : partitions)
     finalizeSynthetic(part.ehFrame);
 
-  symtab->forEachSymbol([](Symbol *s) {
-    if (!s->isPreemptible)
-      s->isPreemptible = computeIsPreemptible(*s);
-  });
+  symtab->forEachSymbol(
+      [](Symbol *s) { s->isPreemptible = computeIsPreemptible(*s); });
 
   // Scan relocations. This must be done after every symbol is declared so that
   // we can correctly decide if a dynamic relocation is needed.