ArchiveWriter.cpp revision 321369
1//===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines the writeArchive function.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Object/ArchiveWriter.h"
15#include "llvm/ADT/ArrayRef.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/ObjectFile.h"
21#include "llvm/Object/SymbolicFile.h"
22#include "llvm/Support/EndianStream.h"
23#include "llvm/Support/Errc.h"
24#include "llvm/Support/ErrorHandling.h"
25#include "llvm/Support/Format.h"
26#include "llvm/Support/Path.h"
27#include "llvm/Support/ToolOutputFile.h"
28#include "llvm/Support/raw_ostream.h"
29
30#if !defined(_MSC_VER) && !defined(__MINGW32__)
31#include <unistd.h>
32#else
33#include <io.h>
34#endif
35
36using namespace llvm;
37
38NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef)
39    : Buf(MemoryBuffer::getMemBuffer(BufRef, false)),
40      MemberName(BufRef.getBufferIdentifier()) {}
41
42Expected<NewArchiveMember>
43NewArchiveMember::getOldMember(const object::Archive::Child &OldMember,
44                               bool Deterministic) {
45  Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef();
46  if (!BufOrErr)
47    return BufOrErr.takeError();
48
49  NewArchiveMember M;
50  assert(M.IsNew == false);
51  M.Buf = MemoryBuffer::getMemBuffer(*BufOrErr, false);
52  M.MemberName = M.Buf->getBufferIdentifier();
53  if (!Deterministic) {
54    auto ModTimeOrErr = OldMember.getLastModified();
55    if (!ModTimeOrErr)
56      return ModTimeOrErr.takeError();
57    M.ModTime = ModTimeOrErr.get();
58    Expected<unsigned> UIDOrErr = OldMember.getUID();
59    if (!UIDOrErr)
60      return UIDOrErr.takeError();
61    M.UID = UIDOrErr.get();
62    Expected<unsigned> GIDOrErr = OldMember.getGID();
63    if (!GIDOrErr)
64      return GIDOrErr.takeError();
65    M.GID = GIDOrErr.get();
66    Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode();
67    if (!AccessModeOrErr)
68      return AccessModeOrErr.takeError();
69    M.Perms = AccessModeOrErr.get();
70  }
71  return std::move(M);
72}
73
74Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName,
75                                                     bool Deterministic) {
76  sys::fs::file_status Status;
77  int FD;
78  if (auto EC = sys::fs::openFileForRead(FileName, FD))
79    return errorCodeToError(EC);
80  assert(FD != -1);
81
82  if (auto EC = sys::fs::status(FD, Status))
83    return errorCodeToError(EC);
84
85  // Opening a directory doesn't make sense. Let it fail.
86  // Linux cannot open directories with open(2), although
87  // cygwin and *bsd can.
88  if (Status.type() == sys::fs::file_type::directory_file)
89    return errorCodeToError(make_error_code(errc::is_a_directory));
90
91  ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
92      MemoryBuffer::getOpenFile(FD, FileName, Status.getSize(), false);
93  if (!MemberBufferOrErr)
94    return errorCodeToError(MemberBufferOrErr.getError());
95
96  if (close(FD) != 0)
97    return errorCodeToError(std::error_code(errno, std::generic_category()));
98
99  NewArchiveMember M;
100  M.IsNew = true;
101  M.Buf = std::move(*MemberBufferOrErr);
102  M.MemberName = M.Buf->getBufferIdentifier();
103  if (!Deterministic) {
104    M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>(
105        Status.getLastModificationTime());
106    M.UID = Status.getUser();
107    M.GID = Status.getGroup();
108    M.Perms = Status.permissions();
109  }
110  return std::move(M);
111}
112
113template <typename T>
114static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size,
115                                  bool MayTruncate = false) {
116  uint64_t OldPos = OS.tell();
117  OS << Data;
118  unsigned SizeSoFar = OS.tell() - OldPos;
119  if (Size > SizeSoFar) {
120    OS.indent(Size - SizeSoFar);
121  } else if (Size < SizeSoFar) {
122    assert(MayTruncate && "Data doesn't fit in Size");
123    // Some of the data this is used for (like UID) can be larger than the
124    // space available in the archive format. Truncate in that case.
125    OS.seek(OldPos + Size);
126  }
127}
128
129static bool isBSDLike(object::Archive::Kind Kind) {
130  switch (Kind) {
131  case object::Archive::K_GNU:
132    return false;
133  case object::Archive::K_BSD:
134  case object::Archive::K_DARWIN:
135    return true;
136  case object::Archive::K_MIPS64:
137  case object::Archive::K_DARWIN64:
138  case object::Archive::K_COFF:
139    break;
140  }
141  llvm_unreachable("not supported for writting");
142}
143
144static void print32(raw_ostream &Out, object::Archive::Kind Kind,
145                    uint32_t Val) {
146  if (isBSDLike(Kind))
147    support::endian::Writer<support::little>(Out).write(Val);
148  else
149    support::endian::Writer<support::big>(Out).write(Val);
150}
151
152static void printRestOfMemberHeader(
153    raw_fd_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime,
154    unsigned UID, unsigned GID, unsigned Perms, unsigned Size) {
155  printWithSpacePadding(Out, sys::toTimeT(ModTime), 12);
156  printWithSpacePadding(Out, UID, 6, true);
157  printWithSpacePadding(Out, GID, 6, true);
158  printWithSpacePadding(Out, format("%o", Perms), 8);
159  printWithSpacePadding(Out, Size, 10);
160  Out << "`\n";
161}
162
163static void
164printGNUSmallMemberHeader(raw_fd_ostream &Out, StringRef Name,
165                          const sys::TimePoint<std::chrono::seconds> &ModTime,
166                          unsigned UID, unsigned GID, unsigned Perms,
167                          unsigned Size) {
168  printWithSpacePadding(Out, Twine(Name) + "/", 16);
169  printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
170}
171
172static void
173printBSDMemberHeader(raw_fd_ostream &Out, StringRef Name,
174                     const sys::TimePoint<std::chrono::seconds> &ModTime,
175                     unsigned UID, unsigned GID, unsigned Perms,
176                     unsigned Size) {
177  uint64_t PosAfterHeader = Out.tell() + 60 + Name.size();
178  // Pad so that even 64 bit object files are aligned.
179  unsigned Pad = OffsetToAlignment(PosAfterHeader, 8);
180  unsigned NameWithPadding = Name.size() + Pad;
181  printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
182  printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
183                          NameWithPadding + Size);
184  Out << Name;
185  assert(PosAfterHeader == Out.tell());
186  while (Pad--)
187    Out.write(uint8_t(0));
188}
189
190static bool useStringTable(bool Thin, StringRef Name) {
191  return Thin || Name.size() >= 16 || Name.contains('/');
192}
193
194static void
195printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin,
196                  StringRef Name,
197                  std::vector<unsigned>::iterator &StringMapIndexIter,
198                  const sys::TimePoint<std::chrono::seconds> &ModTime,
199                  unsigned UID, unsigned GID, unsigned Perms, unsigned Size) {
200  if (isBSDLike(Kind))
201    return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
202  if (!useStringTable(Thin, Name))
203    return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
204  Out << '/';
205  printWithSpacePadding(Out, *StringMapIndexIter++, 15);
206  printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
207}
208
209// Compute the relative path from From to To.
210static std::string computeRelativePath(StringRef From, StringRef To) {
211  if (sys::path::is_absolute(From) || sys::path::is_absolute(To))
212    return To;
213
214  StringRef DirFrom = sys::path::parent_path(From);
215  auto FromI = sys::path::begin(DirFrom);
216  auto ToI = sys::path::begin(To);
217  while (*FromI == *ToI) {
218    ++FromI;
219    ++ToI;
220  }
221
222  SmallString<128> Relative;
223  for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
224    sys::path::append(Relative, "..");
225
226  for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI)
227    sys::path::append(Relative, *ToI);
228
229#ifdef LLVM_ON_WIN32
230  // Replace backslashes with slashes so that the path is portable between *nix
231  // and Windows.
232  std::replace(Relative.begin(), Relative.end(), '\\', '/');
233#endif
234
235  return Relative.str();
236}
237
238static void writeStringTable(raw_fd_ostream &Out, StringRef ArcName,
239                             ArrayRef<NewArchiveMember> Members,
240                             std::vector<unsigned> &StringMapIndexes,
241                             bool Thin) {
242  unsigned StartOffset = 0;
243  for (const NewArchiveMember &M : Members) {
244    StringRef Path = M.Buf->getBufferIdentifier();
245    StringRef Name = M.MemberName;
246    if (!useStringTable(Thin, Name))
247      continue;
248    if (StartOffset == 0) {
249      printWithSpacePadding(Out, "//", 58);
250      Out << "`\n";
251      StartOffset = Out.tell();
252    }
253    StringMapIndexes.push_back(Out.tell() - StartOffset);
254
255    if (Thin) {
256      if (M.IsNew)
257        Out << computeRelativePath(ArcName, Path);
258      else
259        Out << M.Buf->getBufferIdentifier();
260    } else
261      Out << Name;
262
263    Out << "/\n";
264  }
265  if (StartOffset == 0)
266    return;
267  if (Out.tell() % 2)
268    Out << '\n';
269  int Pos = Out.tell();
270  Out.seek(StartOffset - 12);
271  printWithSpacePadding(Out, Pos - StartOffset, 10);
272  Out.seek(Pos);
273}
274
275static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) {
276  using namespace std::chrono;
277
278  if (!Deterministic)
279    return time_point_cast<seconds>(system_clock::now());
280  return sys::TimePoint<seconds>();
281}
282
283// Returns the offset of the first reference to a member offset.
284static ErrorOr<unsigned>
285writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,
286                 ArrayRef<NewArchiveMember> Members,
287                 std::vector<unsigned> &MemberOffsetRefs, bool Deterministic) {
288  unsigned HeaderStartOffset = 0;
289  unsigned BodyStartOffset = 0;
290  SmallString<128> NameBuf;
291  raw_svector_ostream NameOS(NameBuf);
292  LLVMContext Context;
293  for (unsigned MemberNum = 0, N = Members.size(); MemberNum < N; ++MemberNum) {
294    MemoryBufferRef MemberBuffer = Members[MemberNum].Buf->getMemBufferRef();
295    Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
296        object::SymbolicFile::createSymbolicFile(
297            MemberBuffer, llvm::file_magic::unknown, &Context);
298    if (!ObjOrErr) {
299      // FIXME: check only for "not an object file" errors.
300      consumeError(ObjOrErr.takeError());
301      continue;
302    }
303    object::SymbolicFile &Obj = *ObjOrErr.get();
304
305    if (!HeaderStartOffset) {
306      HeaderStartOffset = Out.tell();
307      if (isBSDLike(Kind))
308        printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0);
309      else
310        printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0);
311      BodyStartOffset = Out.tell();
312      print32(Out, Kind, 0); // number of entries or bytes
313    }
314
315    for (const object::BasicSymbolRef &S : Obj.symbols()) {
316      uint32_t Symflags = S.getFlags();
317      if (Symflags & object::SymbolRef::SF_FormatSpecific)
318        continue;
319      if (!(Symflags & object::SymbolRef::SF_Global))
320        continue;
321      if (Symflags & object::SymbolRef::SF_Undefined &&
322          !(Symflags & object::SymbolRef::SF_Indirect))
323        continue;
324
325      unsigned NameOffset = NameOS.tell();
326      if (auto EC = S.printName(NameOS))
327        return EC;
328      NameOS << '\0';
329      MemberOffsetRefs.push_back(MemberNum);
330      if (isBSDLike(Kind))
331        print32(Out, Kind, NameOffset);
332      print32(Out, Kind, 0); // member offset
333    }
334  }
335
336  if (HeaderStartOffset == 0)
337    return 0;
338
339  // ld64 prefers the cctools type archive which pads its string table to a
340  // boundary of sizeof(int32_t).
341  if (isBSDLike(Kind))
342    for (unsigned P = OffsetToAlignment(NameOS.tell(), sizeof(int32_t)); P--;)
343      NameOS << '\0';
344
345  StringRef StringTable = NameOS.str();
346  if (isBSDLike(Kind))
347    print32(Out, Kind, StringTable.size()); // byte count of the string table
348  Out << StringTable;
349  // If there are no symbols, emit an empty symbol table, to satisfy Solaris
350  // tools, older versions of which expect a symbol table in a non-empty
351  // archive, regardless of whether there are any symbols in it.
352  if (StringTable.size() == 0)
353    print32(Out, Kind, 0);
354
355  // ld64 requires the next member header to start at an offset that is
356  // 4 bytes aligned.
357  unsigned Pad = OffsetToAlignment(Out.tell(), 4);
358  while (Pad--)
359    Out.write(uint8_t(0));
360
361  // Patch up the size of the symbol table now that we know how big it is.
362  unsigned Pos = Out.tell();
363  const unsigned MemberHeaderSize = 60;
364  Out.seek(HeaderStartOffset + 48); // offset of the size field.
365  printWithSpacePadding(Out, Pos - MemberHeaderSize - HeaderStartOffset, 10);
366
367  // Patch up the number of symbols.
368  Out.seek(BodyStartOffset);
369  unsigned NumSyms = MemberOffsetRefs.size();
370  if (isBSDLike(Kind))
371    print32(Out, Kind, NumSyms * 8);
372  else
373    print32(Out, Kind, NumSyms);
374
375  Out.seek(Pos);
376  return BodyStartOffset + 4;
377}
378
379std::pair<StringRef, std::error_code>
380llvm::writeArchive(StringRef ArcName,
381                   std::vector<NewArchiveMember> &NewMembers,
382                   bool WriteSymtab, object::Archive::Kind Kind,
383                   bool Deterministic, bool Thin,
384                   std::unique_ptr<MemoryBuffer> OldArchiveBuf) {
385  assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
386  SmallString<128> TmpArchive;
387  int TmpArchiveFD;
388  if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a",
389                                          TmpArchiveFD, TmpArchive))
390    return std::make_pair(ArcName, EC);
391
392  tool_output_file Output(TmpArchive, TmpArchiveFD);
393  raw_fd_ostream &Out = Output.os();
394  if (Thin)
395    Out << "!<thin>\n";
396  else
397    Out << "!<arch>\n";
398
399  std::vector<unsigned> MemberOffsetRefs;
400
401  unsigned MemberReferenceOffset = 0;
402  if (WriteSymtab) {
403    ErrorOr<unsigned> MemberReferenceOffsetOrErr = writeSymbolTable(
404        Out, Kind, NewMembers, MemberOffsetRefs, Deterministic);
405    if (auto EC = MemberReferenceOffsetOrErr.getError())
406      return std::make_pair(ArcName, EC);
407    MemberReferenceOffset = MemberReferenceOffsetOrErr.get();
408  }
409
410  std::vector<unsigned> StringMapIndexes;
411  if (!isBSDLike(Kind))
412    writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin);
413
414  std::vector<unsigned>::iterator StringMapIndexIter = StringMapIndexes.begin();
415  std::vector<unsigned> MemberOffset;
416  for (const NewArchiveMember &M : NewMembers) {
417    MemoryBufferRef File = M.Buf->getMemBufferRef();
418    unsigned Padding = 0;
419
420    unsigned Pos = Out.tell();
421    MemberOffset.push_back(Pos);
422
423    // ld64 expects the members to be 8-byte aligned for 64-bit content and at
424    // least 4-byte aligned for 32-bit content.  Opt for the larger encoding
425    // uniformly.  This matches the behaviour with cctools and ensures that ld64
426    // is happy with archives that we generate.
427    if (Kind == object::Archive::K_DARWIN)
428      Padding = OffsetToAlignment(M.Buf->getBufferSize(), 8);
429
430    printMemberHeader(Out, Kind, Thin, M.MemberName, StringMapIndexIter,
431                      M.ModTime, M.UID, M.GID, M.Perms,
432                      M.Buf->getBufferSize() + Padding);
433
434    if (!Thin)
435      Out << File.getBuffer();
436
437    while (Padding--)
438      Out << '\n';
439    if (Out.tell() % 2)
440      Out << '\n';
441  }
442
443  if (MemberReferenceOffset) {
444    Out.seek(MemberReferenceOffset);
445    for (unsigned MemberNum : MemberOffsetRefs) {
446      if (isBSDLike(Kind))
447        Out.seek(Out.tell() + 4); // skip over the string offset
448      print32(Out, Kind, MemberOffset[MemberNum]);
449    }
450  }
451
452  Output.keep();
453  Out.close();
454
455  // At this point, we no longer need whatever backing memory
456  // was used to generate the NewMembers. On Windows, this buffer
457  // could be a mapped view of the file we want to replace (if
458  // we're updating an existing archive, say). In that case, the
459  // rename would still succeed, but it would leave behind a
460  // temporary file (actually the original file renamed) because
461  // a file cannot be deleted while there's a handle open on it,
462  // only renamed. So by freeing this buffer, this ensures that
463  // the last open handle on the destination file, if any, is
464  // closed before we attempt to rename.
465  OldArchiveBuf.reset();
466
467  sys::fs::rename(TmpArchive, ArcName);
468  return std::make_pair("", std::error_code());
469}
470