Provide a hook to customize missing library error handling
authorserge-sans-paille <sguelton@redhat.com>
Mon, 19 Oct 2020 11:19:52 +0000 (13:19 +0200)
committerserge-sans-paille <sguelton@redhat.com>
Tue, 3 Nov 2020 10:01:29 +0000 (11:01 +0100)
Make it possible for lld users to provide a custom script that would help to
find missing libraries. A possible scenario could be:

    % clang /tmp/a.c -fuse-ld=lld -loauth -Wl,--error-handling-script=/tmp/addLibrary.py
    unable to find library -loauth
    looking for relevant packages to provides that library

        liboauth-0.9.7-4.el7.i686
        liboauth-devel-0.9.7-4.el7.i686
        liboauth-0.9.7-4.el7.x86_64
        liboauth-devel-0.9.7-4.el7.x86_64
        pix-1.6.1-3.el7.x86_64

Where addLibrary would be called with the missing library name as first argument
(in that case addLibrary.py oauth)

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

lld/Common/ErrorHandler.cpp
lld/ELF/Driver.cpp
lld/ELF/Options.td
lld/docs/ReleaseNotes.rst
lld/docs/index.rst
lld/docs/ld.lld.1
lld/include/lld/Common/ErrorHandler.h
lld/test/ELF/error-handling-script-linux.test [new file with mode: 0755]
lld/test/ELF/error-handling-script-windows.bat [new file with mode: 0644]
lld/test/ELF/lit.local.cfg

index 3c3609e..4d6a039 100644 (file)
@@ -16,6 +16,7 @@
 #include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
 #include "llvm/Support/raw_ostream.h"
 #include <mutex>
 #include <regex>
@@ -226,6 +227,50 @@ void ErrorHandler::error(const Twine &msg) {
     exitLld(1);
 }
 
+void ErrorHandler::error(const Twine &msg, ErrorTag tag,
+                         ArrayRef<StringRef> args) {
+  if (errorHandlingScript.empty()) {
+    error(msg);
+    return;
+  }
+  SmallVector<StringRef, 4> scriptArgs;
+  scriptArgs.push_back(errorHandlingScript);
+  switch (tag) {
+  case ErrorTag::LibNotFound:
+    scriptArgs.push_back("missing-lib");
+    break;
+  default:
+    llvm_unreachable("unsupported ErrorTag");
+  }
+  scriptArgs.insert(scriptArgs.end(), args.begin(), args.end());
+  int res = llvm::sys::ExecuteAndWait(errorHandlingScript, scriptArgs);
+  if (res == 0) {
+    return error(msg);
+  } else {
+    // Temporarily disable error limit to make sure the two calls to error(...)
+    // only count as one.
+    uint64_t currentErrorLimit = errorLimit;
+    errorLimit = 0;
+    error(msg);
+    errorLimit = currentErrorLimit;
+    --errorCount;
+
+    switch (res) {
+    case -1:
+      error("error handling script '" + errorHandlingScript +
+            "' failed to execute");
+      break;
+    case -2:
+      error("error handling script '" + errorHandlingScript +
+            "' crashed or timeout");
+      break;
+    default:
+      error("error handling script '" + errorHandlingScript +
+            "' exited with code " + Twine(res));
+    }
+  }
+}
+
 void ErrorHandler::fatal(const Twine &msg) {
   error(msg);
   exitLld(1);
index 30575f6..fbc5e44 100644 (file)
@@ -286,7 +286,7 @@ void LinkerDriver::addLibrary(StringRef name) {
   if (Optional<std::string> path = searchLibrary(name))
     addFile(*path, /*withLOption=*/true);
   else
-    error("unable to find library -l" + name);
+    error("unable to find library -l" + name, ErrorTag::LibNotFound, {name});
 }
 
 // This function is called on startup. We need this for LTO since
@@ -944,6 +944,10 @@ static void readConfigs(opt::InputArgList &args) {
   config->enableNewDtags =
       args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
   config->entry = args.getLastArgValue(OPT_entry);
+
+  errorHandler().errorHandlingScript =
+      args.getLastArgValue(OPT_error_handling_script);
+
   config->executeOnly =
       args.hasFlag(OPT_execute_only, OPT_no_execute_only, false);
   config->exportDynamic =
index 9f1fbd1..37d6fda 100644 (file)
@@ -179,6 +179,9 @@ defm error_limit:
 def error_unresolved_symbols: F<"error-unresolved-symbols">,
   HelpText<"Report unresolved symbols as errors">;
 
+defm error_handling_script: EEq<"error-handling-script",
+    "Specify an error handling script">;
+
 defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
 
 defm execute_only: BB<"execute-only",
index f50c306..e0b17ca 100644 (file)
@@ -24,7 +24,8 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 
-* ...
+* ``--error-handling-script`` is added to allow for user-defined handlers upon
+  missing libraries. (`D87758 <https://reviews.llvm.org/D87758>`_)
 
 Breaking changes
 ----------------
index 900ad82..40da6d7 100644 (file)
@@ -174,6 +174,7 @@ document soon.
    WebAssembly
    windows_support
    missingkeyfunction
+   error_handling_script
    Partitions
    ReleaseNotes
    ELF/linker_script
index 5edeaf8..0de278a 100644 (file)
@@ -181,6 +181,17 @@ Maximum number of errors to emit before stopping.
 A value of zero indicates that there is no limit.
 .It Fl -error-unresolved-symbols
 Report unresolved symbols as errors.
+.It Fl -error-handing-script Ns = Ns Ar script_path
+Call script
+.Ar script_path
+upon some error, with
+.Ar tag
+as first argument, and an extra parameter as second argument. The script is
+expected to return 0 on success. Any other value is considered a generic error.
+.Ar tag
+may be
+.Cm missing-lib
+followed by the name of the missing library.
 .It Fl -execute-only
 Mark executable sections unreadable.
 This option is currently only supported on AArch64.
index 79a5940..64f3630 100644 (file)
@@ -89,11 +89,14 @@ extern llvm::raw_ostream *stderrOS;
 llvm::raw_ostream &outs();
 llvm::raw_ostream &errs();
 
+enum class ErrorTag { LibNotFound };
+
 class ErrorHandler {
 public:
   uint64_t errorCount = 0;
   uint64_t errorLimit = 20;
   StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
+  StringRef errorHandlingScript;
   StringRef logName = "lld";
   bool exitEarly = true;
   bool fatalWarnings = false;
@@ -103,6 +106,7 @@ public:
   std::function<void()> cleanupCallback;
 
   void error(const Twine &msg);
+  void error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args);
   LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg);
   void log(const Twine &msg);
   void message(const Twine &msg);
@@ -126,6 +130,9 @@ private:
 ErrorHandler &errorHandler();
 
 inline void error(const Twine &msg) { errorHandler().error(msg); }
+inline void error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) {
+  errorHandler().error(msg, tag, args);
+}
 inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
   errorHandler().fatal(msg);
 }
