Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -27,6 +27,7 @@ using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; +using namespace llvm::sys; using namespace lld; using namespace lld::elf; @@ -98,18 +99,31 @@ return V; } -static void dumpFile(StringRef SrcPath) { - SmallString<128> DirName; - sys::path::append(DirName, Config->Reproduce, sys::path::parent_path(SrcPath)); - if (std::error_code EC = sys::fs::create_directories(DirName)) { - error(EC, "--reproduce: can't create directory"); +// Concatenates S and T so that the resulting path becomes S/T. +// There are a few exceptions: +// +// 1. The result will never escape from S. Therefore, all ".." +// are removed from T before concatenati them. +// 2. Windows drive letters are removed from T before concatenation. +static std::string concat(StringRef S, StringRef T) { + // Remove leading '/' or a drive letter, and then remove "..". + SmallString<128> T2(path::relative_path(T)); + path::remove_dots(T2, /*remove_dot_dot=*/true); + + SmallString<128> Res; + path::append(Res, S, T2); + return Res.str(); +} + +static void copyFile(StringRef Src, StringRef Dest) { + SmallString<128> Dir(Dest); + path::remove_filename(Dir); + if (std::error_code EC = sys::fs::create_directories(Dir)) { + error(EC, Dir + ": can't create directory"); return; } - - SmallString<128> DestPathName; - sys::path::append(DestPathName, Config->Reproduce, SrcPath); - if (std::error_code EC = sys::fs::copy_file(SrcPath, DestPathName)) - error(EC, "--reproduce: can't copy file"); + if (std::error_code EC = sys::fs::copy_file(Src, Dest)) + error(EC, "failed to copy file: " + Dest); } // Opens and parses a file. Path has to be resolved already. @@ -119,7 +133,7 @@ if (Config->Verbose) llvm::outs() << Path << "\n"; if (!Config->Reproduce.empty()) - dumpFile(Path); + copyFile(Path, concat(Config->Reproduce, Path)); Optional Buffer = readFile(Path); if (!Buffer.hasValue()) @@ -241,21 +255,21 @@ return false; } -static void dumpLinkerInvocation(ArrayRef Args) { - std::error_code EC = sys::fs::create_directories(Config->Reproduce, - false /* IgnoreExisting */); - if (EC) { - error(EC, "--reproduce: can't create directory"); +static void logCommandline(ArrayRef Args) { + if (std::error_code EC = sys::fs::create_directories( + Config->Reproduce, /*IgnoreExisting=*/false)) { + error(EC, Config->Reproduce + ": can't create directory"); return; } - SmallString<128> InvocationPath; - sys::path::append(InvocationPath, Config->Reproduce, "invocation.txt"); - raw_fd_ostream OS(InvocationPath, EC, sys::fs::OpenFlags::F_None); + SmallString<128> Path; + path::append(Path, Config->Reproduce, "invocation.txt"); + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); check(EC); OS << Args[0]; - for (unsigned I = 1, E = Args.size(); I < E; ++I) + for (size_t I = 1, E = Args.size(); I < E; ++I) OS << " " << Args[I]; OS << "\n"; } @@ -276,7 +290,7 @@ readConfigs(Args); if (!Config->Reproduce.empty()) - dumpLinkerInvocation(ArgsArr); + logCommandline(ArgsArr); createFiles(Args); checkOptions(Args); Index: test/ELF/reproduce.s =================================================================== --- test/ELF/reproduce.s +++ test/ELF/reproduce.s @@ -1,21 +1,27 @@ # REQUIRES: x86 -# RUN: rm -rf %t.dir1 -# RUN: mkdir -p %t.dir1/build -# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir1/build/foo.o -# RUN: cd %t.dir1 -# RUN: ld.lld build/foo.o -o bar -shared --as-needed --reproduce repro -# RUN: diff build/foo.o repro/build/foo.o +# 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 build1/foo.o -o bar -shared --as-needed --reproduce repro +# RUN: diff build1/foo.o repro/build1/foo.o + +# RUN: FileCheck %s --check-prefix=INVOCATION < repro/invocation.txt +# INVOCATION: lld{{[^\s]*}} build1/foo.o -o bar -shared --as-needed --reproduce repro + +# RUN: mkdir -p %t.dir/build2/a/b/c +# 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: diff %t.dir/build2/foo.o repro/foo.o + +# 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 mov $42, %rdi syscall - -# RUN: FileCheck %s --check-prefix=INVOCATION < repro/invocation.txt -# INVOCATION: lld{{[^\s]*}} build/foo.o -o bar -shared --as-needed --reproduce repro - -# RUN: not ld.lld build/foo.o -o bar -shared --as-needed --reproduce repro 2>&1 \ -# RUN: | FileCheck --check-prefix=ERROR %s -# ERROR: --reproduce: can't create directory