| //===- Archive.cpp --------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "Archive.h" | |
| #include "llvm/ObjCopy/CommonConfig.h" | |
| #include "llvm/ObjCopy/MultiFormatConfig.h" | |
| #include "llvm/ObjCopy/ObjCopy.h" | |
| #include "llvm/Object/Error.h" | |
| #include "llvm/Object/MachO.h" | |
| #include "llvm/Support/FileOutputBuffer.h" | |
| #include "llvm/Support/SmallVectorMemoryBuffer.h" | |
| namespace llvm { | |
| namespace objcopy { | |
| using namespace llvm::object; | |
| Expected<std::vector<NewArchiveMember>> | |
| createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) { | |
| std::vector<NewArchiveMember> NewArchiveMembers; | |
| Error Err = Error::success(); | |
| for (const Archive::Child &Child : Ar.children(Err)) { | |
| Expected<StringRef> ChildNameOrErr = Child.getName(); | |
| if (!ChildNameOrErr) | |
| return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); | |
| Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); | |
| if (!ChildOrErr) | |
| return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", | |
| ChildOrErr.takeError()); | |
| SmallVector<char, 0> Buffer; | |
| raw_svector_ostream MemStream(Buffer); | |
| if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream)) | |
| return std::move(E); | |
| Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember( | |
| Child, Config.getCommonConfig().DeterministicArchives); | |
| if (!Member) | |
| return createFileError(Ar.getFileName(), Member.takeError()); | |
| Member->Buf = std::make_unique<SmallVectorMemoryBuffer>( | |
| std::move(Buffer), ChildNameOrErr.get()); | |
| Member->MemberName = Member->Buf->getBufferIdentifier(); | |
| NewArchiveMembers.push_back(std::move(*Member)); | |
| } | |
| if (Err) | |
| return createFileError(Config.getCommonConfig().InputFilename, | |
| std::move(Err)); | |
| return std::move(NewArchiveMembers); | |
| } | |
| // For regular archives this function simply calls llvm::writeArchive, | |
| // For thin archives it writes the archive file itself as well as its members. | |
| static Error deepWriteArchive(StringRef ArcName, | |
| ArrayRef<NewArchiveMember> NewMembers, | |
| bool WriteSymtab, object::Archive::Kind Kind, | |
| bool Deterministic, bool Thin) { | |
| if (Kind == object::Archive::K_BSD && !NewMembers.empty() && | |
| NewMembers.front().detectKindFromObject() == object::Archive::K_DARWIN) | |
| Kind = object::Archive::K_DARWIN; | |
| if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, | |
| Deterministic, Thin)) | |
| return createFileError(ArcName, std::move(E)); | |
| if (!Thin) | |
| return Error::success(); | |
| for (const NewArchiveMember &Member : NewMembers) { | |
| // For regular files (as 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. | |
| Expected<std::unique_ptr<FileOutputBuffer>> FB = | |
| FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(), | |
| FileOutputBuffer::F_executable); | |
| if (!FB) | |
| return FB.takeError(); | |
| std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), | |
| (*FB)->getBufferStart()); | |
| if (Error E = (*FB)->commit()) | |
| return E; | |
| } | |
| return Error::success(); | |
| } | |
| Error executeObjcopyOnArchive(const MultiFormatConfig &Config, | |
| const object::Archive &Ar) { | |
| Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = | |
| createNewArchiveMembers(Config, Ar); | |
| if (!NewArchiveMembersOrErr) | |
| return NewArchiveMembersOrErr.takeError(); | |
| const CommonConfig &CommonConfig = Config.getCommonConfig(); | |
| return deepWriteArchive(CommonConfig.OutputFilename, *NewArchiveMembersOrErr, | |
| Ar.hasSymbolTable(), Ar.kind(), | |
| CommonConfig.DeterministicArchives, Ar.isThin()); | |
| } | |
| } // end namespace objcopy | |
| } // end namespace llvm |