Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -14,6 +14,7 @@ #include "lld/Core/LLVM.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/raw_ostream.h" @@ -29,6 +30,10 @@ void addLibrary(StringRef Name); llvm::LLVMContext Context; + // for --reproduce + std::unique_ptr ReproduceArchive; + llvm::StringSet<> IncludedFiles; + private: std::vector getArchiveMembers(MemoryBufferRef MB); llvm::Optional readFile(StringRef Path); @@ -69,7 +74,7 @@ void printVersion(); void createResponseFile(const llvm::opt::InputArgList &Args); -void copyInputFile(StringRef Path); +void maybeCopyInputFile(StringRef Path, StringRef Buffer); std::string findFromSearchPaths(StringRef Path); std::string searchLibrary(StringRef Path); Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -108,14 +108,14 @@ using namespace sys::fs; if (Config->Verbose) outs() << Path << "\n"; - if (!Config->Reproduce.empty()) - copyInputFile(Path); Optional Buffer = readFile(Path); if (!Buffer.hasValue()) return; MemoryBufferRef MBRef = *Buffer; + maybeCopyInputFile(Path, MBRef.getBuffer()); + switch (identify_magic(MBRef.getBuffer())) { case file_magic::unknown: readLinkerScript(MBRef); @@ -252,8 +252,13 @@ readConfigs(Args); initLLVM(Args); - if (!Config->Reproduce.empty()) + if (!Config->Reproduce.empty()) { + std::error_code EC; + ReproduceArchive = make_unique(Config->Reproduce + ".cpio", + EC, fs::F_None); + check(EC); createResponseFile(Args); + } createFiles(Args); checkOptions(Args); @@ -486,6 +491,8 @@ for (auto *Arg : Args.filtered(OPT_wrap)) Symtab.wrap(Arg->getValue()); + maybeCloseReproArchive(); + // Write the result to the file. if (Config->GcSections) markLive(); Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -117,16 +117,40 @@ return Dest.str(); } -// Copies file Src to {Config->Reproduce}/Src. -void elf::copyInputFile(StringRef Src) { +static void maybePrintCpioMember(StringRef Path, StringRef Data) { + if (Config->Reproduce.empty()) + return; + + if (!Driver->IncludedFiles.insert(Path).second) + return; + + raw_fd_ostream &OS = *Driver->ReproduceArchive; + OS << "070707"; // c_magic + OS << "000000"; // c_dev + OS << "000000"; // c_ino // FIXME: should be unique + OS << "100664"; // c_mode: C_ISREG | rw-rw-r-- + OS << "000000"; // c_uid + OS << "000000"; // c_gid + OS << "000001"; // c_nlink + OS << "000000"; // c_rdev + OS << "00000000000"; // c_mtime + OS << format("%06o", Path.size() + 1); // c_namesize + OS << format("%011o", Data.size()); // c_filesize + OS << Path << '\0'; // c_name + OS << Data; // c_filedata +} + +// Write file Src with content Data to the archive. +void elf::maybeCopyInputFile(StringRef Src, StringRef Data) { std::string Dest = getDestPath(Src); - StringRef Dir = path::parent_path(Dest); - if (std::error_code EC = fs::create_directories(Dir)) { - error(EC, Dir + ": can't create directory"); + maybePrintCpioMember(Dest, Data); +} + +void elf::maybeCloseReproArchive() { + if (!Driver->ReproduceArchive) return; - } - if (std::error_code EC = fs::copy_file(Src, Dest)) - error(EC, "failed to copy file: " + Dest); + maybePrintCpioMember("TRAILER!!!", ""); + Driver->ReproduceArchive.reset(); } // Quote a given string if it contains a space character. @@ -148,20 +172,8 @@ // "ld.lld @response.txt". Used by --reproduce. This feature is // supposed to be used by users to report an issue to LLD developers. void elf::createResponseFile(const opt::InputArgList &Args) { - // Create the output directory. - if (std::error_code EC = - fs::create_directories(Config->Reproduce, /*IgnoreExisting=*/false)) { - error(EC, Config->Reproduce + ": can't create directory"); - return; - } - - // Open "response.txt". - SmallString<128> Path; - path::append(Path, Config->Reproduce, "response.txt"); - std::error_code EC; - raw_fd_ostream OS(Path, EC, fs::OpenFlags::F_None); - check(EC); - + SmallString<0> Data; + raw_svector_ostream OS(Data); // Copy the command line to response.txt while rewriting paths. for (auto *Arg : Args) { switch (Arg->getOption().getID()) { @@ -185,6 +197,10 @@ OS << "\n"; } } + + SmallString<128> Dest; + path::append(Dest, Config->Reproduce, "response.txt"); + maybePrintCpioMember(Dest, Data); } std::string elf::findFromSearchPaths(StringRef Path) { Index: ELF/Error.h =================================================================== --- ELF/Error.h +++ ELF/Error.h @@ -18,6 +18,8 @@ extern bool HasError; extern llvm::raw_ostream *ErrorOS; +void maybeCloseReproArchive(); + void log(const Twine &Msg); void warning(const Twine &Msg); Index: ELF/Error.cpp =================================================================== --- ELF/Error.cpp +++ ELF/Error.cpp @@ -29,6 +29,7 @@ void error(const Twine &Msg) { *ErrorOS << Msg << "\n"; HasError = true; + maybeCloseReproArchive(); } void error(std::error_code EC, const Twine &Prefix) { Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -372,12 +372,15 @@ if (!Seen.insert(C.getChildOffset()).second) return MemoryBufferRef(); - if (!Config->Reproduce.empty() && C.getParent()->isThin()) - copyInputFile(check(C.getFullName())); + MemoryBufferRef Ret = + check(C.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + + Sym->getName()); - return check(C.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + - Sym->getName()); + if (C.getParent()->isThin()) + maybeCopyInputFile(check(C.getFullName()), Ret.getBuffer()); + + return Ret; } template Index: test/ELF/reproduce-error.s =================================================================== --- /dev/null +++ test/ELF/reproduce-error.s @@ -0,0 +1,12 @@ +# RUN: rm -rf %t.dir +# RUN: mkdir -p %t.dir +# RUN: cd %t.dir + +# RUN: not ld.lld --reproduce repro abc -o t 2>&1 | FileCheck %s +# CHECK: cannot open abc: No such file or directory + +# RUN: grep TRAILER repro.cpio +# RUN: cpio -id < repro.cpio +# RUN: FileCheck --check-prefix=RSP %s < repro/response.txt +# RSP: abc +# RSP: -o t Index: test/ELF/reproduce-linkerscript.s =================================================================== --- test/ELF/reproduce-linkerscript.s +++ test/ELF/reproduce-linkerscript.s @@ -6,6 +6,7 @@ # RUN: echo "INPUT(\"%t.dir/build/foo.o\")" > %t.dir/build/foo.script # RUN: cd %t.dir # RUN: ld.lld build/foo.script -o bar --reproduce repro +# RUN: cpio -id < repro.cpio # RUN: diff build/foo.script repro/%:t.dir/build/foo.script # RUN: diff build/foo.o repro/%:t.dir/build/foo.o Index: test/ELF/reproduce-thin-archive.s =================================================================== --- test/ELF/reproduce-thin-archive.s +++ test/ELF/reproduce-thin-archive.s @@ -6,6 +6,7 @@ # RUN: cd %t.dir # RUN: llvm-ar --format=gnu rcT foo.a foo.o # RUN: ld.lld -m elf_x86_64 foo.a -o bar --reproduce repro +# RUN: cpio -id < repro.cpio # RUN: diff foo.a repro/%:t.dir/foo.a # RUN: diff foo.o repro/%:t.dir/foo.o Index: test/ELF/reproduce.s =================================================================== --- test/ELF/reproduce.s +++ test/ELF/reproduce.s @@ -1,15 +1,11 @@ # REQUIRES: x86 -# XXX: Temporary hack to work around windows path length limitation due to -# the build dir for llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast. -# When we directly generate an archive this won't be an issue. -# REQUIRES: shell - # RUN: rm -rf %t.dir # RUN: mkdir -p %t.dir/build1 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir/build1/foo.o # RUN: cd %t.dir # RUN: ld.lld --hash-style=gnu build1/foo.o -o bar -shared --as-needed --reproduce repro +# RUN: cpio -id < repro.cpio # RUN: diff build1/foo.o repro/%:t.dir/build1/foo.o # RUN: FileCheck %s --check-prefix=RSP < repro/response.txt @@ -24,6 +20,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir/build2/foo.o # RUN: cd %t.dir/build2/a/b/c # RUN: ld.lld ./../../../foo.o -o bar -shared --as-needed --reproduce repro +# RUN: cpio -id < repro.cpio # RUN: diff %t.dir/build2/foo.o repro/%:t.dir/build2/foo.o # RUN: echo "{ local: *; };" > ver @@ -33,6 +30,7 @@ # RUN: ld.lld --reproduce repro2 'foo bar' -L"foo bar" -Lfile \ # RUN: --dynamic-list dyn -rpath file --script file --version-script ver \ # RUN: --dynamic-linker "some unusual/path" +# RUN: cpio -id < repro2.cpio # RUN: FileCheck %s --check-prefix=RSP2 < repro2/response.txt # RSP2: "{{.*}}foo bar" # RSP2-NEXT: -L "{{.*}}foo bar" @@ -43,10 +41,6 @@ # RSP2-NEXT: --version-script {{.+}}ver # RSP2-NEXT: --dynamic-linker "some unusual/path" -# RUN: not ld.lld build1/foo.o -o bar -shared --as-needed --reproduce . 2>&1 \ -# RUN: | FileCheck --check-prefix=ERROR %s -# ERROR: can't create directory - .globl _start _start: mov $60, %rax