diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -94,6 +94,13 @@
   bool match(llvm::StringRef symbolName) const;
 };
 
+enum class SymtabPresence {
+  All,
+  None,
+  SelectivelyIncluded,
+  SelectivelyExcluded,
+};
+
 struct Configuration {
   Symbol *entry = nullptr;
   bool hasReexports = false;
@@ -179,6 +186,10 @@
   SymbolPatterns unexportedSymbols;
   SymbolPatterns whyLive;
 
+  SymtabPresence localSymbolsPresence = SymtabPresence::All;
+  SymbolPatterns includedLocalSymbols;
+  SymbolPatterns excludedLocalSymbols;
+
   bool zeroModTime = false;
 
   llvm::StringRef osoPrefix;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -938,26 +938,29 @@
   return matchLiteral(symbolName) || matchGlob(symbolName);
 }
 
+static void handleSymbolPatternsListHelper(const Arg *arg,
+                                           SymbolPatterns &symbolPatterns) {
+  StringRef path = arg->getValue();
+  Optional<MemoryBufferRef> buffer = readFile(path);
+  if (!buffer) {
+    error("Could not read symbol file: " + path);
+    return;
+  }
+  MemoryBufferRef mbref = *buffer;
+  for (StringRef line : args::getLines(mbref)) {
+    line = line.take_until([](char c) { return c == '#'; }).trim();
+    if (!line.empty())
+      symbolPatterns.insert(line);
+  }
+}
 static void handleSymbolPatterns(InputArgList &args,
                                  SymbolPatterns &symbolPatterns,
                                  unsigned singleOptionCode,
                                  unsigned listFileOptionCode) {
   for (const Arg *arg : args.filtered(singleOptionCode))
     symbolPatterns.insert(arg->getValue());
-  for (const Arg *arg : args.filtered(listFileOptionCode)) {
-    StringRef path = arg->getValue();
-    Optional<MemoryBufferRef> buffer = readFile(path);
-    if (!buffer) {
-      error("Could not read symbol file: " + path);
-      continue;
-    }
-    MemoryBufferRef mbref = *buffer;
-    for (StringRef line : args::getLines(mbref)) {
-      line = line.take_until([](char c) { return c == '#'; }).trim();
-      if (!line.empty())
-        symbolPatterns.insert(line);
-    }
-  }
+  for (const Arg *arg : args.filtered(listFileOptionCode))
+    handleSymbolPatternsListHelper(arg, symbolPatterns);
 }
 
 static void createFiles(const InputArgList &args) {
@@ -1406,9 +1409,53 @@
           ">>> ignoring unexports");
     config->unexportedSymbols.clear();
   }
+
+  // Imitating LD64's:
+  // -non_global_symbols_no_strip_list and -non_global_symbols_strip_list can't
+  // both be present.
+  // But -x can be used with either of these two, in which case, the last arg
+  // takes effect.
+  // (TODO: This is kind of confusing - considering disallowing using them
+  // together for a more straightforward behaviour)
+  {
+    bool hasInclude = false;
+    bool hasExclude = false;
+    for (const Arg *arg :
+         args.filtered(OPT_x, OPT_non_global_symbols_no_strip_list,
+                       OPT_non_global_symbols_strip_list)) {
+      switch (arg->getOption().getID()) {
+      case OPT_x:
+        config->localSymbolsPresence = SymtabPresence::None;
+
+        break;
+      case OPT_non_global_symbols_no_strip_list:
+        if (hasExclude) {
+          error("cannot use both -non_global_symbols_no_strip_list and "
+                "-non_global_symbols_strip_list");
+        } else {
+          hasInclude = true;
+          config->localSymbolsPresence = SymtabPresence::SelectivelyIncluded;
+          handleSymbolPatternsListHelper(arg, config->includedLocalSymbols);
+        }
+        break;
+      case OPT_non_global_symbols_strip_list:
+        if (hasInclude) {
+          error("cannot use both -non_global_symbols_no_strip_list and "
+                "-non_global_symbols_strip_list");
+        } else {
+          hasExclude = true;
+          config->localSymbolsPresence = SymtabPresence::SelectivelyExcluded;
+          handleSymbolPatternsListHelper(arg, config->excludedLocalSymbols);
+        }
+        break;
+      default:
+        llvm_unreachable("unexpected option");
+      }
+    }
+  }
   // Explicitly-exported literal symbols must be defined, but might
