Home | History | Annotate | Download | only in Object
      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/IR/LLVMContext.h"
     18 #include "llvm/Object/Archive.h"
     19 #include "llvm/Object/ObjectFile.h"
     20 #include "llvm/Object/SymbolicFile.h"
     21 #include "llvm/Support/EndianStream.h"
     22 #include "llvm/Support/Errc.h"
     23 #include "llvm/Support/ErrorHandling.h"
     24 #include "llvm/Support/Format.h"
     25 #include "llvm/Support/Path.h"
     26 #include "llvm/Support/ToolOutputFile.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 
     29 #if !defined(_MSC_VER) && !defined(__MINGW32__)
     30 #include <unistd.h>
     31 #else
     32 #include <io.h>
     33 #endif
     34 
     35 using namespace llvm;
     36 
     37 NewArchiveIterator::NewArchiveIterator(const object::Archive::Child &OldMember,
     38                                        StringRef Name)
     39     : IsNewMember(false), Name(Name), OldMember(OldMember) {}
     40 
     41 NewArchiveIterator::NewArchiveIterator(StringRef FileName)
     42     : IsNewMember(true), Name(FileName), OldMember(nullptr, nullptr, nullptr) {}
     43 
     44 StringRef NewArchiveIterator::getName() const { return Name; }
     45 
     46 bool NewArchiveIterator::isNewMember() const { return IsNewMember; }
     47 
     48 const object::Archive::Child &NewArchiveIterator::getOld() const {
     49   assert(!IsNewMember);
     50   return OldMember;
     51 }
     52 
     53 StringRef NewArchiveIterator::getNew() const {
     54   assert(IsNewMember);
     55   return Name;
     56 }
     57 
     58 llvm::ErrorOr<int>
     59 NewArchiveIterator::getFD(sys::fs::file_status &NewStatus) const {
     60   assert(IsNewMember);
     61   int NewFD;
     62   if (auto EC = sys::fs::openFileForRead(Name, NewFD))
     63     return EC;
     64   assert(NewFD != -1);
     65 
     66   if (auto EC = sys::fs::status(NewFD, NewStatus))
     67     return EC;
     68 
     69   // Opening a directory doesn't make sense. Let it fail.
     70   // Linux cannot open directories with open(2), although
     71   // cygwin and *bsd can.
     72   if (NewStatus.type() == sys::fs::file_type::directory_file)
     73     return make_error_code(errc::is_a_directory);
     74 
     75   return NewFD;
     76 }
     77 
     78 template <typename T>
     79 static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size,
     80                                   bool MayTruncate = false) {
     81   uint64_t OldPos = OS.tell();
     82   OS << Data;
     83   unsigned SizeSoFar = OS.tell() - OldPos;
     84   if (Size > SizeSoFar) {
     85     OS.indent(Size - SizeSoFar);
     86   } else if (Size < SizeSoFar) {
     87     assert(MayTruncate && "Data doesn't fit in Size");
     88     // Some of the data this is used for (like UID) can be larger than the
     89     // space available in the archive format. Truncate in that case.
     90     OS.seek(OldPos + Size);
     91   }
     92 }
     93 
     94 static void print32(raw_ostream &Out, object::Archive::Kind Kind,
     95                     uint32_t Val) {
     96   if (Kind == object::Archive::K_GNU)
     97     support::endian::Writer<support::big>(Out).write(Val);
     98   else
     99     support::endian::Writer<support::little>(Out).write(Val);
    100 }
    101 
    102 static void printRestOfMemberHeader(raw_fd_ostream &Out,
    103                                     const sys::TimeValue &ModTime, unsigned UID,
    104                                     unsigned GID, unsigned Perms,
    105                                     unsigned Size) {
    106   printWithSpacePadding(Out, ModTime.toEpochTime(), 12);
    107   printWithSpacePadding(Out, UID, 6, true);
    108   printWithSpacePadding(Out, GID, 6, true);
    109   printWithSpacePadding(Out, format("%o", Perms), 8);
    110   printWithSpacePadding(Out, Size, 10);
    111   Out << "`\n";
    112 }
    113 
    114 static void printGNUSmallMemberHeader(raw_fd_ostream &Out, StringRef Name,
    115                                       const sys::TimeValue &ModTime,
    116                                       unsigned UID, unsigned GID,
    117                                       unsigned Perms, unsigned Size) {
    118   printWithSpacePadding(Out, Twine(Name) + "/", 16);
    119   printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
    120 }
    121 
    122 static void printBSDMemberHeader(raw_fd_ostream &Out, StringRef Name,
    123                                  const sys::TimeValue &ModTime, unsigned UID,
    124                                  unsigned GID, unsigned Perms, unsigned Size) {
    125   uint64_t PosAfterHeader = Out.tell() + 60 + Name.size();
    126   // Pad so that even 64 bit object files are aligned.
    127   unsigned Pad = OffsetToAlignment(PosAfterHeader, 8);
    128   unsigned NameWithPadding = Name.size() + Pad;
    129   printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16);
    130   printRestOfMemberHeader(Out, ModTime, UID, GID, Perms,
    131                           NameWithPadding + Size);
    132   Out << Name;
    133   assert(PosAfterHeader == Out.tell());
    134   while (Pad--)
    135     Out.write(uint8_t(0));
    136 }
    137 
    138 static bool useStringTable(bool Thin, StringRef Name) {
    139   return Thin || Name.size() >= 16;
    140 }
    141 
    142 static void
    143 printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin,
    144                   StringRef Name,
    145                   std::vector<unsigned>::iterator &StringMapIndexIter,
    146                   const sys::TimeValue &ModTime, unsigned UID, unsigned GID,
    147                   unsigned Perms, unsigned Size) {
    148   if (Kind == object::Archive::K_BSD)
    149     return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
    150   if (!useStringTable(Thin, Name))
    151     return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size);
    152   Out << '/';
    153   printWithSpacePadding(Out, *StringMapIndexIter++, 15);
    154   printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size);
    155 }
    156 
    157 // Compute the relative path from From to To.
    158 static std::string computeRelativePath(StringRef From, StringRef To) {
    159   if (sys::path::is_absolute(From) || sys::path::is_absolute(To))
    160     return To;
    161 
    162   StringRef DirFrom = sys::path::parent_path(From);
    163   auto FromI = sys::path::begin(DirFrom);
    164   auto ToI = sys::path::begin(To);
    165   while (*FromI == *ToI) {
    166     ++FromI;
    167     ++ToI;
    168   }
    169 
    170   SmallString<128> Relative;
    171   for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI)
    172     sys::path::append(Relative, "..");
    173 
    174   for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI)
    175     sys::path::append(Relative, *ToI);
    176 
    177   return Relative.str();
    178 }
    179 
    180 static void writeStringTable(raw_fd_ostream &Out, StringRef ArcName,
    181                              ArrayRef<NewArchiveIterator> Members,
    182                              std::vector<unsigned> &StringMapIndexes,
    183                              bool Thin) {
    184   unsigned StartOffset = 0;
    185   for (const NewArchiveIterator &I : Members) {
    186     StringRef Name = sys::path::filename(I.getName());
    187     if (!useStringTable(Thin, Name))
    188       continue;
    189     if (StartOffset == 0) {
    190       printWithSpacePadding(Out, "//", 58);
    191       Out << "`\n";
    192       StartOffset = Out.tell();
    193     }
    194     StringMapIndexes.push_back(Out.tell() - StartOffset);
    195 
    196     if (Thin)
    197       Out << computeRelativePath(ArcName, I.getName());
    198     else
    199       Out << Name;
    200 
    201     Out << "/\n";
    202   }
    203   if (StartOffset == 0)
    204     return;
    205   if (Out.tell() % 2)
    206     Out << '\n';
    207   int Pos = Out.tell();
    208   Out.seek(StartOffset - 12);
    209   printWithSpacePadding(Out, Pos - StartOffset, 10);
    210   Out.seek(Pos);
    211 }
    212 
    213 static sys::TimeValue now(bool Deterministic) {
    214   if (!Deterministic)
    215     return sys::TimeValue::now();
    216   sys::TimeValue TV;
    217   TV.fromEpochTime(0);
    218   return TV;
    219 }
    220 
    221 // Returns the offset of the first reference to a member offset.
    222 static ErrorOr<unsigned>
    223 writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind,
    224                  ArrayRef<NewArchiveIterator> Members,
    225                  ArrayRef<MemoryBufferRef> Buffers,
    226                  std::vector<unsigned> &MemberOffsetRefs, bool Deterministic) {
    227   unsigned HeaderStartOffset = 0;
    228   unsigned BodyStartOffset = 0;
    229   SmallString<128> NameBuf;
    230   raw_svector_ostream NameOS(NameBuf);
    231   LLVMContext Context;
    232   for (unsigned MemberNum = 0, N = Members.size(); MemberNum < N; ++MemberNum) {
    233     MemoryBufferRef MemberBuffer = Buffers[MemberNum];
    234     ErrorOr<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
    235         object::SymbolicFile::createSymbolicFile(
    236             MemberBuffer, sys::fs::file_magic::unknown, &Context);
    237     if (!ObjOrErr)
    238       continue;  // FIXME: check only for "not an object file" errors.
    239     object::SymbolicFile &Obj = *ObjOrErr.get();
    240 
    241     if (!HeaderStartOffset) {
    242       HeaderStartOffset = Out.tell();
    243       if (Kind == object::Archive::K_GNU)
    244         printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0);
    245       else
    246         printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0);
    247       BodyStartOffset = Out.tell();
    248       print32(Out, Kind, 0); // number of entries or bytes
    249     }
    250 
    251     for (const object::BasicSymbolRef &S : Obj.symbols()) {
    252       uint32_t Symflags = S.getFlags();
    253       if (Symflags & object::SymbolRef::SF_FormatSpecific)
    254         continue;
    255       if (!(Symflags & object::SymbolRef::SF_Global))
    256         continue;
    257       if (Symflags & object::SymbolRef::SF_Undefined)
    258         continue;
    259 
    260       unsigned NameOffset = NameOS.tell();
    261       if (auto EC = S.printName(NameOS))
    262         return EC;
    263       NameOS << '\0';
    264       MemberOffsetRefs.push_back(MemberNum);
    265       if (Kind == object::Archive::K_BSD)
    266         print32(Out, Kind, NameOffset);
    267       print32(Out, Kind, 0); // member offset
    268     }
    269   }
    270 
    271   if (HeaderStartOffset == 0)
    272     return 0;
    273 
    274   StringRef StringTable = NameOS.str();
    275   if (Kind == object::Archive::K_BSD)
    276     print32(Out, Kind, StringTable.size()); // byte count of the string table
    277   Out << StringTable;
    278 
    279   // ld64 requires the next member header to start at an offset that is
    280   // 4 bytes aligned.
    281   unsigned Pad = OffsetToAlignment(Out.tell(), 4);
    282   while (Pad--)
    283     Out.write(uint8_t(0));
    284 
    285   // Patch up the size of the symbol table now that we know how big it is.
    286   unsigned Pos = Out.tell();
    287   const unsigned MemberHeaderSize = 60;
    288   Out.seek(HeaderStartOffset + 48); // offset of the size field.
    289   printWithSpacePadding(Out, Pos - MemberHeaderSize - HeaderStartOffset, 10);
    290 
    291   // Patch up the number of symbols.
    292   Out.seek(BodyStartOffset);
    293   unsigned NumSyms = MemberOffsetRefs.size();
    294   if (Kind == object::Archive::K_GNU)
    295     print32(Out, Kind, NumSyms);
    296   else
    297     print32(Out, Kind, NumSyms * 8);
    298 
    299   Out.seek(Pos);
    300   return BodyStartOffset + 4;
    301 }
    302 
    303 std::pair<StringRef, std::error_code>
    304 llvm::writeArchive(StringRef ArcName,
    305                    std::vector<NewArchiveIterator> &NewMembers,
    306                    bool WriteSymtab, object::Archive::Kind Kind,
    307                    bool Deterministic, bool Thin) {
    308   SmallString<128> TmpArchive;
    309   int TmpArchiveFD;
    310   if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a",
    311                                           TmpArchiveFD, TmpArchive))
    312     return std::make_pair(ArcName, EC);
    313 
    314   tool_output_file Output(TmpArchive, TmpArchiveFD);
    315   raw_fd_ostream &Out = Output.os();
    316   if (Thin)
    317     Out << "!<thin>\n";
    318   else
    319     Out << "!<arch>\n";
    320 
    321   std::vector<unsigned> MemberOffsetRefs;
    322 
    323   std::vector<std::unique_ptr<MemoryBuffer>> Buffers;
    324   std::vector<MemoryBufferRef> Members;
    325   std::vector<sys::fs::file_status> NewMemberStatus;
    326 
    327   for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) {
    328     NewArchiveIterator &Member = NewMembers[I];
    329     MemoryBufferRef MemberRef;
    330 
    331     if (Member.isNewMember()) {
    332       StringRef Filename = Member.getNew();
    333       NewMemberStatus.resize(NewMemberStatus.size() + 1);
    334       sys::fs::file_status &Status = NewMemberStatus.back();
    335       ErrorOr<int> FD = Member.getFD(Status);
    336       if (auto EC = FD.getError())
    337         return std::make_pair(Filename, EC);
    338       ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr =
    339           MemoryBuffer::getOpenFile(FD.get(), Filename, Status.getSize(),
    340                                     false);
    341       if (auto EC = MemberBufferOrErr.getError())
    342         return std::make_pair(Filename, EC);
    343       if (close(FD.get()) != 0)
    344         return std::make_pair(Filename,
    345                               std::error_code(errno, std::generic_category()));
    346       Buffers.push_back(std::move(MemberBufferOrErr.get()));
    347       MemberRef = Buffers.back()->getMemBufferRef();
    348     } else {
    349       const object::Archive::Child &OldMember = Member.getOld();
    350       assert((!Thin || OldMember.getParent()->isThin()) &&
    351              "Thin archives cannot refers to member of other archives");
    352       ErrorOr<MemoryBufferRef> MemberBufferOrErr =
    353           OldMember.getMemoryBufferRef();
    354       if (auto EC = MemberBufferOrErr.getError())
    355         return std::make_pair("", EC);
    356       MemberRef = MemberBufferOrErr.get();
    357     }
    358     Members.push_back(MemberRef);
    359   }
    360 
    361   unsigned MemberReferenceOffset = 0;
    362   if (WriteSymtab) {
    363     ErrorOr<unsigned> MemberReferenceOffsetOrErr = writeSymbolTable(
    364         Out, Kind, NewMembers, Members, MemberOffsetRefs, Deterministic);
    365     if (auto EC = MemberReferenceOffsetOrErr.getError())
    366       return std::make_pair(ArcName, EC);
    367     MemberReferenceOffset = MemberReferenceOffsetOrErr.get();
    368   }
    369 
    370   std::vector<unsigned> StringMapIndexes;
    371   if (Kind != object::Archive::K_BSD)
    372     writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin);
    373 
    374   unsigned MemberNum = 0;
    375   unsigned NewMemberNum = 0;
    376   std::vector<unsigned>::iterator StringMapIndexIter = StringMapIndexes.begin();
    377   std::vector<unsigned> MemberOffset;
    378   for (const NewArchiveIterator &I : NewMembers) {
    379     MemoryBufferRef File = Members[MemberNum++];
    380 
    381     unsigned Pos = Out.tell();
    382     MemberOffset.push_back(Pos);
    383 
    384     sys::TimeValue ModTime;
    385     unsigned UID;
    386     unsigned GID;
    387     unsigned Perms;
    388     if (Deterministic) {
    389       ModTime.fromEpochTime(0);
    390       UID = 0;
    391       GID = 0;
    392       Perms = 0644;
    393     } else if (I.isNewMember()) {
    394       const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum];
    395       ModTime = Status.getLastModificationTime();
    396       UID = Status.getUser();
    397       GID = Status.getGroup();
    398       Perms = Status.permissions();
    399     } else {
    400       const object::Archive::Child &OldMember = I.getOld();
    401       ModTime = OldMember.getLastModified();
    402       UID = OldMember.getUID();
    403       GID = OldMember.getGID();
    404       Perms = OldMember.getAccessMode();
    405     }
    406 
    407     if (I.isNewMember()) {
    408       StringRef FileName = I.getNew();
    409       const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum++];
    410       printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName),
    411                         StringMapIndexIter, ModTime, UID, GID, Perms,
    412                         Status.getSize());
    413     } else {
    414       const object::Archive::Child &OldMember = I.getOld();
    415       ErrorOr<uint32_t> Size = OldMember.getSize();
    416       if (std::error_code EC = Size.getError())
    417         return std::make_pair("", EC);
    418       StringRef FileName = I.getName();
    419       printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName),
    420                         StringMapIndexIter, ModTime, UID, GID, Perms,
    421                         Size.get());
    422     }
    423 
    424     if (!Thin)
    425       Out << File.getBuffer();
    426 
    427     if (Out.tell() % 2)
    428       Out << '\n';
    429   }
    430 
    431   if (MemberReferenceOffset) {
    432     Out.seek(MemberReferenceOffset);
    433     for (unsigned MemberNum : MemberOffsetRefs) {
    434       if (Kind == object::Archive::K_BSD)
    435         Out.seek(Out.tell() + 4); // skip over the string offset
    436       print32(Out, Kind, MemberOffset[MemberNum]);
    437     }
    438   }
    439 
    440   Output.keep();
    441   Out.close();
    442   sys::fs::rename(TmpArchive, ArcName);
    443   return std::make_pair("", std::error_code());
    444 }
    445