xref: /freebsd/contrib/llvm-project/llvm/lib/Object/ArchiveWriter.cpp (revision 419822b372f543b22d7fb04eae0dffacf058feb6)
1  //===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===//
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  // This file defines the writeArchive function.
10  //
11  //===----------------------------------------------------------------------===//
12  
13  #include "llvm/Object/ArchiveWriter.h"
14  #include "llvm/ADT/ArrayRef.h"
15  #include "llvm/ADT/StringMap.h"
16  #include "llvm/ADT/StringRef.h"
17  #include "llvm/BinaryFormat/Magic.h"
18  #include "llvm/IR/LLVMContext.h"
19  #include "llvm/Object/Archive.h"
20  #include "llvm/Object/Error.h"
21  #include "llvm/Object/ObjectFile.h"
22  #include "llvm/Object/SymbolicFile.h"
23  #include "llvm/Support/Alignment.h"
24  #include "llvm/Support/EndianStream.h"
25  #include "llvm/Support/Errc.h"
26  #include "llvm/Support/ErrorHandling.h"
27  #include "llvm/Support/Format.h"
28  #include "llvm/Support/Path.h"
29  #include "llvm/Support/SmallVectorMemoryBuffer.h"
30  #include "llvm/Support/ToolOutputFile.h"
31  #include "llvm/Support/raw_ostream.h"
32  
33  #include <map>
34  
35  #if !defined(_MSC_VER) && !defined(__MINGW32__)
36  #include <unistd.h>
37  #else
38  #include <io.h>
39  #endif
40  
41  using namespace llvm;
42  
43  NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
44      : Buf(MemoryBuffer::getMemBuffer(BufRef, false)),
45        MemberName(BufRef.getBufferIdentifier()) {}
46  
47  Expected<NewArchiveMember>
48  NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,
49                                 bool Deterministic) {
50    Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();
51    if (!BufOrErr)
52      return BufOrErr.takeError();
53  
54    NewArchiveMember M;
55    M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);
56    M.MemberName = M.Buf->getBufferIdentifier();
57    if (!Deterministic) {
58      auto ModTimeOrErr = OldMember.getLastModified();
59      if (!ModTimeOrErr)
60        return ModTimeOrErr.takeError();
61      M.ModTime = ModTimeOrErr.get();
62      Expected<unsigned> UIDOrErr = OldMember.getUID();
63      if (!UIDOrErr)
64        return UIDOrErr.takeError();
65      M.UID = UIDOrErr.get();
66      Expected<unsigned> GIDOrErr = OldMember.getGID();
67      if (!GIDOrErr)
68        return GIDOrErr.takeError();
69      M.GID = GIDOrErr.get();
70      Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();
71      if (!AccessModeOrErr)
72        return AccessModeOrErr.takeError();
73      M.Perms = AccessModeOrErr.get();
74    }
75    return std::move(M);
76  }
77  
78  Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,
79                                                       bool Deterministic) {
80    sys::fs::file_status Status;
81    auto FDOrErr = sys::fs::openNativeFileForRead(FileName);
82    if (!FDOrErr)
83      return FDOrErr.takeError();
84    sys::fs::file_t FD = *FDOrErr;
85    assert(FD != sys::fs::kInvalidFile);
86  
87    if (auto EC = sys::fs::status(FD, Status))
88      return errorCodeToError(EC);
89  
90    // Opening a directory doesn't make sense. Let it fail.
91    // Linux cannot open directories with open(2), although
92    // cygwin and *bsd can.
93    if (Status.type() == sys::fs::file_type::directory_file)
94      return errorCodeToError(make_error_code(errc::is_a_directory));
95  
96    ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
97        MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);
98    if (!MemberBufferOrErr)
99      return errorCodeToError(MemberBufferOrErr.getError());
100  
101    if (auto EC = sys::fs::closeFile(FD))
102      return errorCodeToError(EC);
103  
104    NewArchiveMember M;
105    M.Buf = std::move(*MemberBufferOrErr);
106    M.MemberName = M.Buf->getBufferIdentifier();
107    if (!Deterministic) {
108      M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(
109          Status.getLastModificationTime());
110      M.UID = Status.getUser();
111      M.GID = Status.getGroup();
112      M.Perms = Status.permissions();
113    }
114    return std::move(M);
115  }
116  
117  template <typename T>
118  static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {
119    uint64_t OldPos = OS.tell();
120    OS << Data;
121    unsigned SizeSoFar = OS.tell() - OldPos;
122    assert(SizeSoFar <= Size && "Data doesn't fit in Size");
123    OS.indent(Size - SizeSoFar);
124  }
125  
126  static bool isDarwin(object::Archive::Kind Kind) {
127    return Kind == object::Archive::K_DARWIN ||
128           Kind == object::Archive::K_DARWIN64;
129  }
130  
131  static bool isBSDLike(object::Archive::Kind Kind) {
132    switch (Kind) {
133    case object::Archive::K_GNU:
134    case object::Archive::K_GNU64:
135      return false;
136    case object::Archive::K_BSD:
137    case object::Archive::K_DARWIN:
138    case object::Archive::K_DARWIN64:
139      return true;
140    case object::Archive::K_COFF:
141      break;
142    }
143    llvm_unreachable("not supported for writting");
144  }
145  
146  template <class T>
147  static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) {
148    support::endian::write(Out, Val,
149                           isBSDLike(Kind) ? support::little : support::big);
150  }
151  
152  static void printRestOfMemberHeader(
153      raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,
154      unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
155    printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);
156  
157    // The format has only 6 chars for uid and gid. Truncate if the provided
158    // values don't fit.
159    printWithSpacePadding(Out, UID % 1000000, 6);
160    printWithSpacePadding(Out, GID % 1000000, 6);
161  
162    printWithSpacePadding(Out, format("%o", Perms), 8);
163    printWithSpacePadding(Out, Size, 10);
164    Out << "`\n";
165  }
166  
167  static void
168  printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name,
169                            const sys::TimePoint<std::chrono::seconds> &ModTime,
170                            unsigned UID, unsigned GID, unsigned Perms,
171                            uint64_t Size) {
172    printWithSpacePadding(Out, Twine(Name) + "/", 16);
173    printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
174  }
175  
176  static void
177  printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,
178                       const sys::TimePoint<std::chrono::seconds> &ModTime,
179                       unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {
180    uint64_t PosAfterHeader = Pos + 60 + Name.size();
181    // Pad so that even 64 bit object files are aligned.
182    unsigned Pad = offsetToAlignment(PosAfterHeader, Align(8));
183    unsigned NameWithPadding = Name.size() + Pad;
184    printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
185    printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
186                            NameWithPadding + Size);
187    Out << Name;
188    while (Pad--)
189      Out.write(uint8_t(0));
190  }
191  
192  static bool useStringTable(bool Thin, StringRef Name) {
193    return Thin || Name.size() >= 16 || Name.contains('/');
194  }
195  
196  static bool is64BitKind(object::Archive::Kind Kind) {
197    switch (Kind) {
198    case object::Archive::K_GNU:
199    case object::Archive::K_BSD:
200    case object::Archive::K_DARWIN:
201    case object::Archive::K_COFF:
202      return false;
203    case object::Archive::K_DARWIN64:
204    case object::Archive::K_GNU64:
205      return true;
206    }
207    llvm_unreachable("not supported for writting");
208  }
209  
210  static void
211  printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable,
212                    StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind,
213                    bool Thin, const NewArchiveMember &M,
214                    sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) {
215    if (isBSDLike(Kind))
216      return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,
217                                  M.Perms, Size);
218    if (!useStringTable(Thin, M.MemberName))
219      return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,
220                                       M.Perms, Size);
221    Out << '/';
222    uint64_t NamePos;
223    if (Thin) {
224      NamePos = StringTable.tell();
225      StringTable << M.MemberName << "/\n";
226    } else {
227      auto Insertion = MemberNames.insert({M.MemberName, uint64_t(0)});
228      if (Insertion.second) {
229        Insertion.first->second = StringTable.tell();
230        StringTable << M.MemberName << "/\n";
231      }
232      NamePos = Insertion.first->second;
233    }
234    printWithSpacePadding(Out, NamePos, 15);
235    printRestOfMemberHeader(Out, ModTime, M.UID, M.GID, M.Perms, Size);
236  }
237  
238  namespace {
239  struct MemberData {
240    std::vector<unsigned> Symbols;
241    std::string Header;
242    StringRef Data;
243    StringRef Padding;
244  };
245  } // namespace
246  
247  static MemberData computeStringTable(StringRef Names) {
248    unsigned Size = Names.size();
249    unsigned Pad = offsetToAlignment(Size, Align(2));
250    std::string Header;
251    raw_string_ostream Out(Header);
252    printWithSpacePadding(Out, "//", 48);
253    printWithSpacePadding(Out, Size + Pad, 10);
254    Out << "`\n";
255    Out.flush();
256    return {{}, std::move(Header), Names, Pad ? "\n" : ""};
257  }
258  
259  static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {
260    using namespace std::chrono;
261  
262    if (!Deterministic)
263      return time_point_cast<seconds>(system_clock::now());
264    return sys::TimePoint<seconds>();
265  }
266  
267  static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
268    Expected<uint32_t> SymFlagsOrErr = S.getFlags();
269    if (!SymFlagsOrErr)
270      // TODO: Actually report errors helpfully.
271      report_fatal_error(SymFlagsOrErr.takeError());
272    if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific)
273      return false;
274    if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global))
275      return false;
276    if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined)
277      return false;
278    return true;
279  }
280  
281  static void printNBits(raw_ostream &Out, object::Archive::Kind Kind,
282                         uint64_t Val) {
283    if (is64BitKind(Kind))
284      print<uint64_t>(Out, Kind, Val);
285    else
286      print<uint32_t>(Out, Kind, Val);
287  }
288  
289  static uint64_t computeSymbolTableSize(object::Archive::Kind Kind,
290                                         uint64_t NumSyms, uint64_t OffsetSize,
291                                         StringRef StringTable,
292                                         uint32_t *Padding = nullptr) {
293    assert((OffsetSize == 4 || OffsetSize == 8) && "Unsupported OffsetSize");
294    uint64_t Size = OffsetSize; // Number of entries
295    if (isBSDLike(Kind))
296      Size += NumSyms * OffsetSize * 2; // Table
297    else
298      Size += NumSyms * OffsetSize; // Table
299    if (isBSDLike(Kind))
300      Size += OffsetSize; // byte count
301    Size += StringTable.size();
302    // ld64 expects the members to be 8-byte aligned for 64-bit content and at
303    // least 4-byte aligned for 32-bit content.  Opt for the larger encoding
304    // uniformly.
305    // We do this for all bsd formats because it simplifies aligning members.
306    uint32_t Pad = offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));
307    Size += Pad;
308    if (Padding)
309      *Padding = Pad;
310    return Size;
311  }
312  
313  static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,
314                                     bool Deterministic, uint64_t Size) {
315    if (isBSDLike(Kind)) {
316      const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";
317      printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,
318                           Size);
319    } else {
320      const char *Name = is64BitKind(Kind) ? "/SYM64" : "";
321      printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);
322    }
323  }
324  
325  static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,
326                               bool Deterministic, ArrayRef<MemberData> Members,
327                               StringRef StringTable) {
328    // We don't write a symbol table on an archive with no members -- except on
329    // Darwin, where the linker will abort unless the archive has a symbol table.
330    if (StringTable.empty() && !isDarwin(Kind))
331      return;
332  
333    unsigned NumSyms = 0;
334    for (const MemberData &M : Members)
335      NumSyms += M.Symbols.size();
336  
337    uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;
338    uint32_t Pad;
339    uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize, StringTable, &Pad);
340    writeSymbolTableHeader(Out, Kind, Deterministic, Size);
341  
342    uint64_t Pos = Out.tell() + Size;
343  
344    if (isBSDLike(Kind))
345      printNBits(Out, Kind, NumSyms * 2 * OffsetSize);
346    else
347      printNBits(Out, Kind, NumSyms);
348  
349    for (const MemberData &M : Members) {
350      for (unsigned StringOffset : M.Symbols) {
351        if (isBSDLike(Kind))
352          printNBits(Out, Kind, StringOffset);
353        printNBits(Out, Kind, Pos); // member offset
354      }
355      Pos += M.Header.size() + M.Data.size() + M.Padding.size();
356    }
357  
358    if (isBSDLike(Kind))
359      // byte count of the string table
360      printNBits(Out, Kind, StringTable.size());
361    Out << StringTable;
362  
363    while (Pad--)
364      Out.write(uint8_t(0));
365  }
366  
367  static Expected<std::vector<unsigned>>
368  getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
369    std::vector<unsigned> Ret;
370  
371    // In the scenario when LLVMContext is populated SymbolicFile will contain a
372    // reference to it, thus SymbolicFile should be destroyed first.
373    LLVMContext Context;
374    std::unique_ptr<object::SymbolicFile> Obj;
375  
376    const file_magic Type = identify_magic(Buf.getBuffer());
377    // Treat unsupported file types as having no symbols.
378    if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
379      return Ret;
380    if (Type == file_magic::bitcode) {
381      auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
382          Buf, file_magic::bitcode, &Context);
383      if (!ObjOrErr)
384        return ObjOrErr.takeError();
385      Obj = std::move(*ObjOrErr);
386    } else {
387      auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
388      if (!ObjOrErr)
389        return ObjOrErr.takeError();
390      Obj = std::move(*ObjOrErr);
391    }
392  
393    HasObject = true;
394    for (const object::BasicSymbolRef &S : Obj->symbols()) {
395      if (!isArchiveSymbol(S))
396        continue;
397      Ret.push_back(SymNames.tell());
398      if (Error E = S.printName(SymNames))
399        return std::move(E);
400      SymNames << '\0';
401    }
402    return Ret;
403  }
404  
405  static Expected<std::vector<MemberData>>
406  computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
407                    object::Archive::Kind Kind, bool Thin, bool Deterministic,
408                    bool NeedSymbols, ArrayRef<NewArchiveMember> NewMembers) {
409    static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
410  
411    // This ignores the symbol table, but we only need the value mod 8 and the
412    // symbol table is aligned to be a multiple of 8 bytes
413    uint64_t Pos = 0;
414  
415    std::vector<MemberData> Ret;
416    bool HasObject = false;
417  
418    // Deduplicate long member names in the string table and reuse earlier name
419    // offsets. This especially saves space for COFF Import libraries where all
420    // members have the same name.
421    StringMap<uint64_t> MemberNames;
422  
423    // UniqueTimestamps is a special case to improve debugging on Darwin:
424    //
425    // The Darwin linker does not link debug info into the final
426    // binary. Instead, it emits entries of type N_OSO in in the output
427    // binary's symbol table, containing references to the linked-in
428    // object files. Using that reference, the debugger can read the
429    // debug data directly from the object files. Alternatively, an
430    // invocation of 'dsymutil' will link the debug data from the object
431    // files into a dSYM bundle, which can be loaded by the debugger,
432    // instead of the object files.
433    //
434    // For an object file, the N_OSO entries contain the absolute path
435    // path to the file, and the file's timestamp. For an object
436    // included in an archive, the path is formatted like
437    // "/absolute/path/to/archive.a(member.o)", and the timestamp is the
438    // archive member's timestamp, rather than the archive's timestamp.
439    //
440    // However, this doesn't always uniquely identify an object within
441    // an archive -- an archive file can have multiple entries with the
442    // same filename. (This will happen commonly if the original object
443    // files started in different directories.) The only way they get
444    // distinguished, then, is via the timestamp. But this process is
445    // unable to find the correct object file in the archive when there
446    // are two files of the same name and timestamp.
447    //
448    // Additionally, timestamp==0 is treated specially, and causes the
449    // timestamp to be ignored as a match criteria.
450    //
451    // That will "usually" work out okay when creating an archive not in
452    // deterministic timestamp mode, because the objects will probably
453    // have been created at different timestamps.
454    //
455    // To ameliorate this problem, in deterministic archive mode (which
456    // is the default), on Darwin we will emit a unique non-zero
457    // timestamp for each entry with a duplicated name. This is still
458    // deterministic: the only thing affecting that timestamp is the
459    // order of the files in the resultant archive.
460    //
461    // See also the functions that handle the lookup:
462    // in lldb: ObjectContainerBSDArchive::Archive::FindObject()
463    // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().
464    bool UniqueTimestamps = Deterministic && isDarwin(Kind);
465    std::map<StringRef, unsigned> FilenameCount;
466    if (UniqueTimestamps) {
467      for (const NewArchiveMember &M : NewMembers)
468        FilenameCount[M.MemberName]++;
469      for (auto &Entry : FilenameCount)
470        Entry.second = Entry.second > 1 ? 1 : 0;
471    }
472  
473    for (const NewArchiveMember &M : NewMembers) {
474      std::string Header;
475      raw_string_ostream Out(Header);
476  
477      MemoryBufferRef Buf = M.Buf->getMemBufferRef();
478      StringRef Data = Thin ? "" : Buf.getBuffer();
479  
480      // ld64 expects the members to be 8-byte aligned for 64-bit content and at
481      // least 4-byte aligned for 32-bit content.  Opt for the larger encoding
482      // uniformly.  This matches the behaviour with cctools and ensures that ld64
483      // is happy with archives that we generate.
484      unsigned MemberPadding =
485          isDarwin(Kind) ? offsetToAlignment(Data.size(), Align(8)) : 0;
486      unsigned TailPadding =
487          offsetToAlignment(Data.size() + MemberPadding, Align(2));
488      StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding);
489  
490      sys::TimePoint<std::chrono::seconds> ModTime;
491      if (UniqueTimestamps)
492        // Increment timestamp for each file of a given name.
493        ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++);
494      else
495        ModTime = M.ModTime;
496  
497      uint64_t Size = Buf.getBufferSize() + MemberPadding;
498      if (Size > object::Archive::MaxMemberSize) {
499        std::string StringMsg =
500            "File " + M.MemberName.str() + " exceeds size limit";
501        return make_error<object::GenericBinaryError>(
502            std::move(StringMsg), object::object_error::parse_failed);
503      }
504  
505      printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M,
506                        ModTime, Size);
507      Out.flush();
508  
509      std::vector<unsigned> Symbols;
510      if (NeedSymbols) {
511        Expected<std::vector<unsigned>> SymbolsOrErr =
512            getSymbols(Buf, SymNames, HasObject);
513        if (auto E = SymbolsOrErr.takeError())
514          return std::move(E);
515        Symbols = std::move(*SymbolsOrErr);
516      }
517  
518      Pos += Header.size() + Data.size() + Padding.size();
519      Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding});
520    }
521    // If there are no symbols, emit an empty symbol table, to satisfy Solaris
522    // tools, older versions of which expect a symbol table in a non-empty
523    // archive, regardless of whether there are any symbols in it.
524    if (HasObject && SymNames.tell() == 0)
525      SymNames << '\0' << '\0' << '\0';
526    return Ret;
527  }
528  
529  namespace llvm {
530  
531  static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) {
532    SmallString<128> Ret = P;
533    std::error_code Err = sys::fs::make_absolute(Ret);
534    if (Err)
535      return Err;
536    sys::path::remove_dots(Ret, /*removedotdot*/ true);
537    return Ret;
538  }
539  
540  // Compute the relative path from From to To.
541  Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {
542    ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(To);
543    ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(From);
544    if (!PathToOrErr || !DirFromOrErr)
545      return errorCodeToError(std::error_code(errno, std::generic_category()));
546  
547    const SmallString<128> &PathTo = *PathToOrErr;
548    const SmallString<128> &DirFrom = sys::path::parent_path(*DirFromOrErr);
549  
550    // Can't construct a relative path between different roots
551    if (sys::path::root_name(PathTo) != sys::path::root_name(DirFrom))
552      return sys::path::convert_to_slash(PathTo);
553  
554    // Skip common prefixes
555    auto FromTo =
556        std::mismatch(sys::path::begin(DirFrom), sys::path::end(DirFrom),
557                      sys::path::begin(PathTo));
558    auto FromI = FromTo.first;
559    auto ToI = FromTo.second;
560  
561    // Construct relative path
562    SmallString<128> Relative;
563    for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
564      sys::path::append(Relative, sys::path::Style::posix, "..");
565  
566    for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI)
567      sys::path::append(Relative, sys::path::Style::posix, *ToI);
568  
569    return std::string(Relative.str());
570  }
571  
572  static Error writeArchiveToStream(raw_ostream &Out,
573                                    ArrayRef<NewArchiveMember> NewMembers,
574                                    bool WriteSymtab, object::Archive::Kind Kind,
575                                    bool Deterministic, bool Thin) {
576    assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
577  
578    SmallString<0> SymNamesBuf;
579    raw_svector_ostream SymNames(SymNamesBuf);
580    SmallString<0> StringTableBuf;
581    raw_svector_ostream StringTable(StringTableBuf);
582  
583    Expected<std::vector<MemberData>> DataOrErr =
584        computeMemberData(StringTable, SymNames, Kind, Thin, Deterministic,
585                          WriteSymtab, NewMembers);
586    if (Error E = DataOrErr.takeError())
587      return E;
588    std::vector<MemberData> &Data = *DataOrErr;
589  
590    if (!StringTableBuf.empty())
591      Data.insert(Data.begin(), computeStringTable(StringTableBuf));
592  
593    // We would like to detect if we need to switch to a 64-bit symbol table.
594    if (WriteSymtab) {
595      uint64_t MaxOffset = 8; // For the file signature.
596      uint64_t LastOffset = MaxOffset;
597      uint64_t NumSyms = 0;
598      for (const auto &M : Data) {
599        // Record the start of the member's offset
600        LastOffset = MaxOffset;
601        // Account for the size of each part associated with the member.
602        MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size();
603        NumSyms += M.Symbols.size();
604      }
605  
606      // We assume 32-bit offsets to see if 32-bit symbols are possible or not.
607      uint64_t SymtabSize = computeSymbolTableSize(Kind, NumSyms, 4, SymNamesBuf);
608      auto computeSymbolTableHeaderSize =
609          [=] {
610            SmallString<0> TmpBuf;
611            raw_svector_ostream Tmp(TmpBuf);
612            writeSymbolTableHeader(Tmp, Kind, Deterministic, SymtabSize);
613            return TmpBuf.size();
614          };
615      LastOffset += computeSymbolTableHeaderSize() + SymtabSize;
616  
617      // The SYM64 format is used when an archive's member offsets are larger than
618      // 32-bits can hold. The need for this shift in format is detected by
619      // writeArchive. To test this we need to generate a file with a member that
620      // has an offset larger than 32-bits but this demands a very slow test. To
621      // speed the test up we use this environment variable to pretend like the
622      // cutoff happens before 32-bits and instead happens at some much smaller
623      // value.
624      uint64_t Sym64Threshold = 1ULL << 32;
625      const char *Sym64Env = std::getenv("SYM64_THRESHOLD");
626      if (Sym64Env)
627        StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);
628  
629      // If LastOffset isn't going to fit in a 32-bit varible we need to switch
630      // to 64-bit. Note that the file can be larger than 4GB as long as the last
631      // member starts before the 4GB offset.
632      if (LastOffset >= Sym64Threshold) {
633        if (Kind == object::Archive::K_DARWIN)
634          Kind = object::Archive::K_DARWIN64;
635        else
636          Kind = object::Archive::K_GNU64;
637      }
638    }
639  
640    if (Thin)
641      Out << "!<thin>\n";
642    else
643      Out << "!<arch>\n";
644  
645    if (WriteSymtab)
646      writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf);
647  
648    for (const MemberData &M : Data)
649      Out << M.Header << M.Data << M.Padding;
650  
651    Out.flush();
652    return Error::success();
653  }
654  
655  Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
656                     bool WriteSymtab, object::Archive::Kind Kind,
657                     bool Deterministic, bool Thin,
658                     std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
659    Expected<sys::fs::TempFile> Temp =
660        sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");
661    if (!Temp)
662      return Temp.takeError();
663    raw_fd_ostream Out(Temp->FD, false);
664  
665    if (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind,
666                                       Deterministic, Thin)) {
667      if (Error DiscardError = Temp->discard())
668        return joinErrors(std::move(E), std::move(DiscardError));
669      return E;
670    }
671  
672    // At this point, we no longer need whatever backing memory
673    // was used to generate the NewMembers. On Windows, this buffer
674    // could be a mapped view of the file we want to replace (if
675    // we're updating an existing archive, say). In that case, the
676    // rename would still succeed, but it would leave behind a
677    // temporary file (actually the original file renamed) because
678    // a file cannot be deleted while there's a handle open on it,
679    // only renamed. So by freeing this buffer, this ensures that
680    // the last open handle on the destination file, if any, is
681    // closed before we attempt to rename.
682    OldArchiveBuf.reset();
683  
684    return Temp->keep(ArcName);
685  }
686  
687  Expected<std::unique_ptr<MemoryBuffer>>
688  writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab,
689                       object::Archive::Kind Kind, bool Deterministic,
690                       bool Thin) {
691    SmallVector<char, 0> ArchiveBufferVector;
692    raw_svector_ostream ArchiveStream(ArchiveBufferVector);
693  
694    if (Error E = writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab,
695                                       Kind, Deterministic, Thin))
696      return std::move(E);
697  
698    return std::make_unique<SmallVectorMemoryBuffer>(
699        std::move(ArchiveBufferVector));
700  }
701  
702  } // namespace llvm
703