-  // languish in an archive if unreferenced elsewhere. Light a fire
-  // under those lazy symbols!
+  // languish in an archive if unreferenced elsewhere or if they are in the
+  // non-global strip list. Light a fire under those lazy symbols!
   for (const CachedHashStringRef &cachedName : config->exportedSymbols.literals)
     symtab->addUndefined(cachedName.val(), /*file=*/nullptr,
                          /*isWeakRef=*/false);
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -581,17 +581,14 @@
     Group<grp_symtab>;
 def x : Flag<["-"], "x">,
     HelpText<"Exclude non-global symbols from the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">,
     MetaVarName<"<path>">,
     HelpText<"Specify in <path> the non-global symbols that should be removed from the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">,
     MetaVarName<"<path>">,
     HelpText<"Specify in <path> the non-global symbols that should remain in the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def oso_prefix : Separate<["-"], "oso_prefix">,
     MetaVarName<"<path>">,
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -704,8 +704,8 @@
   OutputSegment *dataSeg = in.lazyPointers->parent;
   os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
                              dataSeg->index);
-  uint64_t offset = in.lazyPointers->addr - dataSeg->addr +
-                    sym.stubsIndex * target->wordSize;
+  uint64_t offset =
+      in.lazyPointers->addr - dataSeg->addr + sym.stubsIndex * target->wordSize;
   encodeULEB128(offset, os);
   encodeDylibOrdinal(ordinalForSymbol(sym), os);
 
@@ -963,16 +963,41 @@
     symbols.push_back({sym, strx});
   };
 
+  std::function<void(Symbol *)> localSymbolsHandler;
+  switch (config->localSymbolsPresence) {
+  case SymtabPresence::All:
+    localSymbolsHandler = [&](Symbol *sym) { addSymbol(localSymbols, sym); };
+    break;
+  case SymtabPresence::None:
+    localSymbolsHandler = [&](Symbol *) { /* Do nothing*/ };
+    break;
+  case SymtabPresence::SelectivelyIncluded:
+    localSymbolsHandler = [&](Symbol *sym) {
+      if (config->includedLocalSymbols.match(sym->getName()))
+        addSymbol(localSymbols, sym);
+    };
+    break;
+  case SymtabPresence::SelectivelyExcluded:
+    localSymbolsHandler = [&](Symbol *sym) {
+      if (!config->excludedLocalSymbols.match(sym->getName()))
+        addSymbol(localSymbols, sym);
+    };
+    break;
+  }
+
   // Local symbols aren't in the SymbolTable, so we walk the list of object
   // files to gather them.
