[llvm-objcopy] Add support for static libraries

This diff adds support for handling static libraries 
to llvm-objcopy and llvm-strip.

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D48413

llvm-svn: 336455
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index bf58f34..c28862f 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -13,6 +13,8 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Object/ELFTypes.h"
@@ -191,23 +193,23 @@
 }
 
 std::unique_ptr<Writer> CreateWriter(const CopyConfig &Config, Object &Obj,
-                                     StringRef File, ElfType OutputElfType) {
+                                     Buffer &Buf, ElfType OutputElfType) {
   if (Config.OutputFormat == "binary") {
-    return llvm::make_unique<BinaryWriter>(File, Obj);
+    return llvm::make_unique<BinaryWriter>(Obj, Buf);
   }
   // Depending on the initial ELFT and OutputFormat we need a different Writer.
   switch (OutputElfType) {
   case ELFT_ELF32LE:
-    return llvm::make_unique<ELFWriter<ELF32LE>>(File, Obj,
+    return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
                                                  !Config.StripSections);
   case ELFT_ELF64LE:
-    return llvm::make_unique<ELFWriter<ELF64LE>>(File, Obj,
+    return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
                                                  !Config.StripSections);
   case ELFT_ELF32BE:
-    return llvm::make_unique<ELFWriter<ELF32BE>>(File, Obj,
+    return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
                                                  !Config.StripSections);
   case ELFT_ELF64BE:
-    return llvm::make_unique<ELFWriter<ELF64BE>>(File, Obj,
+    return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
                                                  !Config.StripSections);
   }
   llvm_unreachable("Invalid output format");
@@ -218,7 +220,8 @@
   auto DWOFile = Reader.create();
   DWOFile->removeSections(
       [&](const SectionBase &Sec) { return OnlyKeepDWOPred(*DWOFile, Sec); });
-  auto Writer = CreateWriter(Config, *DWOFile, File, OutputElfType);
+  FileBuffer FB(File);
+  auto Writer = CreateWriter(Config, *DWOFile, FB, OutputElfType);
   Writer->finalize();
   Writer->write();
 }
@@ -441,24 +444,89 @@
     Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
 }
 
-std::unique_ptr<Reader> CreateReader(StringRef InputFilename,
-                                     ElfType &OutputElfType) {
-  // Right now we can only read ELF files so there's only one reader;
-  auto Out = llvm::make_unique<ELFReader>(InputFilename);
-  // We need to set the default ElfType for output.
-  OutputElfType = Out->getElfType();
-  return std::move(Out);
+void ExecuteElfObjcopyOnBinary(const CopyConfig &Config, Binary &Binary,
+                               Buffer &Out) {
+  ELFReader Reader(&Binary);
+  std::unique_ptr<Object> Obj = Reader.create();
+
+  HandleArgs(Config, *Obj, Reader, Reader.getElfType());
+
+  std::unique_ptr<Writer> Writer =
+      CreateWriter(Config, *Obj, Out, Reader.getElfType());
+  Writer->finalize();
+  Writer->write();
+}
+
+// For regular archives this function simply calls llvm::writeArchive,
+// For thin archives it writes the archive file itself as well as its members.
+Error deepWriteArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
+                       bool WriteSymtab, object::Archive::Kind Kind,
+                       bool Deterministic, bool Thin) {
+  Error E =
+      writeArchive(ArcName, NewMembers, WriteSymtab, Kind, Deterministic, Thin);
+  if (!Thin || E)
+    return E;
+  for (const NewArchiveMember &Member : NewMembers) {
+    // Internally, FileBuffer will use the buffer created by
+    // FileOutputBuffer::create, for regular files (that is the case for
+    // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer.
+    // OnDiskBuffer uses a temporary file and then renames it. So in reality
+    // there is no inefficiency / duplicated in-memory buffers in this case. For
+    // now in-memory buffers can not be completely avoided since
+    // NewArchiveMember still requires them even though writeArchive does not
+    // write them on disk.
+    FileBuffer FB(Member.MemberName);
+    FB.allocate(Member.Buf->getBufferSize());
+    std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
+              FB.getBufferStart());
+    if (auto E = FB.commit())
+      return E;
+  }
+  return Error::success();
+}
+
+void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) {
+  std::vector<NewArchiveMember> NewArchiveMembers;
+  Error Err = Error::success();
+  for (const Archive::Child &Child : Ar.children(Err)) {
+    Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+    if (!ChildOrErr)
+      reportError(Ar.getFileName(), ChildOrErr.takeError());
+    Expected<StringRef> ChildNameOrErr = Child.getName();
+    if (!ChildNameOrErr)
+      reportError(Ar.getFileName(), ChildNameOrErr.takeError());
+
+    MemBuffer MB(ChildNameOrErr.get());
+    ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB);
+
+    Expected<NewArchiveMember> Member =
+        NewArchiveMember::getOldMember(Child, true);
+    if (!Member)
+      reportError(Ar.getFileName(), Member.takeError());
+    Member->Buf = MB.releaseMemoryBuffer();
+    Member->MemberName = Member->Buf->getBufferIdentifier();
+    NewArchiveMembers.push_back(std::move(*Member));
+  }
+
+  if (Err)
+    reportError(Config.InputFilename, std::move(Err));
+  if (Error E =
+          deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
+                           Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin()))
+    reportError(Config.OutputFilename, std::move(E));
 }
 
 void ExecuteElfObjcopy(const CopyConfig &Config) {
-  ElfType OutputElfType;
-  auto Reader = CreateReader(Config.InputFilename, OutputElfType);
-  auto Obj = Reader->create();
-  auto Writer =
-      CreateWriter(Config, *Obj, Config.OutputFilename, OutputElfType);
-  HandleArgs(Config, *Obj, *Reader, OutputElfType);
-  Writer->finalize();
-  Writer->write();
+  Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
+      createBinary(Config.InputFilename);
+  if (!BinaryOrErr)
+    reportError(Config.InputFilename, BinaryOrErr.takeError());
+
+  if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary()))
+    return ExecuteElfObjcopyOnArchive(Config, *Ar);
+
+  FileBuffer FB(Config.OutputFilename);
+  ExecuteElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
 }
 
 // ParseObjcopyOptions returns the config and sets the input arguments. If a
@@ -584,7 +652,7 @@
       InputArgs.getLastArgValue(STRIP_output, Positional[0]);
 
   Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
-  
+
   Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all);
   Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);