[llvm] r336455 - [llvm-objcopy] Add support for static libraries

Alexander Shaposhnikov via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 6 10:51:03 PDT 2018


Author: alexshap
Date: Fri Jul  6 10:51:03 2018
New Revision: 336455

URL: http://llvm.org/viewvc/llvm-project?rev=336455&view=rev
Log:
[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

Added:
    llvm/trunk/test/tools/llvm-objcopy/basic-archive-copy.test
Modified:
    llvm/trunk/test/tools/llvm-objcopy/fail-no-output-directory.test
    llvm/trunk/test/tools/llvm-objcopy/strip-all.test
    llvm/trunk/test/tools/llvm-objcopy/strip-debug.test
    llvm/trunk/tools/llvm-objcopy/Object.cpp
    llvm/trunk/tools/llvm-objcopy/Object.h
    llvm/trunk/tools/llvm-objcopy/llvm-objcopy.cpp

Added: llvm/trunk/test/tools/llvm-objcopy/basic-archive-copy.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/basic-archive-copy.test?rev=336455&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/basic-archive-copy.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/basic-archive-copy.test Fri Jul  6 10:51:03 2018
@@ -0,0 +1,99 @@
+# RUN: yaml2obj %s > %t
+
+# RUN: rm -f %t.a
+# RUN: llvm-ar crs %t.a %t
+# RUN: cp %t.a %t.copy.a
+# RUN: llvm-objcopy %t.a %t2.a
+# RUN: llvm-objcopy %t %t2
+# RUN: llvm-ar p %t2.a > %t3
+# RUN: cmp %t2 %t3
+
+# RUN: llvm-readobj -sections %t2 | FileCheck %s
+# RUN: llvm-nm -print-armap %t.a | FileCheck --check-prefix=INDEX-TABLE %s
+# RUN: llvm-nm -print-armap %t2.a | FileCheck --check-prefix=INDEX-TABLE %s
+# Verify that llvm-objcopy has not modifed the input.
+# RUN: cmp %t.copy.a %t.a
+
+# INDEX-TABLE: Archive map
+# INDEX-TABLE-NEXT: foo in
+
+# RUN: rm -f %t.no.index.a
+# RUN: llvm-ar crS %t.no.index.a %t
+# RUN: llvm-objcopy %t.no.index.a %t2.no.index.a
+# RUN: llvm-ar p %t2.no.index.a > %t4
+
+# RUN: llvm-nm -print-armap %t.no.index.a | FileCheck --check-prefix=NO-INDEX-TABLE %s
+# RUN: llvm-nm -print-armap %t2.no.index.a | FileCheck --check-prefix=NO-INDEX-TABLE %s
+# RUN: cmp %t2 %t4
+
+# NO-INDEX-TABLE-NOT: Archive map
+# NO-INDEX-TABLE-NOT: foo in
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .bss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_ALLOC ]
+    AddressAlign:    0x0000000000000010
+    Size:            64
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000000010
+    Content:         "00000000"
+Symbols:
+   Global:
+     - Name:     foo
+       Type:     STT_FUNC
+       Section:  .text
+       Value:    0x1004
+
+# CHECK: Type: SHT_NULL
+
+# CHECK:      Name: .bss
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT:   SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 64
+
+# CHECK:      Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT:   SHF_ALLOC
+# CHECK-NEXT:   SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 4
+
+# CHECK:      Name: .symtab
+# CHECK-NEXT: Type: SHT_SYMTAB
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 48
+
+# CHECK:      Name: .strtab
+# CHECK-NEXT: Type: SHT_STRTAB
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 6
+
+# CHECK:      Name: .shstrtab
+# CHECK-NEXT: Type: SHT_STRTAB
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 38

Modified: llvm/trunk/test/tools/llvm-objcopy/fail-no-output-directory.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/fail-no-output-directory.test?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/fail-no-output-directory.test (original)
+++ llvm/trunk/test/tools/llvm-objcopy/fail-no-output-directory.test Fri Jul  6 10:51:03 2018
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %s > %t
 # RUN: not llvm-objcopy %t no/such/dir 2>&1 | FileCheck %s
-# CHECK: failed to open no/such/dir
+# CHECK: failed to open no/such/dir:
 
 !ELF
 FileHeader:

