[lld/mac] reject -undefined warning and -undefined suppress with -twolevel_namespace
authorNico Weber <thakis@chromium.org>
Thu, 18 Feb 2021 13:48:07 +0000 (08:48 -0500)
committerNico Weber <thakis@chromium.org>
Sat, 20 Feb 2021 18:35:22 +0000 (13:35 -0500)
See discussion on https://reviews.llvm.org/D93263

-flat_namespace isn't implemented yet, and neither is -undefined dynamic,
so this makes -undefined pretty pointless in lld/MachO for now. But once
we implement -flat_namespace (which we need to do anyways to get check-llvm
to pass with lld as host linker), the code's already there.

Follow-up to https://reviews.llvm.org/D93263#2491865

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

lld/MachO/Config.h
lld/MachO/Driver.cpp
lld/MachO/Options.td
lld/test/MachO/treat-undef-sym.s

index 94f84b2..8ac07a1 100644 (file)
@@ -30,6 +30,11 @@ struct PlatformInfo {
   llvm::VersionTuple sdk;
 };
 
+enum class NamespaceKind {
+  twolevel,
+  flat,
+};
+
 enum class UndefinedSymbolTreatment {
   unknown,
   error,
@@ -61,6 +66,7 @@ struct Configuration {
   bool demangle = false;
   llvm::MachO::Architecture arch;
   PlatformInfo platform;
+  NamespaceKind namespaceKind = NamespaceKind::twolevel;
   UndefinedSymbolTreatment undefinedSymbolTreatment =
       UndefinedSymbolTreatment::error;
   llvm::MachO::HeaderFileType outputType;
index 7c7fd86..eaa3b8f 100644 (file)
@@ -582,18 +582,27 @@ static void handlePlatformVersion(const opt::Arg *arg) {
 
 static void handleUndefined(const opt::Arg *arg) {
   StringRef treatmentStr = arg->getValue(0);
-  config->undefinedSymbolTreatment =
+  auto treatment =
       StringSwitch<UndefinedSymbolTreatment>(treatmentStr)
           .Case("error", UndefinedSymbolTreatment::error)
           .Case("warning", UndefinedSymbolTreatment::warning)
           .Case("suppress", UndefinedSymbolTreatment::suppress)
           .Case("dynamic_lookup", UndefinedSymbolTreatment::dynamic_lookup)
           .Default(UndefinedSymbolTreatment::unknown);
-  if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::unknown) {
+  if (treatment == UndefinedSymbolTreatment::unknown) {
     warn(Twine("unknown -undefined TREATMENT '") + treatmentStr +
          "', defaulting to 'error'");
-    config->undefinedSymbolTreatment = UndefinedSymbolTreatment::error;
+    treatment = UndefinedSymbolTreatment::error;
+  } else if (config->namespaceKind == NamespaceKind::twolevel &&
+             (treatment == UndefinedSymbolTreatment::warning ||
+              treatment == UndefinedSymbolTreatment::suppress)) {
+    if (treatment == UndefinedSymbolTreatment::warning)
+      error("'-undefined warning' only valid with '-flat_namespace'");
+    else
+      error("'-undefined suppress' only valid with '-flat_namespace'");
+    treatment = UndefinedSymbolTreatment::error;
   }
+  config->undefinedSymbolTreatment = treatment;
 }
 
 static void warnIfDeprecatedOption(const opt::Option &opt) {
@@ -760,6 +769,18 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   if (const opt::Arg *arg = args.getLastArg(OPT_static, OPT_dynamic))
     config->staticLink = (arg->getOption().getID() == OPT_static);
 
+  if (const opt::Arg *arg =
+          args.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) {
+    config->namespaceKind = arg->getOption().getID() == OPT_twolevel_namespace
+                                ? NamespaceKind::twolevel
+                                : NamespaceKind::flat;
+    if (config->namespaceKind == NamespaceKind::flat) {
+      warn("Option '" + arg->getOption().getPrefixedName() +
+           "' is not yet implemented. Stay tuned...");
+      config->namespaceKind = NamespaceKind::twolevel;
+    }
+  }
+
   config->systemLibraryRoots = getSystemLibraryRoots(args);
   config->librarySearchPaths =
       getLibrarySearchPaths(args, config->systemLibraryRoots);
index adff1d1..e2313a8 100644 (file)
@@ -448,9 +448,12 @@ def alias_list : Separate<["-"], "alias_list">,
      Flags<[HelpHidden]>,
      Group<grp_resolve>;
 def flat_namespace : Flag<["-"], "flat_namespace">,
-     HelpText<"Resolve symbols from all dylibs, both direct & transitive. Do not record source libraries: dyld must re-search at runtime and use the first definition found">,
+     HelpText<"Resolve symbols from all dylibs, both direct and transitive. Do not record source libraries: dyld must re-search at runtime and use the first definition found">,
      Flags<[HelpHidden]>,
      Group<grp_resolve>;
+def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
+     HelpText<"Make dyld look up symbols by (dylib,name) pairs (default)">,
+     Group<grp_resolve>;
 def u : Separate<["-"], "u">,
      MetaVarName<"<symbol>">,
      HelpText<"Require that <symbol> be defined for the link to succeed">,
@@ -1302,10 +1305,6 @@ def threaded_starts_section : Flag<["-"], "threaded_starts_section">,
      HelpText<"This option is undocumented in ld64">,
      Flags<[HelpHidden]>,
      Group<grp_undocumented>;
-def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
-     HelpText<"This option is undocumented in ld64">,
-     Flags<[HelpHidden]>,
-     Group<grp_undocumented>;
 def verbose_optimization_hints : Flag<["-"], "verbose_optimization_hints">,
      HelpText<"This option is undocumented in ld64">,
      Flags<[HelpHidden]>,
index 3628c30..854f477 100644 (file)
@@ -5,14 +5,29 @@
 # RUN:     FileCheck %s -check-prefix=UNKNOWN
 # RUN: not %lld -undefined error -o /dev/null %t.o 2>&1 | \
 # RUN:     FileCheck %s -check-prefix=ERROR
-# RUN:     %no_fatal_warnings_lld -undefined warning -o /dev/null %t.o 2>&1 | \
-# RUN:     FileCheck %s -check-prefix=WARNING
-# RUN:     %lld -undefined suppress -o /dev/null %t.o 2>&1 | \
-# RUN:     FileCheck %s -check-prefix=SUPPRESS --allow-empty
+
+# RUN: not %lld -undefined warning -o /dev/null %t.o 2>&1 | \
+# RUN:     FileCheck %s -check-prefix=INVAL-WARNING
+# RUN: not %lld -undefined suppress -o /dev/null %t.o 2>&1 | \
+# RUN:     FileCheck %s -check-prefix=INVAL-SUPPRESS
+
+# FIXME: Enable these -undefined checks once -flat_namespace is implemented.
+# RN: %no_fatal_warnings_lld -flat_namespace -undefined warning \
+# RN:     -o /dev/null %t.o 2>&1 | \
+# RN:     FileCheck %s -check-prefix=WARNING
+# RN: %lld -flat_namespace -undefined suppress -o /dev/null %t.o 2>&1 | \
+# RN:     FileCheck %s -check-prefix=SUPPRESS --allow-empty
 
 # ERROR: error: undefined symbol: _bar
 # ERROR-NEXT: >>> referenced by
 
+# INVAL-WARNING: error: '-undefined warning' only valid with '-flat_namespace'
+# INVAL-WARNING-NEXT: error: undefined symbol: _bar
+
+# INVAL-SUPPRESS: error: '-undefined suppress' only valid with '-flat_namespace'
+# INVAL-SUPPRESS-NEXT: error: undefined symbol: _bar
+
+
 # WARNING: warning: undefined symbol: _bar
 # WARNING-NEXT: >>> referenced by