1//===- Archive.cpp --------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Archive.h"
10#include "llvm/ObjCopy/CommonConfig.h"
11#include "llvm/ObjCopy/MultiFormatConfig.h"
12#include "llvm/ObjCopy/ObjCopy.h"
13#include "llvm/Object/Error.h"
14#include "llvm/Support/FileOutputBuffer.h"
15#include "llvm/Support/SmallVectorMemoryBuffer.h"
16
17using namespace llvm;
18using namespace llvm::objcopy;
19using namespace llvm::object;
20
21Expected<std::vector<NewArchiveMember>>
22objcopy::createNewArchiveMembers(const MultiFormatConfig &Config,
23 const Archive &Ar) {
24 std::vector<NewArchiveMember> NewArchiveMembers;
25 Error Err = Error::success();
26 for (const Archive::Child &Child : Ar.children(Err)) {
27 Expected<StringRef> ChildNameOrErr = Child.getName();
28 if (!ChildNameOrErr)
29 return createFileError(F: Ar.getFileName(), E: ChildNameOrErr.takeError());
30
31 auto MemberName = [&](StringRef Prefix) {
32 return (Twine(Prefix) + "(" + *ChildNameOrErr + ")").str();
33 };
34
35 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
36 if (!ChildOrErr)
37 return createFileError(F: MemberName(Ar.getFileName()),
38 E: ChildOrErr.takeError());
39
40 const CommonConfig &CC = Config.getCommonConfig();
41 if (CC.Verbose) {
42 StringRef FormatName = getObjectFormatName(B: *ChildOrErr->get());
43 printCopyMessage(InPath: MemberName(Ar.getFileName()), InFormatName: FormatName,
44 OutPath: MemberName(CC.OutputFilename), OutFormatName: FormatName);
45 }
46
47 SmallVector<char, 0> Buffer;
48 raw_svector_ostream MemStream(Buffer);
49
50 if (Error E = executeObjcopyOnBinary(Config, In&: *ChildOrErr->get(), Out&: MemStream))
51 return std::move(E);
52
53 Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember(
54 OldMember: Child, Deterministic: Config.getCommonConfig().DeterministicArchives);
55 if (!Member)
56 return createFileError(F: Ar.getFileName(), E: Member.takeError());
57
58 Member->Buf = std::make_unique<SmallVectorMemoryBuffer>(
59 args: std::move(Buffer), args&: ChildNameOrErr.get());
60 Member->MemberName = Member->Buf->getBufferIdentifier();
61 NewArchiveMembers.push_back(x: std::move(*Member));
62 }
63 if (Err)
64 return createFileError(F: Config.getCommonConfig().InputFilename,
65 E: std::move(Err));
66 return std::move(NewArchiveMembers);
67}
68
69// For regular archives this function simply calls llvm::writeArchive,
70// For thin archives it writes the archive file itself as well as its members.
71static Error deepWriteArchive(StringRef ArcName,
72 ArrayRef<NewArchiveMember> NewMembers,
73 SymtabWritingMode WriteSymtab,
74 object::Archive::Kind Kind, bool Deterministic,
75 bool Thin) {
76 if (Kind == object::Archive::K_BSD && !NewMembers.empty() &&
77 NewMembers.front().detectKindFromObject() == object::Archive::K_DARWIN)
78 Kind = object::Archive::K_DARWIN;
79
80 if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind,
81 Deterministic, Thin))
82 return createFileError(F: ArcName, E: std::move(E));
83
84 if (!Thin)
85 return Error::success();
86
87 for (const NewArchiveMember &Member : NewMembers) {
88 // For regular files (as is the case for deepWriteArchive),
89 // FileOutputBuffer::create will return OnDiskBuffer.
90 // OnDiskBuffer uses a temporary file and then renames it. So in reality
91 // there is no inefficiency / duplicated in-memory buffers in this case. For
92 // now in-memory buffers can not be completely avoided since
93 // NewArchiveMember still requires them even though writeArchive does not
94 // write them on disk.
95 Expected<std::unique_ptr<FileOutputBuffer>> FB =
96 FileOutputBuffer::create(FilePath: Member.MemberName, Size: Member.Buf->getBufferSize(),
97 Flags: FileOutputBuffer::F_executable);
98 if (!FB)
99 return FB.takeError();
100 std::copy(first: Member.Buf->getBufferStart(), last: Member.Buf->getBufferEnd(),
101 result: (*FB)->getBufferStart());
102 if (Error E = (*FB)->commit())
103 return E;
104 }
105 return Error::success();
106}
107
108Error objcopy::executeObjcopyOnArchive(const MultiFormatConfig &Config,
109 const object::Archive &Ar) {
110 Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
111 createNewArchiveMembers(Config, Ar);
112 if (!NewArchiveMembersOrErr)
113 return NewArchiveMembersOrErr.takeError();
114 const CommonConfig &CommonConfig = Config.getCommonConfig();
115 return deepWriteArchive(ArcName: CommonConfig.OutputFilename, NewMembers: *NewArchiveMembersOrErr,
116 WriteSymtab: Ar.hasSymbolTable() ? SymtabWritingMode::NormalSymtab
117 : SymtabWritingMode::NoSymtab,
118 Kind: Ar.kind(), Deterministic: CommonConfig.DeterministicArchives,
119 Thin: Ar.isThin());
120}
121