-  for (const InputFile *file : inputFiles) {
-    if (auto *objFile = dyn_cast<ObjFile>(file)) {
-      for (Symbol *sym : objFile->symbols) {
-        if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
-          if (defined->isExternal() || !defined->isLive() ||
-              !defined->includeInSymtab)
-            continue;
-          addSymbol(localSymbols, sym);
+  // But if `-x` is set, then we don't need to.
+  if (config->localSymbolsPresence != SymtabPresence::None) {
+    for (const InputFile *file : inputFiles) {
+      if (auto *objFile = dyn_cast<ObjFile>(file)) {
+        for (Symbol *sym : objFile->symbols) {
+          if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
+            if (defined->isExternal() || !defined->isLive() ||
+                !defined->includeInSymtab)
+              continue;
+            localSymbolsHandler(sym);
+          }
         }
       }
     }
@@ -981,7 +1006,7 @@
   // __dyld_private is a local symbol too. It's linker-created and doesn't
   // exist in any object file.
   if (Defined *dyldPrivate = in.stubHelper->dyldPrivate)
-    addSymbol(localSymbols, dyldPrivate);
+    localSymbolsHandler(dyldPrivate);
 
   for (Symbol *sym : symtab->getSymbols()) {
     if (!sym->isLive())
@@ -991,7 +1016,7 @@
         continue;
       assert(defined->isExternal());
       if (defined->privateExtern)
-        addSymbol(localSymbols, defined);
+        localSymbolsHandler(defined);
       else
         addSymbol(externalSymbols, defined);
     } else if (auto *dysym = dyn_cast<DylibSymbol>(sym)) {
diff --git a/lld/test/MachO/local-symbol-output.s b/lld/test/MachO/local-symbol-output.s
new file mode 100644
--- /dev/null
+++ b/lld/test/MachO/local-symbol-output.s
@@ -0,0 +1,103 @@
+# REQUIRES: x86
+
+# RUN: rm -rf %t; split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/main.s -o %t/main.o
+
+## Check that -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
+## can't be used at the same time.
+# RUN: not %lld %t/main.o -o /dev/null \
+# RUN:       -non_global_symbols_no_strip_list %t/foo.txt \
+# RUN:       -non_global_symbols_strip_list %t/foo.txt 2>&1 | \
+# RUN:     FileCheck --check-prefix=CONFLICT %s
+
+# CONFLICT: error: cannot use both -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
+
+## Check that -x causes none of the local symbols to be emitted.
+# RUN: %lld %t/main.o -x -o %t/no_local.out
+# RUN: llvm-nm %t/no_local.out | FileCheck --check-prefix NO_LOCAL %s
+
+# NO_LOCAL-NOT: t _foo
+# NO_LOCAL-NOT: t _bar
+# NO_LOCAL-NOT: t _baz
+# NO_LOCAL: T _main
+
+## Check that when using -x with -non_global_symbols_no_strip_list, whichever appears
+## last in the command line arg list will take precedence.
+# RUN: %lld %t/main.o -x -non_global_symbols_no_strip_list %t/foo.txt -o %t/x_then_no_strip.out
+# RUN: llvm-nm %t/x_then_no_strip.out | FileCheck --check-prefix X-NO-STRIP %s
+
+# RUN: %lld %t/main.o -non_global_symbols_no_strip_list %t/foo.txt -x -o %t/no_strip_then_x.out
+# RUN: llvm-nm %t/no_strip_then_x.out | FileCheck --check-prefix NO_LOCAL %s
+
+# X-NO-STRIP-NOT: t _bar
+# X-NO-STRIP-DAG: t _foo
+# X-NO-STRIP-DAG: T _main
+
+## Check that -non_global_symbols_no_strip_list can be specified more than once
+## (The final no-strip list is the union of all these)
+# RUN: %lld %t/main.o -o %t/no_strip_multi.out \
+# RUN:    -non_global_symbols_no_strip_list %t/foo.txt \
+# RUN:    -non_global_symbols_no_strip_list %t/bar.txt
+# RUN: llvm-nm %t/no_strip_multi.out | FileCheck --check-prefix NO-STRIP-MULTI %s
+
+# NO-STRIP-MULTI-NOT: t _baz
+# NO-STRIP-MULTI-DAG: t _foo
+# NO-STRIP-MULTI-DAG: t _bar
+# NO-STRIP-MULTI-DAG: T _main
+
+## Check that when using -x with -non_global_symbols_strip_list, whichever appears
+## last in the command line arg list will take precedence.
+# RUN: %lld %t/main.o -x -non_global_symbols_strip_list %t/foo.txt -o %t/x_then_strip.out
+# RUN: llvm-nm %t/x_then_strip.out | FileCheck --check-prefix X-STRIP %s
+
+# RUN: %lld %t/main.o -non_global_symbols_strip_list %t/foo.txt -x -o %t/strip_then_x.out
+# RUN: llvm-nm %t/no_strip_then_x.out | FileCheck --check-prefix NO_LOCAL %s
+
+# X-STRIP-NOT: t _foo
+# X-STRIP-DAG: t _bar
+# X-STRIP-DAG: t _baz
+# X-STRIP-DAG: T _main
+
+## Check that -non_global_symbols_strip_list can be specified more than once
+## (The final strip list is the union of all these)
+# RUN: %lld %t/main.o -o %t/strip_multi.out \
+# RUN:    -non_global_symbols_strip_list %t/foo.txt \
+# RUN:    -non_global_symbols_strip_list %t/bar.txt
+# RUN: llvm-nm %t/strip_multi.out | FileCheck --check-prefix STRIP-MULTI %s
+
+# STRIP-MULTI-NOT: t _foo
+# STRIP-MULTI-NOT: t _bar
+# STRIP-MULTI-DAG: t _baz
+# STRIP-MULTI-DAG: T _main
+
+## Test interactions with exported_symbol.
+# RUN: %lld %t/main.o -o %t/strip_all_export_one.out \
+# RUN:    -x -exported_symbol _foo \
+# RUN:    -undefined dynamic_lookup
+# RUN: llvm-nm %t/strip_all_export_one.out | FileCheck --check-prefix STRIP-EXP %s
+
+# STRIP-EXP: U _foo
+# STRIP-EXP-EMPTY:
+
+#--- foo.txt
+_foo
+
+#--- bar.txt
+_bar
+
+#--- main.s
+
+.globl _main
+
+_foo:
+  ret
+
+_bar:
+  ret
+
+_baz:
+  ret
+
+_main:
+  callq _foo
+  ret
\ No newline at end of file