Modified: llvm/trunk/test/tools/llvm-objcopy/strip-all.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/strip-all.test?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/strip-all.test (original)
+++ llvm/trunk/test/tools/llvm-objcopy/strip-all.test Fri Jul  6 10:51:03 2018
@@ -21,6 +21,12 @@
 # RUN: cmp %t2 %t-should-be-stripped
 # RUN: cmp %t %t-should-remain-the-same
 
+# RUN: rm -f %t.a
+# RUN: llvm-ar crs %t.a %t
+# RUN: llvm-objcopy --strip-all %t.a %t.a
+# RUN: llvm-ar p %t.a > %t6
+# RUN: cmp %t2 %t6
+
 !ELF
 FileHeader:
   Class:           ELFCLASS64

Modified: llvm/trunk/test/tools/llvm-objcopy/strip-debug.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/strip-debug.test?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/strip-debug.test (original)
+++ llvm/trunk/test/tools/llvm-objcopy/strip-debug.test Fri Jul  6 10:51:03 2018
@@ -21,6 +21,69 @@
 # RUN: llvm-strip -S %t6
 # RUN: cmp %t2 %t6
 
+# RUN: rm -f %t.a
+# RUN: llvm-ar crs %t.a %t
+# RUN: llvm-objcopy --strip-debug %t.a %t.a
+# RUN: llvm-ar p %t.a > %t7
+# RUN: cmp %t2 %t7
+
+# Verify that an archive with multiple object files is handled correctly.
+# RUN: cp %t %t.duplicate
+# RUN: cp %t2 %t.duplicate.stripped
+# RUN: rm -f %t.multiple-stripped-obj.a
+# RUN: llvm-ar crs %t.multiple-stripped-obj.a %t2 %t.duplicate.stripped
+# RUN: rm -f %t.multiple-obj.a
+# RUN: llvm-ar crs %t.multiple-obj.a %t %t.duplicate
+# RUN: llvm-objcopy --strip-debug %t.multiple-obj.a %t.multiple-obj.stripped.a
+# RUN: llvm-ar p %t.multiple-stripped-obj.a > %t.multiple-stripped-obj.a.dump
+# RUN: llvm-ar p %t.multiple-obj.stripped.a > %t.multiple-obj.stripped.a.dump
+# RUN: cmp %t.multiple-stripped-obj.a.dump %t.multiple-obj.stripped.a.dump
+
+# We can not use %t inside the patterns passed to FileCheck,
+# thus we have to use "recognizable" file names.
+# RUN: cp %t %t1.o
+# RUN: cp %s %t2.txt
+# RUN: cp %t %t3.o
+# RUN: rm -f %t.non-object.a
+# RUN: llvm-ar cr %t.non-object.a %t1.o %t2.txt %t3.o
+# RUN: llvm-ar t %t.non-object.a | FileCheck %s --check-prefix=NON-OBJECT-ARCHIVE-MEMBERS
+
+# NON-OBJECT-ARCHIVE-MEMBERS: 1.o
+# NON-OBJECT-ARCHIVE-MEMBERS-NEXT: 2.txt
+# NON-OBJECT-ARCHIVE-MEMBERS-NEXT: 3.o
+
+# RUN: cp %t.non-object.a %t.non-object.copy.a
+# RUN: not llvm-objcopy --strip-debug %t.non-object.a %t2.non-object.a 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT
+
+# BAD-FORMAT: The file was not recognized as a valid object file
+
+# Verify that %t.non-object.a has not been modified.
+# RUN: cmp %t.non-object.a %t.non-object.copy.a
+
+# RUN: rm -f %t.thin.a
+# Copy %t to %t.thin.archive.member to avoid changing %t directly.
+# RUN: cp %t %t.thin.archive.member
+# RUN: llvm-ar crsT %t.thin.a %t.thin.archive.member
+# RUN: llvm-objcopy --strip-debug %t.thin.a %t2.thin.a
+# RUN: cat %t.thin.a | FileCheck %s --check-prefix=VERIFY-THIN-ARCHIVE
+# RUN: cat %t2.thin.a | FileCheck %s --check-prefix=VERIFY-THIN-ARCHIVE
+ 
+# VERIFY-THIN-ARCHIVE: !<thin>
+
+# Verify that the member of a thin archive was properly modified.
+# RUN: cmp %t2 %t.thin.archive.member
+
+# RUN: rm -f %t.non-object.thin.a
+# RUN: llvm-ar crsT %t.non-object.thin.a %t1.o %t2.txt %t3.o
+# RUN: cp %t.non-object.thin.a %t.non-object.thin.copy.a
+# RUN: not llvm-objcopy --strip-debug %t.non-object.thin.a %t.non-object.thin.a 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT
+
+# Verify that in the case of error thin archive and its memebers are not getting modified.
+# RUN: cmp %t.non-object.thin.a %t.non-object.thin.copy.a
+# RUN: cmp %t %t1.o
+# RUN: cmp %s %t2.txt
+# RUN: cmp %t %t3.o
+
 !ELF
 FileHeader:
   Class:           ELFCLASS64

