Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -48,6 +48,7 @@ llvm::StringRef SoName; llvm::StringRef Sysroot; std::string RPath; + std::string ReproduceDir = ""; std::vector DynamicList; std::vector SearchPaths; std::vector Undefined; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -19,6 +19,7 @@ #include "Writer.h" #include "lld/Driver/Driver.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include @@ -97,12 +98,42 @@ return V; } +// Should this be moved to LLVM/Support ? +static std::string getFullPath(StringRef FileName) { + SmallString<128> CurPath; + sys::fs::current_path(CurPath); + sys::path::append(CurPath, FileName); + return CurPath.str(); +} + +static void dumpFile(StringRef SrcPath) { + if (Config->ReproduceDir.empty()) + return; + + std::error_code EC; + EC = llvm::sys::fs::create_directories( + Config->ReproduceDir + "/" + llvm::sys::path::parent_path(SrcPath)); + if (EC) { + error("--reproduce: can't create directory " + + Config->ReproduceDir + "/" + SrcPath + + ": " + EC.message()); + return; + } + + EC = llvm::sys::fs::copy_file(SrcPath, + Config->ReproduceDir + "/" + SrcPath); + if (EC) + error("--reproduce: can't copy file " + SrcPath + ": " + EC.message()); +} + // Opens and parses a file. Path has to be resolved already. // Newly created memory buffers are owned by this driver. void LinkerDriver::addFile(StringRef Path) { using namespace llvm::sys::fs; if (Config->Verbose) llvm::outs() << Path << "\n"; + dumpFile(Path); + Optional Buffer = readFile(Path); if (!Buffer.hasValue()) return; @@ -223,6 +254,23 @@ return false; } +static void dumpLinkerInvocation(ArrayRef Args) { + if (Config->ReproduceDir.empty()) + return; + + std::error_code EC = llvm::sys::fs::create_directories(Config->ReproduceDir); + if (EC) { + error("--reproduce: can't create directory: " + EC.message()); + return; + } + + raw_fd_ostream OS(Config->ReproduceDir + "/invocation.txt", EC, + sys::fs::OpenFlags::F_None); + check(EC); + for (auto Arg: Args) + OS << Arg << " "; +} + void LinkerDriver::main(ArrayRef ArgsArr) { ELFOptTable Parser; opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); @@ -237,6 +285,7 @@ initLLVM(Args); readConfigs(Args); + dumpLinkerInvocation(ArgsArr); createFiles(Args); checkOptions(Args); if (HasError) @@ -378,6 +427,15 @@ if (Optional Buffer = readFile(getString(Args, OPT_version_script))) parseVersionScript(*Buffer); + + if (auto *Arg = Args.getLastArg(OPT_reproduce)) { + if (llvm::sys::path::is_relative(Arg->getValue())) + Config->ReproduceDir = getFullPath(Arg->getValue()); + else + Config->ReproduceDir = Arg->getValue(); + if (llvm::sys::fs::exists(Config->ReproduceDir)) + error("--reproduce: directory " + Config->ReproduceDir + " already exists."); + } } void LinkerDriver::createFiles(opt::InputArgList &Args) { Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -120,6 +120,9 @@ def print_gc_sections: Flag<["--"], "print-gc-sections">, HelpText<"List removed unused sections">; +def reproduce : Separate<["--"], "reproduce">, + HelpText<"Dump linker invocation and input files for debugging">; + def rpath : Separate<["-"], "rpath">, HelpText<"Add a DT_RUNPATH to the output">; Index: test/ELF/reproduce.s =================================================================== --- /dev/null +++ test/ELF/reproduce.s @@ -0,0 +1,26 @@ +# REQUIRES: x86 + +# RUN: rm -rf %S/repro +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: ld.lld %t -o %t2 -shared --as-needed --reproduce %S/repro +# RUN: llvm-objdump -d %S/repro/%t | FileCheck %s --check-prefix=DUMP +# RUN: cat %S/repro/invocation.txt | FileCheck %s --check-prefix=INVOCATION + +.globl _start; +_start: + mov $60, %rax + mov $42, %rdi + syscall + +# DUMP: Disassembly of section .text: +# DUMP: _start: +# DUMP: 0: 48 c7 c0 3c 00 00 00 movq $60, %rax +# DUMP: 7: 48 c7 c7 2a 00 00 00 movq $42, %rdi +# DUMP: e: 0f 05 syscall + +# INVOCATION: lld {{.*}}reproduce.s{{.*}} -o {{.*}} -shared --as-needed --reproduce + +# RUN: rm -rf %S/repro2 +# RUN: mkdir %S/repro2 +# RUN: not ld.lld %t -o %t2 --reproduce %S/repro2 2>&1 | FileCheck --check-prefix=EDIR %s +# EDIR: --reproduce: directory {{.*}}repro2 already exists.