LCOV - code coverage report
Current view: top level - lib/Object - ArchiveWriter.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 235 248 94.8 %
Date: 2017-09-14 15:23:50 Functions: 17 17 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13