Modified: llvm/trunk/tools/llvm-objcopy/Object.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/Object.cpp?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/Object.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/Object.cpp Fri Jul  6 10:51:03 2018
@@ -30,12 +30,43 @@ using namespace llvm;
 using namespace object;
 using namespace ELF;
 
+Buffer::~Buffer() {}
+
+void FileBuffer::allocate(size_t Size) {
+  Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+      FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
+  handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) {
+    error("failed to open " + getName() + ": " + E.message());
+  });
+  Buf = std::move(*BufferOrErr);
+}
+
+Error FileBuffer::commit() { return Buf->commit(); }
+
+uint8_t *FileBuffer::getBufferStart() {
+  return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+}
+
+void MemBuffer::allocate(size_t Size) {
+  Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
+}
+
+Error MemBuffer::commit() { return Error::success(); }
+
+uint8_t *MemBuffer::getBufferStart() {
+  return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+}
+
+std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() {
+  return std::move(Buf);
+}
+
 template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
   using Elf_Phdr = typename ELFT::Phdr;
 
-  uint8_t *Buf = BufPtr->getBufferStart();
-  Buf += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
-  Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(Buf);
+  uint8_t *B = Buf.getBufferStart();
+  B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
+  Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
   Phdr.p_type = Seg.Type;
   Phdr.p_flags = Seg.Flags;
   Phdr.p_offset = Seg.Offset;
@@ -53,9 +84,9 @@ void SectionBase::finalize() {}
 void SectionBase::markSymbols() {}
 
 template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
-  uint8_t *Buf = BufPtr->getBufferStart();
-  Buf += Sec.HeaderOffset;
-  typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(Buf);
+  uint8_t *B = Buf.getBufferStart();
+  B += Sec.HeaderOffset;
+  typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(B);
   Shdr.sh_name = Sec.NameIndex;
   Shdr.sh_type = Sec.Type;
   Shdr.sh_flags = Sec.Flags;
@@ -410,9 +441,7 @@ void Section::initialize(SectionTableRef
   }
 }
 
-void Section::finalize() {
-  this->Link = LinkSection ? LinkSection->Index : 0;
-}
+void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; }
 
 void GnuDebugLinkSection::init(StringRef File, StringRef Data) {
   FileName = sys::path::filename(File);
@@ -814,41 +843,33 @@ Writer::~Writer() {}
 
 Reader::~Reader() {}
 
-ELFReader::ELFReader(StringRef File) {
-  auto BinaryOrErr = createBinary(File);
-  if (!BinaryOrErr)
-    reportError(File, BinaryOrErr.takeError());
-  auto OwnedBin = std::move(BinaryOrErr.get());
-  std::tie(Bin, Data) = OwnedBin.takeBinary();
-}
-
 ElfType ELFReader::getElfType() const {
-  if (isa<ELFObjectFile<ELF32LE>>(Bin.get()))
+  if (isa<ELFObjectFile<ELF32LE>>(Bin))
     return ELFT_ELF32LE;
-  if (isa<ELFObjectFile<ELF64LE>>(Bin.get()))
+  if (isa<ELFObjectFile<ELF64LE>>(Bin))
     return ELFT_ELF64LE;
-  if (isa<ELFObjectFile<ELF32BE>>(Bin.get()))
+  if (isa<ELFObjectFile<ELF32BE>>(Bin))
     return ELFT_ELF32BE;
-  if (isa<ELFObjectFile<ELF64BE>>(Bin.get()))
+  if (isa<ELFObjectFile<ELF64BE>>(Bin))
     return ELFT_ELF64BE;
   llvm_unreachable("Invalid ELFType");
 }
 
 std::unique_ptr<Object> ELFReader::create() const {
   auto Obj = llvm::make_unique<Object>();
-  if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(Bin.get())) {
+  if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
     ELFBuilder<ELF32LE> Builder(*o, *Obj);
     Builder.build();
     return Obj;
-  } else if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(Bin.get())) {
+  } else if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
     ELFBuilder<ELF64LE> Builder(*o, *Obj);
     Builder.build();
     return Obj;
