Index: lld/Common/ErrorHandler.cpp =================================================================== --- lld/Common/ErrorHandler.cpp +++ 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,41 @@ 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); + switch (Res) { + case 0: + error(msg); + return; + case -1: + error(msg + "\n'" + errorHandlingScript + + "': error handling script failed to execute"); + return; + case -2: + error(msg + "\n'" + errorHandlingScript + + "': error handling script crash or timeout"); + return; + default: + error(msg + "\n'" + errorHandlingScript + + "': error handling script exited with code " + Twine(Res)); + } +} + void ErrorHandler::fatal(const Twine &msg) { error(msg); exitLld(1); Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -286,7 +286,7 @@ 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 @@ 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: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -179,6 +179,9 @@ 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: lld/docs/ReleaseNotes.rst =================================================================== --- lld/docs/ReleaseNotes.rst +++ lld/docs/ReleaseNotes.rst @@ -24,7 +24,8 @@ ELF Improvements ---------------- -* ... +* ``--error-handling-script`` is added to allow for user-defined handlers upon + missing libraries. (`D87758 `_) Breaking changes ---------------- Index: lld/docs/error_handling_script.rst =================================================================== --- /dev/null +++ lld/docs/error_handling_script.rst @@ -0,0 +1,35 @@ +===================== +Error Handling Script +===================== + +LLD provides the ability to hook into some error handling routines through a +user-provided script specified with ``--error-handling-script=`` +when certain errors are encountered. This document specifies the requirements of +such a script. + +Generic Requirements +==================== + +The script is expected to be available in the ``PATH`` or to be provided using a +full path. It must be executable. It is executed in the same environment as the +parent process. + +Arguments +========= + +LLD calls the error handling script using the following arguments:: + + error-handling-script + +The following tags are supported: + +:``missing-lib``: + indicates that LLD failed to find a library. The library name is specified as + the first argument, e.g. ``error-handling-script missing-lib mylib`` + +Return Value +============ + +Upon success, the script is expected to return 0. A non-zero value is +interpreted as an error and reported to the user. In both cases, LLD still +reports the original error. Index: lld/docs/index.rst =================================================================== --- lld/docs/index.rst +++ lld/docs/index.rst @@ -174,6 +174,7 @@ WebAssembly windows_support missingkeyfunction + error_handling_script Partitions ReleaseNotes ELF/linker_script Index: lld/docs/ld.lld.1 =================================================================== --- lld/docs/ld.lld.1 +++ lld/docs/ld.lld.1 @@ -181,6 +181,18 @@ 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 +when failing to find a library, with +.Ar tag +as first argument, and the name of the missing library as second argument. +The script is expected to return 0 on success, 1 if +.Ar tag +is not supported. Any other value is considered a generic error. +.Ar tag +may be +.Cm missing-lib. .It Fl -execute-only Mark executable sections unreadable. This option is currently only supported on AArch64. Index: lld/include/lld/Common/ErrorHandler.h =================================================================== --- lld/include/lld/Common/ErrorHandler.h +++ lld/include/lld/Common/ErrorHandler.h @@ -89,11 +89,14 @@ 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 @@ 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 @@ 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); } Index: lld/test/ELF/error-handling-script-linux.test =================================================================== --- /dev/null +++ lld/test/ELF/error-handling-script-linux.test @@ -0,0 +1,14 @@ +#!/bin/sh +# REQUIRES: x86 +# UNSUPPORTED: system-windows +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t.o +# RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s %t.o 2>&1 | FileCheck --check-prefix=CHECK-NORMAL %s +# RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s.nope %t.o 2>&1 | FileCheck --check-prefix=CHECK-DOES-NOT-EXIST %s + +# CHECK-NORMAL: script: info: called with missing-lib idontexist +# CHECK-NORMAL-NEXT: ld.lld: error: unable to find library -lidontexist + +# CHECK-DOES-NOT-EXIST: ld.lld: error: unable to find library -lidontexist +# CHECK-DOES-NOT-EXIST-NEXT: '{{.*}}': error handling script failed to execute + +echo "script: info: called with $*" Index: lld/test/ELF/error-handling-script-windows.bat =================================================================== --- /dev/null +++ lld/test/ELF/error-handling-script-windows.bat @@ -0,0 +1,12 @@ +:: REQUIRES: x86, system-windows +:: RUN: echo | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o +:: RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s %t.o 2>&1 | FileCheck --check-prefix=CHECK-NORMAL %s +:: RUN: not ld.lld -o /dev/null -lidontexist --error-handling-script=%s.nope %t.o 2>&1 | FileCheck --check-prefix=CHECK-DOES-NOT-EXIST %s +:: +:: CHECK-NORMAL: ld.lld: error: unable to find library -lidontexist +:: CHECK-NEXT-NORMAL: Script called with missing-lib idontexist +:: +:: CHECK-DOES-NOT-EXIST: ld.lld: error: unable to find library -lidontexist +:: CHECK-NEXT-DOES-NOT-EXIST: ld.lld: error: failed to execute error handling script: + +echo "Script called with %*" Index: lld/test/ELF/lit.local.cfg =================================================================== --- lld/test/ELF/lit.local.cfg +++ lld/test/ELF/lit.local.cfg @@ -1 +1 @@ -config.suffixes = ['.test', '.s', '.ll'] +config.suffixes = ['.test', '.s', '.ll', '.bat']