Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -207,6 +207,9 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { using namespace sys::fs; + if (!tar) + reproducerObjectFiles.push_back(std::string(path)); + Optional buffer = readFile(path); if (!buffer.hasValue()) return; @@ -472,6 +475,50 @@ error("unknown -z value: " + StringRef(arg->getValue())); } +static void addFileToTar(StringRef path) { + auto mbOrErr = MemoryBuffer::getFile(path, false, false); + if (auto ec = mbOrErr.getError()) { + error("cannot open " + path + ": " + ec.message()); + return; + } + std::unique_ptr &mb = *mbOrErr; + if (tar) + tar->append(relativeToRoot(path), mb->getBuffer()); +} + +static void addObjectFileToTar(StringRef path) { + Optional buffer = readFile(path); + if (!buffer.hasValue()) + return; + MemoryBufferRef mbref = *buffer; + if (identify_magic(mbref.getBuffer()) == file_magic::archive) + getArchiveMembers(mbref); +} + +void elf::createCrashReproduceTar(const char *path, + ArrayRef argsArr) { + ELFOptTable parser; + opt::InputArgList args = parser.parse(argsArr.slice(1)); + + if (!path) + return; + Expected> errOrWriter = + TarWriter::create(path, path::stem(path)); + if (errOrWriter) { + tar = std::move(*errOrWriter); + tar->append("response.txt", createResponseFile(args)); + tar->append("version.txt", getLLDVersion() + "\n"); + StringRef ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); + if (!ltoSampleProfile.empty()) + addFileToTar(ltoSampleProfile); + } + for (const std::string &file : reproducerFiles) + addFileToTar(file); + + for (const std::string &file : reproducerObjectFiles) + addObjectFileToTar(file); +} + void LinkerDriver::linkerMain(ArrayRef argsArr) { ELFOptTable parser; opt::InputArgList args = parser.parse(argsArr.slice(1)); @@ -582,6 +629,9 @@ timeTraceProfilerCleanup(); } + + if (args.getLastArg(OPT_debug_crash)) + LLVM_BUILTIN_TRAP; } static std::string getRpath(opt::InputArgList &args) { Index: lld/ELF/InputFiles.h =================================================================== --- lld/ELF/InputFiles.h +++ lld/ELF/InputFiles.h @@ -47,6 +47,14 @@ // to this tar archive. extern std::unique_ptr tar; +// Store file names to the files required to construct a reproducer, added by +// readFile(). +extern std::vector reproducerFiles; + +// Store file names to the files required to construct a reproducer, added by +// addFile(). +extern std::vector reproducerObjectFiles; + // Opens a given file. llvm::Optional readFile(StringRef path); Index: lld/ELF/InputFiles.cpp =================================================================== --- lld/ELF/InputFiles.cpp +++ lld/ELF/InputFiles.cpp @@ -51,6 +51,8 @@ std::vector elf::sharedFiles; std::unique_ptr elf::tar; +std::vector elf::reproducerFiles; +std::vector elf::reproducerObjectFiles; // Returns "", "foo.a(bar.o)" or "baz.o". std::string lld::toString(const InputFile *f) { @@ -128,6 +130,8 @@ if (tar) tar->append(relativeToRoot(path), mbref.getBuffer()); + else + reproducerFiles.push_back(std::string(path)); return mbref; } Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -351,6 +351,12 @@ Eq<"reproduce", "Write tar file containing inputs and command to reproduce link">; +def reproduce_on_crash: F<"reproduce-on-crash">, + HelpText<"Generate tar file containing inputs and command when program crashes">; + +def debug_crash: F<"debug-crash">, + HelpText<"Debug flag to crash lld on purpose">; + defm rosegment: BB<"rosegment", "Put read-only non-executable sections in their own segment (default)", "Do not put read-only non-executable sections in their own segment">; Index: lld/include/lld/Common/Driver.h =================================================================== --- lld/include/lld/Common/Driver.h +++ lld/include/lld/Common/Driver.h @@ -40,6 +40,9 @@ namespace elf { bool link(llvm::ArrayRef args, bool canExitEarly, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); + +void createCrashReproduceTar(const char *path, + llvm::ArrayRef argsArr); } namespace mach_o { Index: lld/include/lld/Common/Reproduce.h =================================================================== --- lld/include/lld/Common/Reproduce.h +++ lld/include/lld/Common/Reproduce.h @@ -9,6 +9,7 @@ #ifndef LLD_COMMON_REPRODUCE_H #define LLD_COMMON_REPRODUCE_H +#include "lld/Common/Args.h" #include "lld/Common/LLVM.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" Index: lld/tools/lld/lld.cpp =================================================================== --- lld/tools/lld/lld.cpp +++ lld/tools/lld/lld.cpp @@ -35,6 +35,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" @@ -173,8 +174,36 @@ llvm::CrashRecoveryContext crc; if (!crc.RunSafely([&]() { r = lldMain(argc, argv, stdoutOS, stderrOS, /*exitEarly=*/false); - })) - return {crc.RetCode, /*canRunAgain=*/false}; + })) { + // lld crashed, save return code. + SafeReturn ret{crc.RetCode, /*canRunAgain=*/false}; + // Generate reproducer if related flag is specified. + std::vector args(argv, argv + argc); + bool hasReproducer = false; + bool hasReproduceOnCrash = false; + for (const char *arg : args) { + std::string arg_str(arg); + if (arg_str == "--reproduce-on-crash") + hasReproduceOnCrash = true; + if (StringRef(arg_str).startswith("--reproduce=")) + hasReproducer = true; + } + // For now only ELF is supported. + if (parseFlavor(args) == Gnu && !isPETarget(args) && + hasReproduceOnCrash && !hasReproducer) { + // Creat temp file for reproducer. + SmallString<128> reproducerPath; + std::error_code EC = + llvm::sys::fs::createTemporaryFile("lld", "tar", reproducerPath); + if (EC) { + stderrOS << "failed to create temporary file for reproducer\n"; + return ret; + } + stdoutOS << "reproducer writes to \"" << reproducerPath << "\"\n"; + elf::createCrashReproduceTar(std::string(reproducerPath).data(), args); + } + return ret; + } } // Cleanup memory and reset everything back in pristine condition. This path