brintos

brintos / llvm-project-archived public Read only

0
0
Text · 52.1 KiB · a112597 Raw
1371 lines · cpp
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-exception6//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/COFF.h"21#include "llvm/Object/COFFImportFile.h"22#include "llvm/Object/Error.h"23#include "llvm/Object/IRObjectFile.h"24#include "llvm/Object/MachO.h"25#include "llvm/Object/ObjectFile.h"26#include "llvm/Object/SymbolicFile.h"27#include "llvm/Object/XCOFFObjectFile.h"28#include "llvm/Support/Alignment.h"29#include "llvm/Support/EndianStream.h"30#include "llvm/Support/Errc.h"31#include "llvm/Support/ErrorHandling.h"32#include "llvm/Support/Format.h"33#include "llvm/Support/MathExtras.h"34#include "llvm/Support/Path.h"35#include "llvm/Support/SmallVectorMemoryBuffer.h"36#include "llvm/Support/raw_ostream.h"37 38#include <cerrno>39#include <map>40 41#if !defined(_MSC_VER) && !defined(__MINGW32__)42#include <unistd.h>43#else44#include <io.h>45#endif46 47using namespace llvm;48using namespace llvm::object;49 50struct SymMap {51  bool UseECMap = false;52  std::map<std::string, uint16_t> Map;53  std::map<std::string, uint16_t> ECMap;54};55 56NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)57    : Buf(MemoryBuffer::getMemBuffer(BufRef, false)),58      MemberName(BufRef.getBufferIdentifier()) {}59 60object::Archive::Kind NewArchiveMember::detectKindFromObject() const {61  auto MemBufferRef = this->Buf->getMemBufferRef();62  Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =63      object::ObjectFile::createObjectFile(MemBufferRef);64 65  if (OptionalObject) {66    if (isa<object::MachOObjectFile>(**OptionalObject))67      return object::Archive::K_DARWIN;68    if (isa<object::XCOFFObjectFile>(**OptionalObject))69      return object::Archive::K_AIXBIG;70    if (isa<object::COFFObjectFile>(**OptionalObject) ||71        isa<object::COFFImportFile>(**OptionalObject))72      return object::Archive::K_COFF;73    return object::Archive::K_GNU;74  }75 76  // Squelch the error in case we had a non-object file.77  consumeError(OptionalObject.takeError());78 79  // If we're adding a bitcode file to the archive, detect the Archive kind80  // based on the target triple.81  LLVMContext Context;82  if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {83    if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(84            MemBufferRef, file_magic::bitcode, &Context)) {85      auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);86      auto TargetTriple = Triple(IRObject.getTargetTriple());87      return object::Archive::getDefaultKindForTriple(TargetTriple);88    } else {89      // Squelch the error in case this was not a SymbolicFile.90      consumeError(ObjOrErr.takeError());91    }92  }93 94  return object::Archive::getDefaultKind();95}96 97Expected<NewArchiveMember>98NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,99                               bool Deterministic) {100  Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();101  if (!BufOrErr)102    return BufOrErr.takeError();103 104  NewArchiveMember M;105  M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);106  M.MemberName = M.Buf->getBufferIdentifier();107  if (!Deterministic) {108    auto ModTimeOrErr = OldMember.getLastModified();109    if (!ModTimeOrErr)110      return ModTimeOrErr.takeError();111    M.ModTime = ModTimeOrErr.get();112    Expected<unsigned> UIDOrErr = OldMember.getUID();113    if (!UIDOrErr)114      return UIDOrErr.takeError();115    M.UID = UIDOrErr.get();116    Expected<unsigned> GIDOrErr = OldMember.getGID();117    if (!GIDOrErr)118      return GIDOrErr.takeError();119    M.GID = GIDOrErr.get();120    Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();121    if (!AccessModeOrErr)122      return AccessModeOrErr.takeError();123    M.Perms = AccessModeOrErr.get();124  }125  return std::move(M);126}127 128Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,129                                                     bool Deterministic) {130  sys::fs::file_status Status;131  auto FDOrErr = sys::fs::openNativeFileForRead(FileName);132  if (!FDOrErr)133    return FDOrErr.takeError();134  sys::fs::file_t FD = *FDOrErr;135  assert(FD != sys::fs::kInvalidFile);136 137  if (auto EC = sys::fs::status(FD, Status))138    return errorCodeToError(EC);139 140  // Opening a directory doesn't make sense. Let it fail.141  // Linux cannot open directories with open(2), although142  // cygwin and *bsd can.143  if (Status.type() == sys::fs::file_type::directory_file)144    return errorCodeToError(make_error_code(errc::is_a_directory));145 146  ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =147      MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);148  if (!MemberBufferOrErr)149    return errorCodeToError(MemberBufferOrErr.getError());150 151  if (auto EC = sys::fs::closeFile(FD))152    return errorCodeToError(EC);153 154  NewArchiveMember M;155  M.Buf = std::move(*MemberBufferOrErr);156  M.MemberName = M.Buf->getBufferIdentifier();157  if (!Deterministic) {158    M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(159        Status.getLastModificationTime());160    M.UID = Status.getUser();161    M.GID = Status.getGroup();162    M.Perms = Status.permissions();163  }164  return std::move(M);165}166 167template <typename T>168static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) {169  uint64_t OldPos = OS.tell();170  OS << Data;171  unsigned SizeSoFar = OS.tell() - OldPos;172  assert(SizeSoFar <= Size && "Data doesn't fit in Size");173  OS.indent(Size - SizeSoFar);174}175 176static bool isDarwin(object::Archive::Kind Kind) {177  return Kind == object::Archive::K_DARWIN ||178         Kind == object::Archive::K_DARWIN64;179}180 181static bool isAIXBigArchive(object::Archive::Kind Kind) {182  return Kind == object::Archive::K_AIXBIG;183}184 185static bool isCOFFArchive(object::Archive::Kind Kind) {186  return Kind == object::Archive::K_COFF;187}188 189static bool isBSDLike(object::Archive::Kind Kind) {190  switch (Kind) {191  case object::Archive::K_GNU:192  case object::Archive::K_GNU64:193  case object::Archive::K_AIXBIG:194  case object::Archive::K_COFF:195    return false;196  case object::Archive::K_BSD:197  case object::Archive::K_DARWIN:198  case object::Archive::K_DARWIN64:199    return true;200  }201  llvm_unreachable("not supported for writting");202}203 204template <class T>205static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) {206  support::endian::write(Out, Val,207                         isBSDLike(Kind) ? llvm::endianness::little208                                         : llvm::endianness::big);209}210 211template <class T> static void printLE(raw_ostream &Out, T Val) {212  support::endian::write(Out, Val, llvm::endianness::little);213}214 215static void printRestOfMemberHeader(216    raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,217    unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {218  printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);219 220  // The format has only 6 chars for uid and gid. Truncate if the provided221  // values don't fit.222  printWithSpacePadding(Out, UID % 1000000, 6);223  printWithSpacePadding(Out, GID % 1000000, 6);224 225  printWithSpacePadding(Out, format("%o", Perms), 8);226  printWithSpacePadding(Out, Size, 10);227  Out << "`\n";228}229 230static void231printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name,232                          const sys::TimePoint<std::chrono::seconds> &ModTime,233                          unsigned UID, unsigned GID, unsigned Perms,234                          uint64_t Size) {235  printWithSpacePadding(Out, Twine(Name) + "/", 16);236  printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);237}238 239static void240printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name,241                     const sys::TimePoint<std::chrono::seconds> &ModTime,242                     unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) {243  uint64_t PosAfterHeader = Pos + 60 + Name.size();244  // Pad so that even 64 bit object files are aligned.245  unsigned Pad = offsetToAlignment(PosAfterHeader, Align(8));246  unsigned NameWithPadding = Name.size() + Pad;247  printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);248  printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,249                          NameWithPadding + Size);250  Out << Name;251  while (Pad--)252    Out.write(uint8_t(0));253}254 255static void256printBigArchiveMemberHeader(raw_ostream &Out, StringRef Name,257                            const sys::TimePoint<std::chrono::seconds> &ModTime,258                            unsigned UID, unsigned GID, unsigned Perms,259                            uint64_t Size, uint64_t PrevOffset,260                            uint64_t NextOffset) {261  unsigned NameLen = Name.size();262 263  printWithSpacePadding(Out, Size, 20);           // File member size264  printWithSpacePadding(Out, NextOffset, 20);     // Next member header offset265  printWithSpacePadding(Out, PrevOffset, 20); // Previous member header offset266  printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); // File member date267  // The big archive format has 12 chars for uid and gid.268  printWithSpacePadding(Out, UID % 1000000000000, 12);   // UID269  printWithSpacePadding(Out, GID % 1000000000000, 12);   // GID270  printWithSpacePadding(Out, format("%o", Perms), 12);   // Permission271  printWithSpacePadding(Out, NameLen, 4);                // Name length272  if (NameLen) {273    printWithSpacePadding(Out, Name, NameLen); // Name274    if (NameLen % 2)275      Out.write(uint8_t(0)); // Null byte padding276  }277  Out << "`\n"; // Terminator278}279 280static bool useStringTable(bool Thin, StringRef Name) {281  return Thin || Name.size() >= 16 || Name.contains('/');282}283 284static bool is64BitKind(object::Archive::Kind Kind) {285  switch (Kind) {286  case object::Archive::K_GNU:287  case object::Archive::K_BSD:288  case object::Archive::K_DARWIN:289  case object::Archive::K_COFF:290    return false;291  case object::Archive::K_AIXBIG:292  case object::Archive::K_DARWIN64:293  case object::Archive::K_GNU64:294    return true;295  }296  llvm_unreachable("not supported for writting");297}298 299static void300printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable,301                  StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind,302                  bool Thin, const NewArchiveMember &M,303                  sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) {304  if (isBSDLike(Kind))305    return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID,306                                M.Perms, Size);307  if (!useStringTable(Thin, M.MemberName))308    return printGNUSmallMemberHeader(Out, M.MemberName, ModTime, M.UID, M.GID,309                                     M.Perms, Size);310  Out << '/';311  uint64_t NamePos;312  if (Thin) {313    NamePos = StringTable.tell();314    StringTable << M.MemberName << "/\n";315  } else {316    auto Insertion = MemberNames.insert({M.MemberName, uint64_t(0)});317    if (Insertion.second) {318      Insertion.first->second = StringTable.tell();319      StringTable << M.MemberName;320      if (isCOFFArchive(Kind))321        StringTable << '\0';322      else323        StringTable << "/\n";324    }325    NamePos = Insertion.first->second;326  }327  printWithSpacePadding(Out, NamePos, 15);328  printRestOfMemberHeader(Out, ModTime, M.UID, M.GID, M.Perms, Size);329}330 331namespace {332struct MemberData {333  std::vector<unsigned> Symbols;334  std::string Header;335  StringRef Data;336  StringRef Padding;337  uint64_t PreHeadPadSize = 0;338  std::unique_ptr<SymbolicFile> SymFile = nullptr;339};340} // namespace341 342static MemberData computeStringTable(StringRef Names) {343  unsigned Size = Names.size();344  unsigned Pad = offsetToAlignment(Size, Align(2));345  std::string Header;346  raw_string_ostream Out(Header);347  printWithSpacePadding(Out, "//", 48);348  printWithSpacePadding(Out, Size + Pad, 10);349  Out << "`\n";350  Out.flush();351  return {{}, std::move(Header), Names, Pad ? "\n" : ""};352}353 354static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {355  using namespace std::chrono;356 357  if (!Deterministic)358    return time_point_cast<seconds>(system_clock::now());359  return sys::TimePoint<seconds>();360}361 362static bool isArchiveSymbol(const object::BasicSymbolRef &S) {363  Expected<uint32_t> SymFlagsOrErr = S.getFlags();364  if (!SymFlagsOrErr)365    // TODO: Actually report errors helpfully.366    report_fatal_error(SymFlagsOrErr.takeError());367  if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific)368    return false;369  if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global))370    return false;371  if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined)372    return false;373  return true;374}375 376static void printNBits(raw_ostream &Out, object::Archive::Kind Kind,377                       uint64_t Val) {378  if (is64BitKind(Kind))379    print<uint64_t>(Out, Kind, Val);380  else381    print<uint32_t>(Out, Kind, Val);382}383 384static uint64_t computeSymbolTableSize(object::Archive::Kind Kind,385                                       uint64_t NumSyms, uint64_t OffsetSize,386                                       uint64_t StringTableSize,387                                       uint32_t *Padding = nullptr) {388  assert((OffsetSize == 4 || OffsetSize == 8) && "Unsupported OffsetSize");389  uint64_t Size = OffsetSize; // Number of entries390  if (isBSDLike(Kind))391    Size += NumSyms * OffsetSize * 2; // Table392  else393    Size += NumSyms * OffsetSize; // Table394  if (isBSDLike(Kind))395    Size += OffsetSize; // byte count396  Size += StringTableSize;397  // ld64 expects the members to be 8-byte aligned for 64-bit content and at398  // least 4-byte aligned for 32-bit content.  Opt for the larger encoding399  // uniformly.400  // We do this for all bsd formats because it simplifies aligning members.401  // For the big archive format, the symbol table is the last member, so there402  // is no need to align.403  uint32_t Pad = isAIXBigArchive(Kind)404                     ? 0405                     : offsetToAlignment(Size, Align(isBSDLike(Kind) ? 8 : 2));406 407  Size += Pad;408  if (Padding)409    *Padding = Pad;410  return Size;411}412 413static uint64_t computeSymbolMapSize(uint64_t NumObj, SymMap &SymMap,414                                     uint32_t *Padding = nullptr) {415  uint64_t Size = sizeof(uint32_t) * 2; // Number of symbols and objects entries416  Size += NumObj * sizeof(uint32_t);    // Offset table417 418  for (auto S : SymMap.Map)419    Size += sizeof(uint16_t) + S.first.length() + 1;420 421  uint32_t Pad = offsetToAlignment(Size, Align(2));422  Size += Pad;423  if (Padding)424    *Padding = Pad;425  return Size;426}427 428static uint64_t computeECSymbolsSize(SymMap &SymMap,429                                     uint32_t *Padding = nullptr) {430  uint64_t Size = sizeof(uint32_t); // Number of symbols431 432  for (auto S : SymMap.ECMap)433    Size += sizeof(uint16_t) + S.first.length() + 1;434 435  uint32_t Pad = offsetToAlignment(Size, Align(2));436  Size += Pad;437  if (Padding)438    *Padding = Pad;439  return Size;440}441 442static void writeSymbolTableHeader(raw_ostream &Out, object::Archive::Kind Kind,443                                   bool Deterministic, uint64_t Size,444                                   uint64_t PrevMemberOffset = 0,445                                   uint64_t NextMemberOffset = 0) {446  if (isBSDLike(Kind)) {447    const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF";448    printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0,449                         Size);450  } else if (isAIXBigArchive(Kind)) {451    printBigArchiveMemberHeader(Out, "", now(Deterministic), 0, 0, 0, Size,452                                PrevMemberOffset, NextMemberOffset);453  } else {454    const char *Name = is64BitKind(Kind) ? "/SYM64" : "";455    printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size);456  }457}458 459static uint64_t computeHeadersSize(object::Archive::Kind Kind,460                                   uint64_t NumMembers,461                                   uint64_t StringMemberSize, uint64_t NumSyms,462                                   uint64_t SymNamesSize, SymMap *SymMap) {463  uint32_t OffsetSize = is64BitKind(Kind) ? 8 : 4;464  uint64_t SymtabSize =465      computeSymbolTableSize(Kind, NumSyms, OffsetSize, SymNamesSize);466  auto computeSymbolTableHeaderSize = [=] {467    SmallString<0> TmpBuf;468    raw_svector_ostream Tmp(TmpBuf);469    writeSymbolTableHeader(Tmp, Kind, true, SymtabSize);470    return TmpBuf.size();471  };472  uint32_t HeaderSize = computeSymbolTableHeaderSize();473  uint64_t Size = strlen("!<arch>\n") + HeaderSize + SymtabSize;474 475  if (SymMap) {476    Size += HeaderSize + computeSymbolMapSize(NumMembers, *SymMap);477    if (SymMap->ECMap.size())478      Size += HeaderSize + computeECSymbolsSize(*SymMap);479  }480 481  return Size + StringMemberSize;482}483 484static Expected<std::unique_ptr<SymbolicFile>>485getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context,486                object::Archive::Kind Kind, function_ref<void(Error)> Warn) {487  const file_magic Type = identify_magic(Buf.getBuffer());488  // Don't attempt to read non-symbolic file types.489  if (!object::SymbolicFile::isSymbolicFile(Type, &Context))490    return nullptr;491  if (Type == file_magic::bitcode) {492    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(493        Buf, file_magic::bitcode, &Context);494    // An error reading a bitcode file most likely indicates that the file495    // was created by a compiler from the future. Normally we don't try to496    // implement forwards compatibility for bitcode files, but when creating an497    // archive we can implement best-effort forwards compatibility by treating498    // the file as a blob and not creating symbol index entries for it. lld and499    // mold ignore the archive symbol index, so provided that you use one of500    // these linkers, LTO will work as long as lld or the gold plugin is newer501    // than the compiler. We only ignore errors if the archive format is one502    // that is supported by a linker that is known to ignore the index,503    // otherwise there's no chance of this working so we may as well error out.504    // We print a warning on read failure so that users of linkers that rely on505    // the symbol index can diagnose the issue.506    //507    // This is the same behavior as GNU ar when the linker plugin returns an508    // error when reading the input file. If the bitcode file is actually509    // malformed, it will be diagnosed at link time.510    if (!ObjOrErr) {511      switch (Kind) {512      case object::Archive::K_BSD:513      case object::Archive::K_GNU:514      case object::Archive::K_GNU64:515        Warn(ObjOrErr.takeError());516        return nullptr;517      case object::Archive::K_AIXBIG:518      case object::Archive::K_COFF:519      case object::Archive::K_DARWIN:520      case object::Archive::K_DARWIN64:521        return ObjOrErr.takeError();522      }523    }524    return std::move(*ObjOrErr);525  } else {526    auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);527    if (!ObjOrErr)528      return ObjOrErr.takeError();529    return std::move(*ObjOrErr);530  }531}532 533static bool is64BitSymbolicFile(const SymbolicFile *SymObj) {534  return SymObj != nullptr ? SymObj->is64Bit() : false;535}536 537// Log2 of PAGESIZE(4096) on an AIX system.538static const uint32_t Log2OfAIXPageSize = 12;539 540// In the AIX big archive format, since the data content follows the member file541// name, if the name ends on an odd byte, an extra byte will be added for542// padding. This ensures that the data within the member file starts at an even543// byte.544static const uint32_t MinBigArchiveMemDataAlign = 2;545 546template <typename AuxiliaryHeader>547uint16_t getAuxMaxAlignment(uint16_t AuxHeaderSize, AuxiliaryHeader *AuxHeader,548                            uint16_t Log2OfMaxAlign) {549  // If the member doesn't have an auxiliary header, it isn't a loadable object550  // and so it just needs aligning at the minimum value.551  if (AuxHeader == nullptr)552    return MinBigArchiveMemDataAlign;553 554  // If the auxiliary header does not have both MaxAlignOfData and555  // MaxAlignOfText field, it is not a loadable shared object file, so align at556  // the minimum value. The 'ModuleType' member is located right after557  // 'MaxAlignOfData' in the AuxiliaryHeader.558  if (AuxHeaderSize < offsetof(AuxiliaryHeader, ModuleType))559    return MinBigArchiveMemDataAlign;560 561  // If the XCOFF object file does not have a loader section, it is not562  // loadable, so align at the minimum value.563  if (AuxHeader->SecNumOfLoader == 0)564    return MinBigArchiveMemDataAlign;565 566  // The content of the loadable member file needs to be aligned at MAX(maximum567  // alignment of .text, maximum alignment of .data) if there are both fields.568  // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a569  // word boundary, while 64-bit members are aligned on a PAGESIZE(2^12=4096)570  // boundary.571  uint16_t Log2OfAlign =572      std::max(AuxHeader->MaxAlignOfText, AuxHeader->MaxAlignOfData);573  return 1 << (Log2OfAlign > Log2OfAIXPageSize ? Log2OfMaxAlign : Log2OfAlign);574}575 576// AIX big archives may contain shared object members. The AIX OS requires these577// members to be aligned if they are 64-bit and recommends it for 32-bit578// members. This ensures that when these members are loaded they are aligned in579// memory.580static uint32_t getMemberAlignment(SymbolicFile *SymObj) {581  XCOFFObjectFile *XCOFFObj = dyn_cast_or_null<XCOFFObjectFile>(SymObj);582  if (!XCOFFObj)583    return MinBigArchiveMemDataAlign;584 585  // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a586  // word boundary, while 64-bit members are aligned on a PAGESIZE boundary.587  return XCOFFObj->is64Bit()588             ? getAuxMaxAlignment(XCOFFObj->fileHeader64()->AuxHeaderSize,589                                  XCOFFObj->auxiliaryHeader64(),590                                  Log2OfAIXPageSize)591             : getAuxMaxAlignment(XCOFFObj->fileHeader32()->AuxHeaderSize,592                                  XCOFFObj->auxiliaryHeader32(), 2);593}594 595static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind,596                             bool Deterministic, ArrayRef<MemberData> Members,597                             StringRef StringTable, uint64_t MembersOffset,598                             unsigned NumSyms, uint64_t PrevMemberOffset = 0,599                             uint64_t NextMemberOffset = 0,600                             bool Is64Bit = false) {601  // We don't write a symbol table on an archive with no members -- except on602  // Darwin, where the linker will abort unless the archive has a symbol table.603  if (StringTable.empty() && !isDarwin(Kind) && !isCOFFArchive(Kind))604    return;605 606  uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4;607  uint32_t Pad;608  uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize,609                                         StringTable.size(), &Pad);610  writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset,611                         NextMemberOffset);612 613  if (isBSDLike(Kind))614    printNBits(Out, Kind, NumSyms * 2 * OffsetSize);615  else616    printNBits(Out, Kind, NumSyms);617 618  uint64_t Pos = MembersOffset;619  for (const MemberData &M : Members) {620    if (isAIXBigArchive(Kind)) {621      Pos += M.PreHeadPadSize;622      if (is64BitSymbolicFile(M.SymFile.get()) != Is64Bit) {623        Pos += M.Header.size() + M.Data.size() + M.Padding.size();624        continue;625      }626    }627 628    for (unsigned StringOffset : M.Symbols) {629      if (isBSDLike(Kind))630        printNBits(Out, Kind, StringOffset);631      printNBits(Out, Kind, Pos); // member offset632    }633    Pos += M.Header.size() + M.Data.size() + M.Padding.size();634  }635 636  if (isBSDLike(Kind))637    // byte count of the string table638    printNBits(Out, Kind, StringTable.size());639  Out << StringTable;640 641  while (Pad--)642    Out.write(uint8_t(0));643}644 645static void writeSymbolMap(raw_ostream &Out, object::Archive::Kind Kind,646                           bool Deterministic, ArrayRef<MemberData> Members,647                           SymMap &SymMap, uint64_t MembersOffset) {648  uint32_t Pad;649  uint64_t Size = computeSymbolMapSize(Members.size(), SymMap, &Pad);650  writeSymbolTableHeader(Out, Kind, Deterministic, Size, 0);651 652  uint32_t Pos = MembersOffset;653 654  printLE<uint32_t>(Out, Members.size());655  for (const MemberData &M : Members) {656    printLE(Out, Pos); // member offset657    Pos += M.Header.size() + M.Data.size() + M.Padding.size();658  }659 660  printLE<uint32_t>(Out, SymMap.Map.size());661 662  for (auto S : SymMap.Map)663    printLE(Out, S.second);664  for (auto S : SymMap.Map)665    Out << S.first << '\0';666 667  while (Pad--)668    Out.write(uint8_t(0));669}670 671static void writeECSymbols(raw_ostream &Out, object::Archive::Kind Kind,672                           bool Deterministic, ArrayRef<MemberData> Members,673                           SymMap &SymMap) {674  uint32_t Pad;675  uint64_t Size = computeECSymbolsSize(SymMap, &Pad);676  printGNUSmallMemberHeader(Out, "/<ECSYMBOLS>", now(Deterministic), 0, 0, 0,677                            Size);678 679  printLE<uint32_t>(Out, SymMap.ECMap.size());680 681  for (auto S : SymMap.ECMap)682    printLE(Out, S.second);683  for (auto S : SymMap.ECMap)684    Out << S.first << '\0';685  while (Pad--)686    Out.write(uint8_t(0));687}688 689static bool isECObject(object::SymbolicFile &Obj) {690  if (Obj.isCOFF())691    return cast<llvm::object::COFFObjectFile>(&Obj)->getMachine() !=692           COFF::IMAGE_FILE_MACHINE_ARM64;693 694  if (Obj.isCOFFImportFile())695    return cast<llvm::object::COFFImportFile>(&Obj)->getMachine() !=696           COFF::IMAGE_FILE_MACHINE_ARM64;697 698  if (Obj.isIR()) {699    Expected<std::string> TripleStr =700        getBitcodeTargetTriple(Obj.getMemoryBufferRef());701    if (!TripleStr)702      return false;703    Triple T(std::move(*TripleStr));704    return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64;705  }706 707  return false;708}709 710static bool isAnyArm64COFF(object::SymbolicFile &Obj) {711  if (Obj.isCOFF())712    return COFF::isAnyArm64(cast<COFFObjectFile>(&Obj)->getMachine());713 714  if (Obj.isCOFFImportFile())715    return COFF::isAnyArm64(cast<COFFImportFile>(&Obj)->getMachine());716 717  if (Obj.isIR()) {718    Expected<std::string> TripleStr =719        getBitcodeTargetTriple(Obj.getMemoryBufferRef());720    if (!TripleStr)721      return false;722    Triple T(std::move(*TripleStr));723    return T.isOSWindows() && T.getArch() == Triple::aarch64;724  }725 726  return false;727}728 729bool isImportDescriptor(StringRef Name) {730  return Name.starts_with(ImportDescriptorPrefix) ||731         Name == StringRef{NullImportDescriptorSymbolName} ||732         (Name.starts_with(NullThunkDataPrefix) &&733          Name.ends_with(NullThunkDataSuffix));734}735 736static Expected<std::vector<unsigned>> getSymbols(SymbolicFile *Obj,737                                                  uint16_t Index,738                                                  raw_ostream &SymNames,739                                                  SymMap *SymMap) {740  std::vector<unsigned> Ret;741 742  if (Obj == nullptr)743    return Ret;744 745  std::map<std::string, uint16_t> *Map = nullptr;746  if (SymMap)747    Map = SymMap->UseECMap && isECObject(*Obj) ? &SymMap->ECMap : &SymMap->Map;748 749  for (const object::BasicSymbolRef &S : Obj->symbols()) {750    if (!isArchiveSymbol(S))751      continue;752    if (Map) {753      std::string Name;754      raw_string_ostream NameStream(Name);755      if (Error E = S.printName(NameStream))756        return std::move(E);757      if (!Map->try_emplace(Name, Index).second)758        continue; // ignore duplicated symbol759      if (Map == &SymMap->Map) {760        Ret.push_back(SymNames.tell());761        SymNames << Name << '\0';762        // If EC is enabled, then the import descriptors are NOT put into EC763        // objects so we need to copy them to the EC map manually.764        if (SymMap->UseECMap && isImportDescriptor(Name))765          SymMap->ECMap[Name] = Index;766      }767    } else {768      Ret.push_back(SymNames.tell());769      if (Error E = S.printName(SymNames))770        return std::move(E);771      SymNames << '\0';772    }773  }774  return Ret;775}776 777static Expected<std::vector<MemberData>>778computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,779                  object::Archive::Kind Kind, bool Thin, bool Deterministic,780                  SymtabWritingMode NeedSymbols, SymMap *SymMap,781                  LLVMContext &Context, ArrayRef<NewArchiveMember> NewMembers,782                  std::optional<bool> IsEC, function_ref<void(Error)> Warn) {783  static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};784  uint64_t MemHeadPadSize = 0;785  uint64_t Pos =786      isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0;787 788  std::vector<MemberData> Ret;789  bool HasObject = false;790 791  // Deduplicate long member names in the string table and reuse earlier name792  // offsets. This especially saves space for COFF Import libraries where all793  // members have the same name.794  StringMap<uint64_t> MemberNames;795 796  // UniqueTimestamps is a special case to improve debugging on Darwin:797  //798  // The Darwin linker does not link debug info into the final799  // binary. Instead, it emits entries of type N_OSO in the output800  // binary's symbol table, containing references to the linked-in801  // object files. Using that reference, the debugger can read the802  // debug data directly from the object files. Alternatively, an803  // invocation of 'dsymutil' will link the debug data from the object804  // files into a dSYM bundle, which can be loaded by the debugger,805  // instead of the object files.806  //807  // For an object file, the N_OSO entries contain the absolute path808  // path to the file, and the file's timestamp. For an object809  // included in an archive, the path is formatted like810  // "/absolute/path/to/archive.a(member.o)", and the timestamp is the811  // archive member's timestamp, rather than the archive's timestamp.812  //813  // However, this doesn't always uniquely identify an object within814  // an archive -- an archive file can have multiple entries with the815  // same filename. (This will happen commonly if the original object816  // files started in different directories.) The only way they get817  // distinguished, then, is via the timestamp. But this process is818  // unable to find the correct object file in the archive when there819  // are two files of the same name and timestamp.820  //821  // Additionally, timestamp==0 is treated specially, and causes the822  // timestamp to be ignored as a match criteria.823  //824  // That will "usually" work out okay when creating an archive not in825  // deterministic timestamp mode, because the objects will probably826  // have been created at different timestamps.827  //828  // To ameliorate this problem, in deterministic archive mode (which829  // is the default), on Darwin we will emit a unique non-zero830  // timestamp for each entry with a duplicated name. This is still831  // deterministic: the only thing affecting that timestamp is the832  // order of the files in the resultant archive.833  //834  // See also the functions that handle the lookup:835  // in lldb: ObjectContainerBSDArchive::Archive::FindObject()836  // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers().837  bool UniqueTimestamps = Deterministic && isDarwin(Kind);838  std::map<StringRef, unsigned> FilenameCount;839  if (UniqueTimestamps) {840    for (const NewArchiveMember &M : NewMembers)841      FilenameCount[M.MemberName]++;842    for (auto &Entry : FilenameCount)843      Entry.second = Entry.second > 1 ? 1 : 0;844  }845 846  std::vector<std::unique_ptr<SymbolicFile>> SymFiles;847 848  if (NeedSymbols != SymtabWritingMode::NoSymtab || isAIXBigArchive(Kind)) {849    for (const NewArchiveMember &M : NewMembers) {850      Expected<std::unique_ptr<SymbolicFile>> SymFileOrErr = getSymbolicFile(851          M.Buf->getMemBufferRef(), Context, Kind, [&](Error Err) {852            Warn(createFileError(M.MemberName, std::move(Err)));853          });854      if (!SymFileOrErr)855        return createFileError(M.MemberName, SymFileOrErr.takeError());856      SymFiles.push_back(std::move(*SymFileOrErr));857    }858  }859 860  if (SymMap) {861    if (IsEC) {862      SymMap->UseECMap = *IsEC;863    } else {864      // When IsEC is not specified by the caller, use it when we have both865      // any ARM64 object (ARM64 or ARM64EC) and any EC object (ARM64EC or866      // AMD64). This may be a single ARM64EC object, but may also be separate867      // ARM64 and AMD64 objects.868      bool HaveArm64 = false, HaveEC = false;869      for (std::unique_ptr<SymbolicFile> &SymFile : SymFiles) {870        if (!SymFile)871          continue;872        if (!HaveArm64)873          HaveArm64 = isAnyArm64COFF(*SymFile);874        if (!HaveEC)875          HaveEC = isECObject(*SymFile);876        if (HaveArm64 && HaveEC) {877          SymMap->UseECMap = true;878          break;879        }880      }881    }882  }883 884  // The big archive format needs to know the offset of the previous member885  // header.886  uint64_t PrevOffset = 0;887  uint64_t NextMemHeadPadSize = 0;888 889  for (uint32_t Index = 0; Index < NewMembers.size(); ++Index) {890    const NewArchiveMember *M = &NewMembers[Index];891    std::string Header;892    raw_string_ostream Out(Header);893 894    MemoryBufferRef Buf = M->Buf->getMemBufferRef();895    StringRef Data = Thin ? "" : Buf.getBuffer();896 897    // ld64 expects the members to be 8-byte aligned for 64-bit content and at898    // least 4-byte aligned for 32-bit content.  Opt for the larger encoding899    // uniformly.  This matches the behaviour with cctools and ensures that ld64900    // is happy with archives that we generate.901    unsigned MemberPadding =902        isDarwin(Kind) ? offsetToAlignment(Data.size(), Align(8)) : 0;903    unsigned TailPadding =904        offsetToAlignment(Data.size() + MemberPadding, Align(2));905    StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding);906 907    sys::TimePoint<std::chrono::seconds> ModTime;908    if (UniqueTimestamps)909      // Increment timestamp for each file of a given name.910      ModTime = sys::toTimePoint(FilenameCount[M->MemberName]++);911    else912      ModTime = M->ModTime;913 914    uint64_t Size = Buf.getBufferSize() + MemberPadding;915    if (Size > object::Archive::MaxMemberSize) {916      std::string StringMsg =917          "File " + M->MemberName.str() + " exceeds size limit";918      return make_error<object::GenericBinaryError>(919          std::move(StringMsg), object::object_error::parse_failed);920    }921 922    std::unique_ptr<SymbolicFile> CurSymFile;923    if (!SymFiles.empty())924      CurSymFile = std::move(SymFiles[Index]);925 926    // In the big archive file format, we need to calculate and include the next927    // member offset and previous member offset in the file member header.928    if (isAIXBigArchive(Kind)) {929      uint64_t OffsetToMemData = Pos + sizeof(object::BigArMemHdrType) +930                                 alignTo(M->MemberName.size(), 2);931 932      if (M == NewMembers.begin())933        NextMemHeadPadSize =934            alignToPowerOf2(OffsetToMemData,935                            getMemberAlignment(CurSymFile.get())) -936            OffsetToMemData;937 938      MemHeadPadSize = NextMemHeadPadSize;939      Pos += MemHeadPadSize;940      uint64_t NextOffset = Pos + sizeof(object::BigArMemHdrType) +941                            alignTo(M->MemberName.size(), 2) + alignTo(Size, 2);942 943      // If there is another member file after this, we need to calculate the944      // padding before the header.945      if (Index + 1 != SymFiles.size()) {946        uint64_t OffsetToNextMemData =947            NextOffset + sizeof(object::BigArMemHdrType) +948            alignTo(NewMembers[Index + 1].MemberName.size(), 2);949        NextMemHeadPadSize =950            alignToPowerOf2(OffsetToNextMemData,951                            getMemberAlignment(SymFiles[Index + 1].get())) -952            OffsetToNextMemData;953        NextOffset += NextMemHeadPadSize;954      }955      printBigArchiveMemberHeader(Out, M->MemberName, ModTime, M->UID, M->GID,956                                  M->Perms, Size, PrevOffset, NextOffset);957      PrevOffset = Pos;958    } else {959      printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, *M,960                        ModTime, Size);961    }962    Out.flush();963 964    std::vector<unsigned> Symbols;965    if (NeedSymbols != SymtabWritingMode::NoSymtab) {966      Expected<std::vector<unsigned>> SymbolsOrErr =967          getSymbols(CurSymFile.get(), Index + 1, SymNames, SymMap);968      if (!SymbolsOrErr)969        return createFileError(M->MemberName, SymbolsOrErr.takeError());970      Symbols = std::move(*SymbolsOrErr);971      if (CurSymFile)972        HasObject = true;973    }974 975    Pos += Header.size() + Data.size() + Padding.size();976    Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding,977                   MemHeadPadSize, std::move(CurSymFile)});978  }979  // If there are no symbols, emit an empty symbol table, to satisfy Solaris980  // tools, older versions of which expect a symbol table in a non-empty981  // archive, regardless of whether there are any symbols in it.982  if (HasObject && SymNames.tell() == 0 && !isCOFFArchive(Kind))983    SymNames << '\0' << '\0' << '\0';984  return std::move(Ret);985}986 987namespace llvm {988 989static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) {990  SmallString<128> Ret = P;991  std::error_code Err = sys::fs::make_absolute(Ret);992  if (Err)993    return Err;994  sys::path::remove_dots(Ret, /*removedotdot*/ true);995  return Ret;996}997 998// Compute the relative path from From to To.999Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {1000  ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(To);1001  ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(From);1002  if (!PathToOrErr || !DirFromOrErr)1003    return errorCodeToError(errnoAsErrorCode());1004 1005  const SmallString<128> &PathTo = *PathToOrErr;1006  const SmallString<128> &DirFrom = sys::path::parent_path(*DirFromOrErr);1007 1008  // Can't construct a relative path between different roots1009  if (sys::path::root_name(PathTo) != sys::path::root_name(DirFrom))1010    return sys::path::convert_to_slash(PathTo);1011 1012  // Skip common prefixes1013  auto FromTo =1014      std::mismatch(sys::path::begin(DirFrom), sys::path::end(DirFrom),1015                    sys::path::begin(PathTo));1016  auto FromI = FromTo.first;1017  auto ToI = FromTo.second;1018 1019  // Construct relative path1020  SmallString<128> Relative;1021  for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)1022    sys::path::append(Relative, sys::path::Style::posix, "..");1023 1024  for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI)1025    sys::path::append(Relative, sys::path::Style::posix, *ToI);1026 1027  return std::string(Relative);1028}1029 1030Error writeArchiveToStream(raw_ostream &Out,1031                           ArrayRef<NewArchiveMember> NewMembers,1032                           SymtabWritingMode WriteSymtab,1033                           object::Archive::Kind Kind, bool Deterministic,1034                           bool Thin, std::optional<bool> IsEC,1035                           function_ref<void(Error)> Warn) {1036  assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");1037 1038  SmallString<0> SymNamesBuf;1039  raw_svector_ostream SymNames(SymNamesBuf);1040  SmallString<0> StringTableBuf;1041  raw_svector_ostream StringTable(StringTableBuf);1042  SymMap SymMap;1043  bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab;1044 1045  // COFF symbol map uses 16-bit indexes, so we can't use it if there are too1046  // many members. COFF format also requires symbol table presence, so use1047  // GNU format when NoSymtab is requested.1048  if (isCOFFArchive(Kind) && (NewMembers.size() > 0xfffe || !ShouldWriteSymtab))1049    Kind = object::Archive::K_GNU;1050 1051  // In the scenario when LLVMContext is populated SymbolicFile will contain a1052  // reference to it, thus SymbolicFile should be destroyed first.1053  LLVMContext Context;1054 1055  Expected<std::vector<MemberData>> DataOrErr = computeMemberData(1056      StringTable, SymNames, Kind, Thin, Deterministic, WriteSymtab,1057      isCOFFArchive(Kind) ? &SymMap : nullptr, Context, NewMembers, IsEC, Warn);1058  if (Error E = DataOrErr.takeError())1059    return E;1060  std::vector<MemberData> &Data = *DataOrErr;1061 1062  uint64_t StringTableSize = 0;1063  MemberData StringTableMember;1064  if (!StringTableBuf.empty() && !isAIXBigArchive(Kind)) {1065    StringTableMember = computeStringTable(StringTableBuf);1066    StringTableSize = StringTableMember.Header.size() +1067                      StringTableMember.Data.size() +1068                      StringTableMember.Padding.size();1069  }1070 1071  // We would like to detect if we need to switch to a 64-bit symbol table.1072  uint64_t LastMemberEndOffset = 0;1073  uint64_t LastMemberHeaderOffset = 0;1074  uint64_t NumSyms = 0;1075  uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files.1076 1077  for (const auto &M : Data) {1078    // Record the start of the member's offset1079    LastMemberEndOffset += M.PreHeadPadSize;1080    LastMemberHeaderOffset = LastMemberEndOffset;1081    // Account for the size of each part associated with the member.1082    LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size();1083    NumSyms += M.Symbols.size();1084 1085    // AIX big archive files may contain two global symbol tables. The1086    // first global symbol table locates 32-bit file members that define global1087    // symbols; the second global symbol table does the same for 64-bit file1088    // members. As a big archive can have both 32-bit and 64-bit file members,1089    // we need to know the number of symbols in each symbol table individually.1090    if (isAIXBigArchive(Kind) && ShouldWriteSymtab) {1091        if (!is64BitSymbolicFile(M.SymFile.get()))1092          NumSyms32 += M.Symbols.size();1093      }1094  }1095 1096  std::optional<uint64_t> HeadersSize;1097 1098  // The symbol table is put at the end of the big archive file. The symbol1099  // table is at the start of the archive file for other archive formats.1100  if (ShouldWriteSymtab && !is64BitKind(Kind)) {1101    // We assume 32-bit offsets to see if 32-bit symbols are possible or not.1102    HeadersSize = computeHeadersSize(Kind, Data.size(), StringTableSize,1103                                     NumSyms, SymNamesBuf.size(),1104                                     isCOFFArchive(Kind) ? &SymMap : nullptr);1105 1106    // The SYM64 format is used when an archive's member offsets are larger than1107    // 32-bits can hold. The need for this shift in format is detected by1108    // writeArchive. To test this we need to generate a file with a member that1109    // has an offset larger than 32-bits but this demands a very slow test. To1110    // speed the test up we use this environment variable to pretend like the1111    // cutoff happens before 32-bits and instead happens at some much smaller1112    // value.1113    uint64_t Sym64Threshold = 1ULL << 32;1114    const char *Sym64Env = std::getenv("SYM64_THRESHOLD");1115    if (Sym64Env)1116      StringRef(Sym64Env).getAsInteger(10, Sym64Threshold);1117 1118    // If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need1119    // to switch to 64-bit. Note that the file can be larger than 4GB as long as1120    // the last member starts before the 4GB offset.1121    if (*HeadersSize + LastMemberHeaderOffset >= Sym64Threshold) {1122      switch (Kind) {1123      case object::Archive::K_COFF:1124        // COFF format has no 64-bit version, so we use GNU64 instead.1125        if (!SymMap.Map.empty() && !SymMap.ECMap.empty())1126          // Only the COFF format supports the ECSYMBOLS section, so don’t use1127          // GNU64 when two symbol maps are required.1128          return make_error<object::GenericBinaryError>(1129              "Archive is too large: ARM64X does not support archives larger "1130              "than 4GB");1131        // Since this changes the headers, we need to recalculate everything.1132        return writeArchiveToStream(Out, NewMembers, WriteSymtab,1133                                    object::Archive::K_GNU64, Deterministic,1134                                    Thin, IsEC, Warn);1135      case object::Archive::K_DARWIN:1136        Kind = object::Archive::K_DARWIN64;1137        break;1138      default:1139        Kind = object::Archive::K_GNU64;1140        break;1141      }1142      HeadersSize.reset();1143    }1144  }1145 1146  if (Thin)1147    Out << "!<thin>\n";1148  else if (isAIXBigArchive(Kind))1149    Out << "<bigaf>\n";1150  else1151    Out << "!<arch>\n";1152 1153  if (!isAIXBigArchive(Kind)) {1154    if (ShouldWriteSymtab) {1155      if (!HeadersSize)1156        HeadersSize = computeHeadersSize(1157            Kind, Data.size(), StringTableSize, NumSyms, SymNamesBuf.size(),1158            isCOFFArchive(Kind) ? &SymMap : nullptr);1159      writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf,1160                       *HeadersSize, NumSyms);1161 1162      if (isCOFFArchive(Kind))1163        writeSymbolMap(Out, Kind, Deterministic, Data, SymMap, *HeadersSize);1164    }1165 1166    if (StringTableSize)1167      Out << StringTableMember.Header << StringTableMember.Data1168          << StringTableMember.Padding;1169 1170    if (ShouldWriteSymtab && SymMap.ECMap.size())1171      writeECSymbols(Out, Kind, Deterministic, Data, SymMap);1172 1173    for (const MemberData &M : Data)1174      Out << M.Header << M.Data << M.Padding;1175  } else {1176    HeadersSize = sizeof(object::BigArchive::FixLenHdr);1177    LastMemberEndOffset += *HeadersSize;1178    LastMemberHeaderOffset += *HeadersSize;1179 1180    // For the big archive (AIX) format, compute a table of member names and1181    // offsets, used in the member table.1182    uint64_t MemberTableNameStrTblSize = 0;1183    std::vector<size_t> MemberOffsets;1184    std::vector<StringRef> MemberNames;1185    // Loop across object to find offset and names.1186    uint64_t MemberEndOffset = sizeof(object::BigArchive::FixLenHdr);1187    for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) {1188      const NewArchiveMember &Member = NewMembers[I];1189      MemberTableNameStrTblSize += Member.MemberName.size() + 1;1190      MemberEndOffset += Data[I].PreHeadPadSize;1191      MemberOffsets.push_back(MemberEndOffset);1192      MemberNames.push_back(Member.MemberName);1193      // File member name ended with "`\n". The length is included in1194      // BigArMemHdrType.1195      MemberEndOffset += sizeof(object::BigArMemHdrType) +1196                         alignTo(Data[I].Data.size(), 2) +1197                         alignTo(Member.MemberName.size(), 2);1198    }1199 1200    // AIX member table size.1201    uint64_t MemberTableSize = 20 + // Number of members field1202                               20 * MemberOffsets.size() +1203                               MemberTableNameStrTblSize;1204 1205    SmallString<0> SymNamesBuf32;1206    SmallString<0> SymNamesBuf64;1207    raw_svector_ostream SymNames32(SymNamesBuf32);1208    raw_svector_ostream SymNames64(SymNamesBuf64);1209 1210    if (ShouldWriteSymtab && NumSyms)1211      // Generate the symbol names for the members.1212      for (const auto &M : Data) {1213        Expected<std::vector<unsigned>> SymbolsOrErr = getSymbols(1214            M.SymFile.get(), 0,1215            is64BitSymbolicFile(M.SymFile.get()) ? SymNames64 : SymNames32,1216            nullptr);1217        if (!SymbolsOrErr)1218          return SymbolsOrErr.takeError();1219      }1220 1221    uint64_t MemberTableEndOffset =1222        LastMemberEndOffset +1223        alignTo(sizeof(object::BigArMemHdrType) + MemberTableSize, 2);1224 1225    // In AIX OS, The 'GlobSymOffset' field in the fixed-length header contains1226    // the offset to the 32-bit global symbol table, and the 'GlobSym64Offset'1227    // contains the offset to the 64-bit global symbol table.1228    uint64_t GlobalSymbolOffset =1229        (ShouldWriteSymtab &&1230         (WriteSymtab != SymtabWritingMode::BigArchive64) && NumSyms32 > 0)1231            ? MemberTableEndOffset1232            : 0;1233 1234    uint64_t GlobalSymbolOffset64 = 0;1235    uint64_t NumSyms64 = NumSyms - NumSyms32;1236    if (ShouldWriteSymtab && (WriteSymtab != SymtabWritingMode::BigArchive32) &&1237        NumSyms64 > 0) {1238      if (GlobalSymbolOffset == 0)1239        GlobalSymbolOffset64 = MemberTableEndOffset;1240      else1241        // If there is a global symbol table for 32-bit members,1242        // the 64-bit global symbol table is after the 32-bit one.1243        GlobalSymbolOffset64 =1244            GlobalSymbolOffset + sizeof(object::BigArMemHdrType) +1245            (NumSyms32 + 1) * 8 + alignTo(SymNamesBuf32.size(), 2);1246    }1247 1248    // Fixed Sized Header.1249    printWithSpacePadding(Out, NewMembers.size() ? LastMemberEndOffset : 0,1250                          20); // Offset to member table1251    // If there are no file members in the archive, there will be no global1252    // symbol table.1253    printWithSpacePadding(Out, GlobalSymbolOffset, 20);1254    printWithSpacePadding(Out, GlobalSymbolOffset64, 20);1255    printWithSpacePadding(Out,1256                          NewMembers.size()1257                              ? sizeof(object::BigArchive::FixLenHdr) +1258                                    Data[0].PreHeadPadSize1259                              : 0,1260                          20); // Offset to first archive member1261    printWithSpacePadding(Out, NewMembers.size() ? LastMemberHeaderOffset : 0,1262                          20); // Offset to last archive member1263    printWithSpacePadding(1264        Out, 0,1265        20); // Offset to first member of free list - Not supported yet1266 1267    for (const MemberData &M : Data) {1268      Out << std::string(M.PreHeadPadSize, '\0');1269      Out << M.Header << M.Data;1270      if (M.Data.size() % 2)1271        Out << '\0';1272    }1273 1274    if (NewMembers.size()) {1275      // Member table.1276      printBigArchiveMemberHeader(Out, "", sys::toTimePoint(0), 0, 0, 0,1277                                  MemberTableSize, LastMemberHeaderOffset,1278                                  GlobalSymbolOffset ? GlobalSymbolOffset1279                                                     : GlobalSymbolOffset64);1280      printWithSpacePadding(Out, MemberOffsets.size(), 20); // Number of members1281      for (uint64_t MemberOffset : MemberOffsets)1282        printWithSpacePadding(Out, MemberOffset,1283                              20); // Offset to member file header.1284      for (StringRef MemberName : MemberNames)1285        Out << MemberName << '\0'; // Member file name, null byte padding.1286 1287      if (MemberTableNameStrTblSize % 2)1288        Out << '\0'; // Name table must be tail padded to an even number of1289                     // bytes.1290 1291      if (ShouldWriteSymtab) {1292        // Write global symbol table for 32-bit file members.1293        if (GlobalSymbolOffset) {1294          writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf32,1295                           *HeadersSize, NumSyms32, LastMemberEndOffset,1296                           GlobalSymbolOffset64);1297          // Add padding between the symbol tables, if needed.1298          if (GlobalSymbolOffset64 && (SymNamesBuf32.size() % 2))1299            Out << '\0';1300        }1301 1302        // Write global symbol table for 64-bit file members.1303        if (GlobalSymbolOffset64)1304          writeSymbolTable(Out, Kind, Deterministic, Data, SymNamesBuf64,1305                           *HeadersSize, NumSyms64,1306                           GlobalSymbolOffset ? GlobalSymbolOffset1307                                              : LastMemberEndOffset,1308                           0, true);1309      }1310    }1311  }1312  Out.flush();1313  return Error::success();1314}1315 1316void warnToStderr(Error Err) {1317  llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "warning: ");1318}1319 1320Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,1321                   SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,1322                   bool Deterministic, bool Thin,1323                   std::unique_ptr<MemoryBuffer> OldArchiveBuf,1324                   std::optional<bool> IsEC, function_ref<void(Error)> Warn) {1325  Expected<sys::fs::TempFile> Temp =1326      sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");1327  if (!Temp)1328    return Temp.takeError();1329  raw_fd_ostream Out(Temp->FD, false);1330 1331  if (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind,1332                                     Deterministic, Thin, IsEC, Warn)) {1333    if (Error DiscardError = Temp->discard())1334      return joinErrors(std::move(E), std::move(DiscardError));1335    return E;1336  }1337 1338  // At this point, we no longer need whatever backing memory1339  // was used to generate the NewMembers. On Windows, this buffer1340  // could be a mapped view of the file we want to replace (if1341  // we're updating an existing archive, say). In that case, the1342  // rename would still succeed, but it would leave behind a1343  // temporary file (actually the original file renamed) because1344  // a file cannot be deleted while there's a handle open on it,1345  // only renamed. So by freeing this buffer, this ensures that1346  // the last open handle on the destination file, if any, is1347  // closed before we attempt to rename.1348  OldArchiveBuf.reset();1349 1350  return Temp->keep(ArcName);1351}1352 1353Expected<std::unique_ptr<MemoryBuffer>>1354writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers,1355                     SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,1356                     bool Deterministic, bool Thin,1357                     function_ref<void(Error)> Warn) {1358  SmallVector<char, 0> ArchiveBufferVector;1359  raw_svector_ostream ArchiveStream(ArchiveBufferVector);1360 1361  if (Error E =1362          writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab, Kind,1363                               Deterministic, Thin, std::nullopt, Warn))1364    return std::move(E);1365 1366  return std::make_unique<SmallVectorMemoryBuffer>(1367      std::move(ArchiveBufferVector), /*RequiresNullTerminator=*/false);1368}1369 1370} // namespace llvm1371