-  } else if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(Bin.get())) {
+  } else if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
     ELFBuilder<ELF32BE> Builder(*o, *Obj);
     Builder.build();
     return Obj;
-  } else if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(Bin.get())) {
+  } else if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
     ELFBuilder<ELF64BE> Builder(*o, *Obj);
     Builder.build();
     return Obj;
@@ -857,8 +878,8 @@ std::unique_ptr<Object> ELFReader::creat
 }
 
 template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
-  uint8_t *Buf = BufPtr->getBufferStart();
-  Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf);
+  uint8_t *B = Buf.getBufferStart();
+  Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(B);
   std::copy(Obj.Ident, Obj.Ident + 16, Ehdr.e_ident);
   Ehdr.e_type = Obj.Type;
   Ehdr.e_machine = Obj.Machine;
@@ -887,10 +908,10 @@ template <class ELFT> void ELFWriter<ELF
 }
 
 template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
-  uint8_t *Buf = BufPtr->getBufferStart() + Obj.SHOffset;
+  uint8_t *B = Buf.getBufferStart() + Obj.SHOffset;
   // This reference serves to write the dummy section header at the begining
   // of the file. It is not used for anything else
-  Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(Buf);
+  Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
   Shdr.sh_name = 0;
   Shdr.sh_type = SHT_NULL;
   Shdr.sh_flags = 0;
@@ -1076,17 +1097,8 @@ template <class ELFT> void ELFWriter<ELF
   writeSectionData();
   if (WriteSectionHeaders)
     writeShdrs();
-  if (auto E = BufPtr->commit())
-    reportError(File, errorToErrorCode(std::move(E)));
-}
-
-void Writer::createBuffer(uint64_t Size) {
-  auto BufferOrErr =
-      FileOutputBuffer::create(File, Size, FileOutputBuffer::F_executable);
-  handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &) {
-    error("failed to open " + File);
-  });
-  BufPtr = std::move(*BufferOrErr);
+  if (auto E = Buf.commit())
+    reportError(Buf.getName(), errorToErrorCode(std::move(E)));
 }
 
 template <class ELFT> void ELFWriter<ELFT>::finalize() {
@@ -1123,8 +1135,8 @@ template <class ELFT> void ELFWriter<ELF
     Section.finalize();
   }
 
