[COFF] support /ERRORLIMIT option

Summary: This adds support for reporting multiple errors in a single invocation of lld-link. The limit defaults to 20 and can be changed with the /ERRORLIMIT command line parameter, or set to unlimited by passing a value of 0.

Reviewers: pcc, ruiu

Reviewed By: ruiu

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

llvm-svn: 295507
This commit is contained in:
Bob Haarman 2017-02-17 22:46:06 +00:00
parent 7ec2c72095
commit b96b10102a
5 changed files with 92 additions and 29 deletions

View File

@ -63,7 +63,7 @@ bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
(ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
Driver = make<LinkerDriver>();
Driver->link(Args);
return true;
return !ErrorCount;
}
// Drop directory components and replace extension with ".exe" or ".dll".
@ -126,9 +126,10 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
if (Magic == file_magic::bitcode)
return Symtab.addFile(make<BitcodeFile>(MBRef));
if (Magic == file_magic::coff_cl_gl_object)
fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
"Recompile without /GL");
Symtab.addFile(make<ObjectFile>(MBRef));
else
Symtab.addFile(make<ObjectFile>(MBRef));
}
void LinkerDriver::enqueuePath(StringRef Path) {
@ -138,8 +139,9 @@ void LinkerDriver::enqueuePath(StringRef Path) {
enqueueTask([=]() {
auto MBOrErr = Future->get();
if (MBOrErr.second)
fatal(MBOrErr.second, "could not open " + PathStr);
Driver->addBuffer(std::move(MBOrErr.first));
error("could not open " + PathStr + ": " + MBOrErr.second.message());
else
Driver->addBuffer(std::move(MBOrErr.first));
});
if (Config->OutputFile == "")
@ -155,12 +157,14 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
}
InputFile *Obj;
if (Magic == file_magic::coff_object)
if (Magic == file_magic::coff_object) {
Obj = make<ObjectFile>(MB);
else if (Magic == file_magic::bitcode)
} else if (Magic == file_magic::bitcode) {
Obj = make<BitcodeFile>(MB);
else
fatal("unknown file type: " + MB.getBufferIdentifier());
} else {
error("unknown file type: " + MB.getBufferIdentifier());
return;
}
Obj->ParentName = ParentName;
Symtab.addFile(Obj);
@ -238,7 +242,7 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_throwingnew:
break;
default:
fatal(Arg->getSpelling() + " is not allowed in .drectve");
error(Arg->getSpelling() + " is not allowed in .drectve");
}
}
}
@ -456,6 +460,15 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Parse command line options.
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
// Handle /errorlimit early, because error() depends on it.
if (auto *Arg = Args.getLastArg(OPT_errorlimit)) {
int N = 20;
StringRef S = Arg->getValue();
if (S.getAsInteger(10, N))
error(Arg->getSpelling() + " number expected, but got " + S);
Config->ErrorLimit = N;
}
// Handle /help
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
@ -514,8 +527,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
if (!Args.hasArg(OPT_dll))
fatal("/noentry must be specified with /dll");
Config->NoEntry = true;
error("/noentry must be specified with /dll");
else
Config->NoEntry = true;
}
// Handle /dll
@ -526,10 +540,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /fixed
if (Args.hasArg(OPT_fixed)) {
if (Args.hasArg(OPT_dynamicbase))
fatal("/fixed must not be specified with /dynamicbase");
Config->Relocatable = false;
Config->DynamicBase = false;
if (Args.hasArg(OPT_dynamicbase)) {
error("/fixed must not be specified with /dynamicbase");
} else {
Config->Relocatable = false;
Config->DynamicBase = false;
}
}
// Handle /machine
@ -601,24 +617,24 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
StringRef OptLevel = StringRef(S).substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
fatal("/opt:lldlto: invalid optimization level: " + OptLevel);
error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue;
}
if (StringRef(S).startswith("lldltojobs=")) {
StringRef Jobs = StringRef(S).substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
fatal("/opt:lldltojobs: invalid job count: " + Jobs);
error("/opt:lldltojobs: invalid job count: " + Jobs);
continue;
}
if (StringRef(S).startswith("lldltopartitions=")) {
StringRef N = StringRef(S).substr(17);
if (N.getAsInteger(10, Config->LTOPartitions) ||
Config->LTOPartitions == 0)
fatal("/opt:lldltopartitions: invalid partition count: " + N);
error("/opt:lldltopartitions: invalid partition count: " + N);
continue;
}
if (S != "ref" && S != "lbr" && S != "nolbr")
fatal("/opt: unknown option: " + S);
error("/opt: unknown option: " + S);
}
}
@ -686,6 +702,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Optional<StringRef> Path = findLib(Arg->getValue()))
enqueuePath(*Path);
if (ErrorCount)
return;
// Windows specific -- Create a resource file containing a manifest file.
if (Config->Manifest == Configuration::Embed)
addBuffer(createManifestRes());
@ -730,11 +749,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
StringRef S = findDefaultEntry();
if (S.empty())
fatal("entry point must be defined");
Config->Entry = addUndefined(S);
if (Config->Verbose)
outs() << "Entry name inferred: " << S << "\n";
if (S.empty()) {
error("entry point must be defined");
} else {
Config->Entry = addUndefined(S);
if (Config->Verbose)
outs() << "Entry name inferred: " << S << "\n";
}
}
// Handle /export
@ -819,6 +840,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
addUndefined(mangle("_load_config_used"));
} while (run());
if (ErrorCount)
return;
// If /msvclto is given, we use the MSVC linker to link LTO output files.
// This is useful because MSVC link.exe can generate complete PDBs.
if (Args.hasArg(OPT_msvclto)) {
@ -844,10 +868,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Handle /safeseh.
if (Args.hasArg(OPT_safeseh))
if (Args.hasArg(OPT_safeseh)) {
for (ObjectFile *File : Symtab.ObjectFiles)
if (!File->SEHCompat)
fatal("/safeseh: " + File->getName() + " is not compatible with SEH");
error("/safeseh: " + File->getName() + " is not compatible with SEH");
if (ErrorCount)
return;
}
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.

View File

@ -59,12 +59,13 @@ void error(const Twine &Msg) {
std::lock_guard<std::mutex> Lock(Mu);
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
errs() << "error " << ErrorCount << " of " << Config->ErrorLimit << "\n";
print("error: ", raw_ostream::RED);
*ErrorOS << Msg << "\n";
} else if (ErrorCount == Config->ErrorLimit) {
print("error: ", raw_ostream::RED);
*ErrorOS << "too many errors emitted, stopping now"
<< " (use -error-limit=0 to see all errors)\n";
<< " (use /ERRORLIMIT:0 to see all errors)\n";
exitLld(1);
}

View File

@ -21,6 +21,8 @@ def base : P<"base", "Base address of the program">;
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">;
def errorlimit : P<"errorlimit",
"Maximum number of errors to emit before stopping (0 = no limit)">;
def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">;

View File

@ -193,7 +193,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
}
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
fatal("duplicate symbol: " + toString(*Existing->body()) + " in " +
error("duplicate symbol: " + toString(*Existing->body()) + " in " +
toString(Existing->body()->getFile()) + " and in " +
(NewFile ? toString(NewFile) : "(internal)"));
}

View File

@ -0,0 +1,33 @@
RUN: not lld-link 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 \
RUN: 21 22 2>&1 | FileCheck -check-prefix=DEFAULT %s
DEFAULT: could not open 01
DEFAULT: could not open 20
DEFAULT-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
DEFAULT-NOT: could not open 21
RUN: not lld-link /ERRORLIMIT:5 01 02 03 04 05 06 07 08 09 10 2>&1 \
RUN: | FileCheck -check-prefix=LIMIT5 %s
LIMIT5: could not open 01
LIMIT5: could not open 05
LIMIT5-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
LIMIT5-NOT: could not open 06
RUN: not lld-link /ERRORLIMIT:0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
RUN: 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=UNLIMITED %s
UNLIMITED: could not open 01
UNLIMITED: could not open 20
UNLIMITED: could not open 21
UNLIMITED: could not open 22
UNLIMITED-NOT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
RUN: not lld-link /ERRORLIMIT:XYZ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 \
RUN: 15 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=WRONG %s
WRONG: /ERRORLIMIT: number expected, but got XYZ
WRONG: could not open 01
WRONG: could not open 19
WRONG-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
WRONG-NOT: could not open 20