Index: llvm/test/tools/llvm-link/Inputs/f.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/Inputs/f.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @f() { +entry: + ret void +} Index: llvm/test/tools/llvm-link/Inputs/g.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/Inputs/g.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @g() { +entry: + ret void +} Index: llvm/test/tools/llvm-link/Inputs/h.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/Inputs/h.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @h() { +entry: + ret void +} Index: llvm/test/tools/llvm-link/archive-bad.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/archive-bad.ll @@ -0,0 +1,7 @@ +# RUN: cp %S/Inputs/f.ll %t.fg.a +# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s + +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.linked.bc + +# CHECK: file too small to be an archive Index: llvm/test/tools/llvm-link/archive.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/archive.ll @@ -0,0 +1,17 @@ +# RUN: llvm-as %S/Inputs/f.ll -o %t.f.bc +# RUN: llvm-as %S/Inputs/g.ll -o %t.g.bc +# RUN: llvm-ar cr %t.fg.a %t.f.bc %t.g.bc +# RUN: llvm-ar cr %t.empty.a +# RUN: llvm-link %S/Inputs/h.ll %t.fg.a %t.empty.a -o %t.linked.bc + +# RUN: llvm-nm %t.linked.bc | FileCheck %s + +# RUN: rm -f %t.f.bc +# RUN: rm -f %t.g.bc +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.empty.a +# RUN: rm -f %t.linked.bc + +# CHECK: -------- T f +# CHECK: -------- T g +# CHECK: -------- T h Index: llvm/test/tools/llvm-link/archivell.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-link/archivell.ll @@ -0,0 +1,7 @@ +# RUN: llvm-ar cr %t.fg.a %S/Inputs/f.ll llvm-as %S/Inputs/g.ll +# RUN: not llvm-link %S/Inputs/h.ll %t.fg.a -o %t.linked.bc 2>&1 | FileCheck %s + +# RUN: rm -f %t.fg.a +# RUN: rm -f %t.linked.bc + +# CHECK: error: member of archive is not a bitcode file Index: llvm/tools/llvm-link/llvm-link.cpp =================================================================== --- llvm/tools/llvm-link/llvm-link.cpp +++ llvm/tools/llvm-link/llvm-link.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm//Object/Archive.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" @@ -139,6 +140,74 @@ return Result; } +static std::unique_ptr loadArFile(const char *argv0, + const std::string ArchiveName, + LLVMContext &Context, Linker &L, + unsigned OrigFlags, + unsigned ApplicableFlags) { + std::unique_ptr Result(new Module("ArchiveModule", Context)); + if (Verbose) + errs() << "Reading library archive file '" << ArchiveName + << "' to memory\n"; + ErrorOr> Buf = + MemoryBuffer::getFile(ArchiveName, -1, false); + ExitOnErr(errorCodeToError(Buf.getError())); + Error Err = Error::success(); + object::Archive Archive(Buf.get()->getMemBufferRef(), Err); + ExitOnErr(std::move(Err)); + for (auto &C : Archive.children(Err)) { + Expected Ename = C.getName(); + if (Error E = Ename.takeError()) { + errs() << argv0 << ": "; + WithColor::error() + << " could not get member name of archive library failed'" + << ArchiveName << "'\n"; + return nullptr; + }; + std::string ChildName = Ename.get().str(); + if (Verbose) + errs() << "Parsing member '" << ChildName + << "' of archive library to module.\n"; + SMDiagnostic ParseErr; + Expected MemBuf = C.getMemoryBufferRef(); + if (Error E = MemBuf.takeError()) { + errs() << argv0 << ": "; + WithColor::error() << " loading memory for member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + }; + + if (!isBitcode(reinterpret_cast + (MemBuf.get().getBufferStart()), + reinterpret_cast + (MemBuf.get().getBufferEnd()))) { + errs() << argv0 << ": "; + WithColor::error() << " member of archive is not a bitcode file: '" + << ChildName << "'\n"; + return nullptr; + } + + std::unique_ptr M = parseIR(MemBuf.get(), ParseErr, Context); + + if (!M.get()) { + errs() << argv0 << ": "; + WithColor::error() << " parsing member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + } + if (Verbose) + errs() << "Linking member '" << ChildName << "' of archive library.\n"; + bool Err = L.linkModules(*Result, std::move(M), ApplicableFlags); + if (Err) + return nullptr; + ApplicableFlags = OrigFlags; + } // end for each child + ExitOnErr(std::move(Err)); + return Result; +} + namespace { /// Helper to load on demand a Module from file and cache it for subsequent @@ -281,7 +350,10 @@ // Similar to some flags, internalization doesn't apply to the first file. bool InternalizeLinkedSymbols = false; for (const auto &File : Files) { - std::unique_ptr M = loadFile(argv0, File, Context); + std::unique_ptr M = + (llvm::sys::path::extension(File) == ".a") + ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags) + : loadFile(argv0, File, Context); if (!M.get()) { errs() << argv0 << ": "; WithColor::error() << " loading file '" << File << "'\n";