-  createBuffer(totalSize());
-  SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(*BufPtr);
+  Buf.allocate(totalSize());
+  SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf);
 }
 
 void BinaryWriter::write() {
@@ -1133,8 +1145,8 @@ void BinaryWriter::write() {
       continue;
     Section.accept(*SecWriter);
   }
-  if (auto E = BufPtr->commit())
-    reportError(File, errorToErrorCode(std::move(E)));
+  if (auto E = Buf.commit())
+    reportError(Buf.getName(), errorToErrorCode(std::move(E)));
 }
 
 void BinaryWriter::finalize() {
@@ -1216,8 +1228,8 @@ void BinaryWriter::finalize() {
       TotalSize = std::max(TotalSize, Section->Offset + Section->Size);
   }
 
-  createBuffer(TotalSize);
-  SecWriter = llvm::make_unique<BinarySectionWriter>(*BufPtr);
+  Buf.allocate(TotalSize);
+  SecWriter = llvm::make_unique<BinarySectionWriter>(Buf);
 }
 
 namespace llvm {

Modified: llvm/trunk/tools/llvm-objcopy/Object.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/Object.h?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/Object.h (original)
+++ llvm/trunk/tools/llvm-objcopy/Object.h Fri Jul  6 10:51:03 2018
@@ -27,6 +27,7 @@
 
 namespace llvm {
 
+class Buffer;
 class SectionBase;
 class Section;
 class OwnedDataSection;
@@ -77,7 +78,7 @@ public:
 
 class SectionWriter : public SectionVisitor {
 protected:
-  FileOutputBuffer &Out;
+  Buffer &Out;
 
 public:
   virtual ~SectionWriter(){};
@@ -91,7 +92,7 @@ public:
   virtual void visit(const GnuDebugLinkSection &Sec) override = 0;
   virtual void visit(const GroupSection &Sec) override = 0;
 
-  SectionWriter(FileOutputBuffer &Buf) : Out(Buf) {}
+  explicit SectionWriter(Buffer &Buf) : Out(Buf) {}
 };
 
 template <class ELFT> class ELFSectionWriter : public SectionWriter {
@@ -107,7 +108,7 @@ public:
   void visit(const GnuDebugLinkSection &Sec) override;
   void visit(const GroupSection &Sec) override;
 
-  ELFSectionWriter(FileOutputBuffer &Buf) : SectionWriter(Buf) {}
+  explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
 };
 
 #define MAKE_SEC_WRITER_FRIEND                                                 \
@@ -123,24 +124,62 @@ public:
   void visit(const GnuDebugLinkSection &Sec) override;
   void visit(const GroupSection &Sec) override;
 
-  BinarySectionWriter(FileOutputBuffer &Buf) : SectionWriter(Buf) {}
+  explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
+};
+
+// The class Buffer abstracts out the common interface of FileOutputBuffer and
+// WritableMemoryBuffer so that the hierarchy of Writers depends on this
+// abstract interface and doesn't depend on a particular implementation.
+// TODO: refactor the buffer classes in LLVM to enable us to use them here
+// directly.
+class Buffer {
+  StringRef Name;
+
+public:
+  virtual ~Buffer();
+  virtual void allocate(size_t Size) = 0;
+  virtual uint8_t *getBufferStart() = 0;
+  virtual Error commit() = 0;
+
+  explicit Buffer(StringRef Name) : Name(Name) {}
+  StringRef getName() const { return Name; }
+};
+
+class FileBuffer : public Buffer {
+  std::unique_ptr<FileOutputBuffer> Buf;
+
+public:
+  void allocate(size_t Size) override;
+  uint8_t *getBufferStart() override;
+  Error commit() override;
+
+  explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
+};
+
+class MemBuffer : public Buffer {
+  std::unique_ptr<WritableMemoryBuffer> Buf;
+
+public:
+  void allocate(size_t Size) override;
+  uint8_t *getBufferStart() override;
+  Error commit() override;
+
+  explicit MemBuffer(StringRef Name) : Buffer(Name) {}
+
+  std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer();
 };
 
 class Writer {
 protected:
-  StringRef File;
   Object &Obj;
-  std::unique_ptr<FileOutputBuffer> BufPtr;
-
-  void createBuffer(uint64_t Size);
+  Buffer &Buf;
 
 public:
   virtual ~Writer();
-
   virtual void finalize() = 0;
   virtual void write() = 0;
 
-  Writer(StringRef File, Object &Obj) : File(File), Obj(Obj) {}
+  Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {}
 };
 
 template <class ELFT> class ELFWriter : public Writer {
@@ -169,8 +208,8 @@ public:
 
   void finalize() override;
   void write() override;
-  ELFWriter(StringRef File, Object &Obj, bool WSH)
-      : Writer(File, Obj), WriteSectionHeaders(WSH) {}
+  ELFWriter(Object &Obj, Buffer &Buf, bool WSH)
+      : Writer(Obj, Buf), WriteSectionHeaders(WSH) {}
 };
 
 class BinaryWriter : public Writer {
@@ -183,7 +222,7 @@ public:
   ~BinaryWriter() {}
   void finalize() override;
   void write() override;
-  BinaryWriter(StringRef File, Object &Obj) : Writer(File, Obj) {}
+  BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
 };
 
 class SectionBase {
@@ -569,14 +608,12 @@ public:
 };
 
 class ELFReader : public Reader {
-private:
-  std::unique_ptr<Binary> Bin;
-  std::shared_ptr<MemoryBuffer> Data;
+  Binary *Bin;
 
 public:
   ElfType getElfType() const;
   std::unique_ptr<Object> create() const override;
-  ELFReader(StringRef File);
+  explicit ELFReader(Binary *B) : Bin(B){};
 };
 
 class Object {

Modified: llvm/trunk/tools/llvm-objcopy/llvm-objcopy.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/llvm-objcopy.cpp?rev=336455&r1=336454&r2=336455&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/llvm-objcopy.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/llvm-objcopy.cpp Fri Jul  6 10:51:03 2018
@@ -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 @@ bool OnlyKeepDWOPred(const Object &Obj,
 }
 
 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 @@ void SplitDWOToFile(const CopyConfig &Co
   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,26 +444,91 @@ void HandleArgs(const CopyConfig &Config
     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();
 
-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);
+  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) {
+  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
 // help flag is set then ParseObjcopyOptions will print the help messege and
 // exit.
@@ -584,7 +652,7 @@ CopyConfig ParseStripOptions(ArrayRef<co
       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);
 




More information about the llvm-commits mailing list