Index: lld/COFF/Driver.h =================================================================== --- lld/COFF/Driver.h +++ lld/COFF/Driver.h @@ -79,6 +79,7 @@ private: std::unique_ptr Tar; // for /linkrepro + std::function TakeBufferHook; // Opens a file. Path has to be resolved already. MemoryBufferRef openFile(StringRef Path); Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -24,6 +24,7 @@ #include "lld/Common/Timer.h" #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" @@ -40,6 +41,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" #include +#include #include #include @@ -48,6 +50,36 @@ using namespace llvm::COFF; using llvm::sys::Process; +namespace { +class ReproWriter { +public: + ReproWriter(StringRef OutputFileName, StringRef Prefix) + : OutputFileName(OutputFileName), Prefix(Prefix), NeedsWrite(true) {} + ~ReproWriter() { + if (!std::atomic_exchange(&NeedsWrite, false) || lld::errorCount() == 0) + return; + Expected> Tar = + TarWriter::create(OutputFileName, Prefix); + if (!Tar) { + lld::warn("Could not write reproducer to " + OutputFileName + ": " + + toString(Tar.takeError())); + return; + } + for (const auto &I : Files) + (*Tar)->append(I.getKey(), I.getValue()); + } + void addBuffer(StringRef Path, StringRef Data) { + Files.try_emplace(Path, Data); + } + +private: + std::string OutputFileName; + StringRef Prefix; + StringMap Files; + std::atomic_bool NeedsWrite; +}; +} // anonymous namespace + namespace lld { namespace coff { @@ -119,9 +151,10 @@ MemoryBufferRef MBRef = *MB; make>(std::move(MB)); // take ownership - if (Driver->Tar) - Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()), - MBRef.getBuffer()); + if (Driver->TakeBufferHook) + Driver->TakeBufferHook(relativeToRoot(MBRef.getBufferIdentifier()), + MBRef.getBuffer()); + return MBRef; } @@ -441,6 +474,7 @@ for (auto *Arg : Args) { switch (Arg->getOption().getID()) { + case OPT_errorrepro: case OPT_linkrepro: case OPT_INPUT: case OPT_defaultlib: @@ -740,6 +774,7 @@ for (auto *Arg : Args) { switch (Arg->getOption().getID()) { + case OPT_errorrepro: case OPT_linkrepro: case OPT_lldmap: case OPT_lldmap_file: @@ -898,6 +933,23 @@ error("/linkrepro: failed to open " + Path + ": " + toString(ErrOrWriter.takeError())); } + + TakeBufferHook = [this](StringRef Path, StringRef Data) { + Tar->append(Path, Data); + }; + } + + std::unique_ptr ReproWriter; + addLldExitHandler([&ReproWriter]() { ReproWriter.reset(); }); + if (auto *Arg = Args.getLastArg(OPT_errorrepro)) { + if (Args.getLastArg(OPT_linkrepro)) + fatal("/errorrepro must not be specified with /linkrepro"); + SmallString<64> Path = StringRef(Arg->getValue()); + sys::path::append(Path, "repro.tar"); + ReproWriter = make_unique(Path, "repro"); + TakeBufferHook = [&ReproWriter](StringRef Path, StringRef Data) { + ReproWriter->addBuffer(Path, Data); + }; } if (!Args.hasArg(OPT_INPUT)) { @@ -1272,10 +1324,11 @@ if (!Resources.empty()) Symtab->addFile(make(convertResToCOFF(Resources))); - if (Tar) - Tar->append("response.txt", - createResponseFile(Args, FilePaths, - ArrayRef(SearchPaths).slice(1))); + if (TakeBufferHook) + TakeBufferHook( + "response.txt", + createResponseFile(Args, FilePaths, + ArrayRef(SearchPaths).slice(1))); // Handle /largeaddressaware Config->LargeAddressAware = Args.hasFlag( Index: lld/COFF/Options.td =================================================================== --- lld/COFF/Options.td +++ lld/COFF/Options.td @@ -139,6 +139,7 @@ // LLD extensions def debug_ghash : F<"debug:ghash">; def debug_dwarf : F<"debug:dwarf">; +def errorrepro : P<"errorrepro", "If linking fails, write repro.tar here">; def export_all_symbols : F<"export-all-symbols">; def kill_at : F<"kill-at">; def lldmingw : F<"lldmingw">; Index: lld/test/COFF/errorrepro.test =================================================================== --- /dev/null +++ lld/test/COFF/errorrepro.test @@ -0,0 +1,40 @@ +# REQUIRES: x86, shell + +# RUN: rm -rf %t.dir +# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj + +# RUN: mkdir -p %t.dir/noerrorrepro +# RUN: cd %t.dir/noerrorrepro +# RUN: not lld-link %t.obj -entry:main -out:%t.exe +# RUN: not test -f repro.tar || (echo 'repro.tar should not exist' >&2; false) + +# RUN: mkdir -p %t.dir/noerror +# RUN: cd %t.dir/noerror +# RUN: lld-link %t.obj %p/Inputs/std32.lib -errorrepro:. -entry:main -out:%t.exe +# RUN: not test -f repro.tar || (echo 'repro.tar should not exist' >&2; false) + +# RUN: mkdir -p %t.dir/errorrepro +# RUN: cd %t.dir/errorrepro +# RUN: not lld-link %t.obj -errorrepro:. -entry:main -out:%t.exe +# RUN: tar xf repro.tar +# RUN: diff %t.obj repro/%:t.obj +# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt + +# RUN: mkdir -p %t.dir/fatal +# RUN: cd %t.dir/fatal +# RUN: not env LLD_IN_TEST=0 \ +# RUN: lld-link %t.obj -errorrepro:. -errorlimit:1 -entry:main -out:%t.exe +# RUN: tar xf repro.tar +# RUN: diff %t.obj repro/%:t.obj +# RUN: FileCheck %s --check-prefix=FATALRSP < repro/response.txt + +# RSP-NOT: -errorrepro +# RSP: -entry:main +# RSP: -out: +# RSP: errorrepro.test.tmp.obj + +# FATALRSP-NOT: -errorrepro +# FATALRSP: -errorlimit:1 +# FATALRSP: -entry:main +# FATALRSP: -out: +# FATALRSP: errorrepro.test.tmp.obj