diff --git a/test/tools/llvm-objcopy/basic-archive-copy.test b/test/tools/llvm-objcopy/basic-archive-copy.test new file mode 100644 index 00000000000..bc050c3c70d --- /dev/null +++ b/test/tools/llvm-objcopy/basic-archive-copy.test @@ -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 diff --git a/test/tools/llvm-objcopy/fail-no-output-directory.test b/test/tools/llvm-objcopy/fail-no-output-directory.test index 89bf62d3556..f66b2e09fce 100644 --- a/test/tools/llvm-objcopy/fail-no-output-directory.test +++ b/test/tools/llvm-objcopy/fail-no-output-directory.test @@ -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: diff --git a/test/tools/llvm-objcopy/strip-all.test b/test/tools/llvm-objcopy/strip-all.test index 2e935526953..d650ab790b8 100644 --- a/test/tools/llvm-objcopy/strip-all.test +++ b/test/tools/llvm-objcopy/strip-all.test @@ -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 diff --git a/test/tools/llvm-objcopy/strip-debug.test b/test/tools/llvm-objcopy/strip-debug.test index 4c217f1d946..c8f90d9ca19 100644 --- a/test/tools/llvm-objcopy/strip-debug.test +++ b/test/tools/llvm-objcopy/strip-debug.test @@ -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: ! + +# 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 diff --git a/tools/llvm-objcopy/Object.cpp b/tools/llvm-objcopy/Object.cpp index 68420670d52..f803ffe1d81 100644 --- a/tools/llvm-objcopy/Object.cpp +++ b/tools/llvm-objcopy/Object.cpp @@ -30,12 +30,43 @@ using namespace llvm; using namespace object; using namespace ELF; +Buffer::~Buffer() {} + +void FileBuffer::allocate(size_t Size) { + Expected> 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(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(Buf->getBufferStart()); +} + +std::unique_ptr MemBuffer::releaseMemoryBuffer() { + return std::move(Buf); +} + template void ELFWriter::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(Buf); + uint8_t *B = Buf.getBufferStart(); + B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); + Elf_Phdr &Phdr = *reinterpret_cast(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 void ELFWriter::writeShdr(const SectionBase &Sec) { - uint8_t *Buf = BufPtr->getBufferStart(); - Buf += Sec.HeaderOffset; - typename ELFT::Shdr &Shdr = *reinterpret_cast(Buf); + uint8_t *B = Buf.getBufferStart(); + B += Sec.HeaderOffset; + typename ELFT::Shdr &Shdr = *reinterpret_cast(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; Shdr.sh_flags = Sec.Flags; @@ -410,9 +441,7 @@ void Section::initialize(SectionTableRef SecTable) { } } -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>(Bin.get())) + if (isa>(Bin)) return ELFT_ELF32LE; - if (isa>(Bin.get())) + if (isa>(Bin)) return ELFT_ELF64LE; - if (isa>(Bin.get())) + if (isa>(Bin)) return ELFT_ELF32BE; - if (isa>(Bin.get())) + if (isa>(Bin)) return ELFT_ELF64BE; llvm_unreachable("Invalid ELFType"); } std::unique_ptr ELFReader::create() const { auto Obj = llvm::make_unique(); - if (auto *o = dyn_cast>(Bin.get())) { + if (auto *o = dyn_cast>(Bin)) { ELFBuilder Builder(*o, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast>(Bin.get())) { + } else if (auto *o = dyn_cast>(Bin)) { ELFBuilder Builder(*o, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast>(Bin.get())) { + } else if (auto *o = dyn_cast>(Bin)) { ELFBuilder Builder(*o, *Obj); Builder.build(); return Obj; - } else if (auto *o = dyn_cast>(Bin.get())) { + } else if (auto *o = dyn_cast>(Bin)) { ELFBuilder Builder(*o, *Obj); Builder.build(); return Obj; @@ -857,8 +878,8 @@ std::unique_ptr ELFReader::create() const { } template void ELFWriter::writeEhdr() { - uint8_t *Buf = BufPtr->getBufferStart(); - Elf_Ehdr &Ehdr = *reinterpret_cast(Buf); + uint8_t *B = Buf.getBufferStart(); + Elf_Ehdr &Ehdr = *reinterpret_cast(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 void ELFWriter::writePhdrs() { } template void ELFWriter::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(Buf); + Elf_Shdr &Shdr = *reinterpret_cast(B); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; @@ -1076,17 +1097,8 @@ template void ELFWriter::write() { 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 void ELFWriter::finalize() { @@ -1123,8 +1135,8 @@ template void ELFWriter::finalize() { Section.finalize(); } - createBuffer(totalSize()); - SecWriter = llvm::make_unique>(*BufPtr); + Buf.allocate(totalSize()); + SecWriter = llvm::make_unique>(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(*BufPtr); + Buf.allocate(TotalSize); + SecWriter = llvm::make_unique(Buf); } namespace llvm { diff --git a/tools/llvm-objcopy/Object.h b/tools/llvm-objcopy/Object.h index 48de0e543a0..2e20b5b299f 100644 --- a/tools/llvm-objcopy/Object.h +++ b/tools/llvm-objcopy/Object.h @@ -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 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 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 Buf; + +public: + void allocate(size_t Size) override; + uint8_t *getBufferStart() override; + Error commit() override; + + explicit MemBuffer(StringRef Name) : Buffer(Name) {} + + std::unique_ptr releaseMemoryBuffer(); }; class Writer { protected: - StringRef File; Object &Obj; - std::unique_ptr 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 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 Bin; - std::shared_ptr Data; + Binary *Bin; public: ElfType getElfType() const; std::unique_ptr create() const override; - ELFReader(StringRef File); + explicit ELFReader(Binary *B) : Bin(B){}; }; class Object { diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp index bf58f342ee4..c28862f073a 100644 --- a/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/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 @@ bool OnlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { } std::unique_ptr CreateWriter(const CopyConfig &Config, Object &Obj, - StringRef File, ElfType OutputElfType) { + Buffer &Buf, ElfType OutputElfType) { if (Config.OutputFormat == "binary") { - return llvm::make_unique(File, Obj); + return llvm::make_unique(Obj, Buf); } // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return llvm::make_unique>(File, Obj, + return llvm::make_unique>(Obj, Buf, !Config.StripSections); case ELFT_ELF64LE: - return llvm::make_unique>(File, Obj, + return llvm::make_unique>(Obj, Buf, !Config.StripSections); case ELFT_ELF32BE: - return llvm::make_unique>(File, Obj, + return llvm::make_unique>(Obj, Buf, !Config.StripSections); case ELFT_ELF64BE: - return llvm::make_unique>(File, Obj, + return llvm::make_unique>(Obj, Buf, !Config.StripSections); } llvm_unreachable("Invalid output format"); @@ -218,7 +220,8 @@ void SplitDWOToFile(const CopyConfig &Config, const Reader &Reader, 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 @@ void HandleArgs(const CopyConfig &Config, Object &Obj, const Reader &Reader, Obj.addSection(Config.AddGnuDebugLink); } -std::unique_ptr CreateReader(StringRef InputFilename, - ElfType &OutputElfType) { - // Right now we can only read ELF files so there's only one reader; - auto Out = llvm::make_unique(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 Obj = Reader.create(); + + HandleArgs(Config, *Obj, Reader, Reader.getElfType()); + + std::unique_ptr 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 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 NewArchiveMembers; + Error Err = Error::success(); + for (const Archive::Child &Child : Ar.children(Err)) { + Expected> ChildOrErr = Child.getAsBinary(); + if (!ChildOrErr) + reportError(Ar.getFileName(), ChildOrErr.takeError()); + Expected ChildNameOrErr = Child.getName(); + if (!ChildNameOrErr) + reportError(Ar.getFileName(), ChildNameOrErr.takeError()); + + MemBuffer MB(ChildNameOrErr.get()); + ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB); + + Expected 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> BinaryOrErr = + createBinary(Config.InputFilename); + if (!BinaryOrErr) + reportError(Config.InputFilename, BinaryOrErr.takeError()); + + if (Archive *Ar = dyn_cast(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 @@ CopyConfig ParseStripOptions(ArrayRef ArgsArr) { 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);