diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 51f0b72339ad..b445282c9e4c 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/LibDriver/LibDriver.h" +#include "llvm/Object/ArchiveWriter.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -417,43 +418,97 @@ static std::string getMapFile(const opt::InputArgList &Args) { return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); } -// Returns true if a given file is a LLVM bitcode file. If it is a -// static library, this function returns true if all files in the -// archive are bitcode files. -static bool isBitcodeFile(StringRef Path) { - using namespace sys::fs; +// Returns slices of MB by parsing MB as an archive file. +// Each slice consists of a member file in the archive. +std::vector getArchiveMembers(MemoryBufferRef MB) { + std::unique_ptr File = + check(Archive::create(MB), + MB.getBufferIdentifier() + ": failed to parse archive"); + std::vector V; + Error Err = Error::success(); + for (const ErrorOr &COrErr : File->children(Err)) { + Archive::Child C = + check(COrErr, MB.getBufferIdentifier() + + ": could not get the child of the archive"); + MemoryBufferRef MBRef = + check(C.getMemoryBufferRef(), + MB.getBufferIdentifier() + + ": could not get the buffer for a child of the archive"); + V.push_back(MBRef); + } + if (Err) + fatal(MB.getBufferIdentifier() + + ": Archive::children failed: " + toString(std::move(Err))); + return V; +} + +// A helper function for filterBitcodeFiles. +static bool needsRebuilding(MemoryBufferRef MB) { + // The MSVC linker doesn't support thin archives, so if it's a thin + // archive, we always need to rebuild it. + std::unique_ptr File = + check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier()); + if (File->isThin()) + return true; + + // Returns true if the archive contains at least one bitcode file. + for (MemoryBufferRef Member : getArchiveMembers(MB)) + if (identify_magic(Member.getBuffer()) == file_magic::bitcode) + return true; + return false; +} + +// Opens a given path as an archive file and removes bitcode files +// from them if exists. This function is to appease the MSVC linker as +// their linker doesn't like archive files containing non-native +// object files. +// +// If a given archive doesn't contain bitcode files, the archive path +// is returned as-is. Otherwise, a new temporary file is created and +// its path is returned. +static Optional +filterBitcodeFiles(StringRef Path, std::vector &TemporaryFiles) { std::unique_ptr MB = check( MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - file_magic Magic = identify_magic(MB->getBuffer()); + MemoryBufferRef MBRef = MB->getMemBufferRef(); + file_magic Magic = identify_magic(MBRef.getBuffer()); if (Magic == file_magic::bitcode) - return true; + return None; + if (Magic != file_magic::archive) + return Path.str(); + if (!needsRebuilding(MBRef)) + return Path.str(); - if (Magic == file_magic::archive) { - std::unique_ptr File = - check(Archive::create(MB->getMemBufferRef())); + log("Creating a temporary archive for " + Path + + " to remove bitcode files"); - Error Err = Error::success(); - for (const ErrorOr &COrErr : File->children(Err)) { - if (Err) - return false; - Archive::Child C = check(COrErr); - MemoryBufferRef MBRef = check(C.getMemoryBufferRef()); - if (identify_magic(MBRef.getBuffer()) != file_magic::bitcode) - return false; - } - if (Err) - return false; - return true; - } + std::vector New; + for (MemoryBufferRef Member : getArchiveMembers(MBRef)) + if (identify_magic(Member.getBuffer()) != file_magic::bitcode) + New.emplace_back(Member); - return false; + SmallString<128> S; + if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path), + ".lib", S)) + fatal(EC, "cannot create a temporary file"); + std::string Temp = S.str(); + TemporaryFiles.push_back(Temp); + + std::pair Ret = + llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, + /*Deterministics=*/true, + /*Thin=*/false); + if (Ret.second) + error("failed to create a new archive " + S.str() + ": " + Ret.first); + return Temp; } // Create response file contents and invoke the MSVC linker. void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { std::string Rsp = "/nologo "; + std::vector Temps; for (auto *Arg : Args) { switch (Arg->getOption().getID()) { @@ -467,14 +522,15 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { if (!StringRef(Arg->getValue()).startswith("lld")) Rsp += toString(Arg) + " "; break; - case OPT_INPUT: - // Bitcode files are stripped as they've been compiled to - // native object files. - if (Optional Path = doFindFile(Arg->getValue())) - if (isBitcodeFile(*Path)) - break; + case OPT_INPUT: { + if (Optional Path = doFindFile(Arg->getValue())) { + if (Optional S = filterBitcodeFiles(*Path, Temps)) + Rsp += quote(*S) + " "; + continue; + } Rsp += quote(Arg->getValue()) + " "; break; + } default: Rsp += toString(Arg) + " "; } @@ -482,6 +538,9 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { std::vector ObjectFiles = Symtab.compileBitcodeFiles(); runMSVCLinker(Rsp, ObjectFiles); + + for (StringRef Path : Temps) + sys::fs::remove(Path); } void LinkerDriver::enqueueTask(std::function Task) { diff --git a/lld/COFF/Error.h b/lld/COFF/Error.h index ebcd3d87d22e..a4f44fb1e36c 100644 --- a/lld/COFF/Error.h +++ b/lld/COFF/Error.h @@ -28,7 +28,7 @@ LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); -template T check(ErrorOr &&V, const Twine &Prefix) { +template T check(ErrorOr V, const Twine &Prefix) { if (auto EC = V.getError()) fatal(EC, Prefix); return std::move(*V); diff --git a/lld/test/COFF/msvclto-archive.ll b/lld/test/COFF/msvclto-archive.ll index 937ef5325bfe..2597605260df 100644 --- a/lld/test/COFF/msvclto-archive.ll +++ b/lld/test/COFF/msvclto-archive.ll @@ -1,19 +1,30 @@ -;; Make sure we do not pass archive files containing only bitcode files. +;; Make sure we re-create archive files to strip bitcode files. ; RUN: llvm-as -o %t.obj %s +; RUN: rm -f %t-main1.a ; RUN: llvm-ar cru %t-main1.a %t.obj ; RUN: mkdir -p %t.dir ; RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t.dir/bitcode.obj %p/Inputs/msvclto.s ; RUN: lld-link %t-main1.a %t.dir/bitcode.obj /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \ ; RUN: /entry:main /verbose > %t.log || true ; RUN: FileCheck -check-prefix=BC %s < %t.log -; BC-NOT: link.exe {{.*}}-main1.a +; BC: Creating a temporary archive for -; RUN: llvm-ar cru %t-main2.a %t.obj %t.dir/bitcode.obj -; RUN: lld-link %t.dir/bitcode.obj %t-main2.a /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \ +; RUN: rm -f %t-main2.a +; RUN: llvm-ar cru %t-main2.a %t.dir/bitcode.obj +; RUN: lld-link %t.obj %t-main2.a /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \ ; RUN: /entry:main /verbose > %t.log || true ; RUN: FileCheck -check-prefix=OBJ %s < %t.log -; OBJ: link.exe {{.*}}-main2.a +; OBJ-NOT: Creating a temporary archive + +;; Make sure that we always rebuild thin archives because +;; the MSVC linker doesn't support thin archives. +; RUN: rm -f %t-main3.a +; RUN: llvm-ar cruT %t-main3.a %t.dir/bitcode.obj +; RUN: lld-link %t.obj %t-main3.a /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \ +; RUN: /entry:main /verbose > %t.log || true +; RUN: FileCheck -check-prefix=THIN %s < %t.log +; THIN: Creating a temporary archive target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc"