[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.

This is a new attempt after r295507, which was reverted because opening files raced with exiting early, causing the test to be flaky. This version avoids the race by exiting before calling enqueuePath.

Reviewers: pcc, ruiu

Reviewed By: ruiu

Subscribers: llvm-commits, dblaikie

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

llvm-svn: 299496
This commit is contained in:
Bob Haarman 2017-04-05 00:43:54 +00:00
parent 2c7171bf3c
commit ac8f7fc07b
5 changed files with 82 additions and 25 deletions

View File

@ -59,7 +59,7 @@ bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
(ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); (ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
Driver = make<LinkerDriver>(); Driver = make<LinkerDriver>();
Driver->link(Args); Driver->link(Args);
return true; return !ErrorCount;
} }
// Drop directory components and replace extension with ".exe" or ".dll". // Drop directory components and replace extension with ".exe" or ".dll".
@ -121,9 +121,11 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
return Symtab.addFile(make<ArchiveFile>(MBRef)); return Symtab.addFile(make<ArchiveFile>(MBRef));
if (Magic == file_magic::bitcode) if (Magic == file_magic::bitcode)
return Symtab.addFile(make<BitcodeFile>(MBRef)); return Symtab.addFile(make<BitcodeFile>(MBRef));
if (Magic == file_magic::coff_cl_gl_object) 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"); "Recompile without /GL");
else
Symtab.addFile(make<ObjectFile>(MBRef)); Symtab.addFile(make<ObjectFile>(MBRef));
} }
@ -134,7 +136,8 @@ void LinkerDriver::enqueuePath(StringRef Path) {
enqueueTask([=]() { enqueueTask([=]() {
auto MBOrErr = Future->get(); auto MBOrErr = Future->get();
if (MBOrErr.second) if (MBOrErr.second)
fatal(MBOrErr.second, "could not open " + PathStr); error("could not open " + PathStr + ": " + MBOrErr.second.message());
else
Driver->addBuffer(std::move(MBOrErr.first)); Driver->addBuffer(std::move(MBOrErr.first));
}); });
} }
@ -148,12 +151,14 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
} }
InputFile *Obj; InputFile *Obj;
if (Magic == file_magic::coff_object) if (Magic == file_magic::coff_object) {
Obj = make<ObjectFile>(MB); Obj = make<ObjectFile>(MB);
else if (Magic == file_magic::bitcode) } else if (Magic == file_magic::bitcode) {
Obj = make<BitcodeFile>(MB); Obj = make<BitcodeFile>(MB);
else } else {
fatal("unknown file type: " + MB.getBufferIdentifier()); error("unknown file type: " + MB.getBufferIdentifier());
return;
}
Obj->ParentName = ParentName; Obj->ParentName = ParentName;
Symtab.addFile(Obj); Symtab.addFile(Obj);
@ -230,7 +235,7 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_throwingnew: case OPT_throwingnew:
break; break;
default: default:
fatal(Arg->getSpelling() + " is not allowed in .drectve"); error(Arg->getSpelling() + " is not allowed in .drectve");
} }
} }
} }
@ -581,6 +586,15 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
V.push_back(Arg->getValue()); V.push_back(Arg->getValue());
cl::ParseCommandLineOptions(V.size(), V.data()); cl::ParseCommandLineOptions(V.size(), V.data());
// 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 // Handle /help
if (Args.hasArg(OPT_help)) { if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]); printHelp(ArgsArr[0]);
@ -638,9 +652,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /noentry // Handle /noentry
if (Args.hasArg(OPT_noentry)) { if (Args.hasArg(OPT_noentry)) {
if (!Args.hasArg(OPT_dll)) if (Args.hasArg(OPT_dll))
fatal("/noentry must be specified with /dll");
Config->NoEntry = true; Config->NoEntry = true;
else
error("/noentry must be specified with /dll");
} }
// Handle /dll // Handle /dll
@ -651,11 +666,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /fixed // Handle /fixed
if (Args.hasArg(OPT_fixed)) { if (Args.hasArg(OPT_fixed)) {
if (Args.hasArg(OPT_dynamicbase)) if (Args.hasArg(OPT_dynamicbase)) {
fatal("/fixed must not be specified with /dynamicbase"); error("/fixed must not be specified with /dynamicbase");
} else {
Config->Relocatable = false; Config->Relocatable = false;
Config->DynamicBase = false; Config->DynamicBase = false;
} }
}
// Handle /machine // Handle /machine
if (auto *Arg = Args.getLastArg(OPT_machine)) if (auto *Arg = Args.getLastArg(OPT_machine))
@ -726,24 +743,24 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
StringRef OptLevel = StringRef(S).substr(7); StringRef OptLevel = StringRef(S).substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3) Config->LTOOptLevel > 3)
fatal("/opt:lldlto: invalid optimization level: " + OptLevel); error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue; continue;
} }
if (StringRef(S).startswith("lldltojobs=")) { if (StringRef(S).startswith("lldltojobs=")) {
StringRef Jobs = StringRef(S).substr(11); StringRef Jobs = StringRef(S).substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
fatal("/opt:lldltojobs: invalid job count: " + Jobs); error("/opt:lldltojobs: invalid job count: " + Jobs);
continue; continue;
} }
if (StringRef(S).startswith("lldltopartitions=")) { if (StringRef(S).startswith("lldltopartitions=")) {
StringRef N = StringRef(S).substr(17); StringRef N = StringRef(S).substr(17);
if (N.getAsInteger(10, Config->LTOPartitions) || if (N.getAsInteger(10, Config->LTOPartitions) ||
Config->LTOPartitions == 0) Config->LTOPartitions == 0)
fatal("/opt:lldltopartitions: invalid partition count: " + N); error("/opt:lldltopartitions: invalid partition count: " + N);
continue; continue;
} }
if (S != "ref" && S != "lbr" && S != "nolbr") if (S != "ref" && S != "lbr" && S != "nolbr")
fatal("/opt: unknown option: " + S); error("/opt: unknown option: " + S);
} }
} }
@ -801,6 +818,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->MapFile = getMapFile(Args); Config->MapFile = getMapFile(Args);
if (ErrorCount)
return;
// Create a list of input files. Files can be given as arguments // Create a list of input files. Files can be given as arguments
// for /defaultlib option. // for /defaultlib option.
std::vector<MemoryBufferRef> MBs; std::vector<MemoryBufferRef> MBs;
@ -959,6 +979,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
addUndefined(mangle("_load_config_used")); addUndefined(mangle("_load_config_used"));
} while (run()); } while (run());
if (ErrorCount)
return;
// If /msvclto is given, we use the MSVC linker to link LTO output files. // 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. // This is useful because MSVC link.exe can generate complete PDBs.
if (Args.hasArg(OPT_msvclto)) { if (Args.hasArg(OPT_msvclto)) {
@ -983,10 +1006,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
} }
// Handle /safeseh. // Handle /safeseh.
if (Args.hasArg(OPT_safeseh)) if (Args.hasArg(OPT_safeseh)) {
for (ObjectFile *File : Symtab.ObjectFiles) for (ObjectFile *File : Symtab.ObjectFiles)
if (!File->SEHCompat) 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 // Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file. // need to create a .lib file.

View File

@ -77,7 +77,7 @@ void error(const Twine &Msg) {
} else if (ErrorCount == Config->ErrorLimit) { } else if (ErrorCount == Config->ErrorLimit) {
print("error: ", raw_ostream::RED); print("error: ", raw_ostream::RED);
*ErrorOS << "too many errors emitted, stopping now" *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); 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 defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def delayload : P<"delayload", "Delay loaded DLL name">; def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">; 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">; def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user. // No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">; def failifmismatch : P<"failifmismatch", "">;

View File

@ -190,7 +190,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
} }
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { 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 " + toString(Existing->body()->getFile()) + " and in " +
(NewFile ? toString(NewFile) : "(internal)")); (NewFile ? toString(NewFile) : "(internal)"));
} }

View File

@ -0,0 +1,29 @@
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