From cfc32267e27f77211ee5eb6e30c52ab2c7740e6e Mon Sep 17 00:00:00 2001 From: serge-sans-paille Date: Mon, 19 Oct 2020 13:19:52 +0200 Subject: [PATCH] Provide a hook to customize missing library error handling 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 | 45 ++++++++++++++++++++++++++ lld/ELF/Driver.cpp | 6 +++- lld/ELF/Options.td | 3 ++ lld/docs/ReleaseNotes.rst | 3 +- lld/docs/index.rst | 1 + lld/docs/ld.lld.1 | 11 +++++++ lld/include/lld/Common/ErrorHandler.h | 7 ++++ lld/test/ELF/error-handling-script-linux.test | 17 ++++++++++ lld/test/ELF/error-handling-script-windows.bat | 15 +++++++++ lld/test/ELF/lit.local.cfg | 2 +- 10 files changed, 107 insertions(+), 3 deletions(-) create mode 100755 lld/test/ELF/error-handling-script-linux.test create mode 100644 lld/test/ELF/error-handling-script-windows.bat diff --git a/lld/Common/ErrorHandler.cpp b/lld/Common/ErrorHandler.cpp index 3c3609e..4d6a039 100644 --- a/lld/Common/ErrorHandler.cpp +++ b/lld/Common/ErrorHandler.cpp @@ -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 #include @@ -226,6 +227,50 @@ void ErrorHandler::error(const Twine &msg) { exitLld(1); } +void ErrorHandler::error(const Twine &msg, ErrorTag tag, + ArrayRef args) { + if (errorHandlingScript.empty()) { + error(msg); + return; + } + SmallVector 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); diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 30575f6..fbc5e44 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -286,7 +286,7 @@ void LinkerDriver::addLibrary(StringRef name) { if (Optional 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 = diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 9f1fbd1..37d6fda 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -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", diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index f50c306..e0b17ca 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -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 `_) Breaking changes ---------------- diff --git a/lld/docs/index.rst b/lld/docs/index.rst index 900ad821..40da6d7 100644 --- a/lld/docs/index.rst +++ b/lld/docs/index.rst @@ -174,6 +174,7 @@ document soon. WebAssembly windows_support missingkeyfunction + error_handling_script Partitions ReleaseNotes ELF/linker_script diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 5edeaf8..0de278a 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -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. diff --git a/lld/include/lld/Common/ErrorHandler.h b/lld/include/lld/Common/ErrorHandler.h index 79a5940..64f3630 100644 --- a/lld/include/lld/Common/ErrorHandler.h +++ b/lld/include/lld/Common/ErrorHandler.h @@ -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 cleanupCallback; void error(const Twine &msg); + void error(const Twine &msg, ErrorTag tag, ArrayRef 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 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 index 0000000..c499680 --- /dev/null +++ b/lld/test/ELF/error-handling-script-linux.test @@ -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 index 0000000..64c4e95 --- /dev/null +++ b/lld/test/ELF/error-handling-script-windows.bat @@ -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 %*" diff --git a/lld/test/ELF/lit.local.cfg b/lld/test/ELF/lit.local.cfg index 284077d..98c97b6 100644 --- a/lld/test/ELF/lit.local.cfg +++ b/lld/test/ELF/lit.local.cfg @@ -1 +1 @@ -config.suffixes = ['.test', '.s', '.ll'] +config.suffixes = ['.test', '.s', '.ll', '.bat'] -- 2.7.4