diff --git a/lld/test/ELF/error-handling-script-linux.test b/lld/test/ELF/error-handling-script-linux.test
new file mode 100755 (executable)
index 0000000..c499680
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+# REQUIRES: x86
+# UNSUPPORTED: system-windows
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t0.o
+# RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s %t0.o 2>&1 |\
+# RUN:   FileCheck --check-prefix=CHECK-LIB %s
+# RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s.nope %t0.o 2>&1 |\
+# RUN:   FileCheck --check-prefix=CHECK-SCRIPT-DOES-NOT-EXIST -DFILE=%s.nope %s
+
+# CHECK-LIB:      script: info: called with missing-lib idontexist
+# CHECK-LIB-NEXT: ld.lld: error: unable to find library -lidontexist
+
+# CHECK-SCRIPT-DOES-NOT-EXIST:      ld.lld: error: unable to find library -lidontexist
+# CHECK-SCRIPT-DOES-NOT-EXIST-NEXT: ld.lld: error: error handling script '[[FILE]]' failed to execute
+
+echo "script: info: called with $*"
diff --git a/lld/test/ELF/error-handling-script-windows.bat b/lld/test/ELF/error-handling-script-windows.bat
new file mode 100644 (file)
index 0000000..64c4e95
--- /dev/null
@@ -0,0 +1,15 @@
+:: REQUIRES: x86, system-windows
+:: RUN: echo | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t0.o
+:: RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s %t0.o 2>&1 |\
+:: RUN:   FileCheck --check-prefix=CHECK-LIB %s
+:: RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s.nope %t0.o 2>&1 |\
+:: RUN:   FileCheck --check-prefix=CHECK-SCRIPT-DOES-NOT-EXIST -DFILE=%s.nope %s
+::
+:: CHECK-LIB:      script: info: called with missing-lib idontexist
+:: CHECK-LIB-NEXT: ld.lld: error: unable to find library -lidontexist
+
+:: CHECK-SCRIPT-DOES-NOT-EXIST:      ld.lld: error: unable to find library -lidontexist
+:: CHECK-SCRIPT-DOES-NOT-EXIST-NEXT: ld.lld: error: error handling script '[[FILE]]' failed to execute
+
+@echo off
+echo "script: info: called with %*"
index 284077d..98c97b6 100644 (file)
@@ -1 +1 @@
-config.suffixes = ['.test', '.s', '.ll']
+config.suffixes = ['.test', '.s', '.ll', '.bat']