Skip to content

Commit

Permalink
Provide a hook to customize missing library error handling
Browse files Browse the repository at this point in the history
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
  • Loading branch information
serge-sans-paille committed Nov 3, 2020
1 parent 90131e3 commit cfc3226
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 3 deletions.
45 changes: 45 additions & 0 deletions lld/Common/ErrorHandler.cpp
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion lld/ELF/Driver.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Options.td
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion lld/docs/ReleaseNotes.rst
Expand Up @@ -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
----------------
Expand Down
1 change: 1 addition & 0 deletions lld/docs/index.rst
Expand Up @@ -174,6 +174,7 @@ document soon.
WebAssembly
windows_support
missingkeyfunction
error_handling_script
Partitions
ReleaseNotes
ELF/linker_script
Expand Down
11 changes: 11 additions & 0 deletions lld/docs/ld.lld.1
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions lld/include/lld/Common/ErrorHandler.h
Expand Up @@ -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;
Expand All @@ -103,6 +106,7 @@ class ErrorHandler {
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);
Expand All @@ -126,6 +130,9 @@ class ErrorHandler {
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);
}
Expand Down
17 changes: 17 additions & 0 deletions 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 $*"
15 changes: 15 additions & 0 deletions 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 %*"
2 changes: 1 addition & 1 deletion lld/test/ELF/lit.local.cfg
@@ -1 +1 @@
config.suffixes = ['.test', '.s', '.ll']
config.suffixes = ['.test', '.s', '.ll', '.bat']

0 comments on commit cfc3226

Please sign in to comment.