[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:
parent
7ec2c72095
commit
b96b10102a
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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", "">;
|
||||
|
|
|
@ -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)"));
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue