1284184Sdim//===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===// 2284184Sdim// 3284184Sdim// The LLVM Compiler Infrastructure 4284184Sdim// 5284184Sdim// This file is distributed under the University of Illinois Open Source 6284184Sdim// License. See LICENSE.TXT for details. 7284184Sdim// 8284184Sdim//===----------------------------------------------------------------------===// 9284184Sdim// 10284184Sdim// This file defines the writeArchive function. 11284184Sdim// 12284184Sdim//===----------------------------------------------------------------------===// 13284184Sdim 14284184Sdim#include "llvm/Object/ArchiveWriter.h" 15284184Sdim#include "llvm/ADT/ArrayRef.h" 16284184Sdim#include "llvm/ADT/StringRef.h" 17284184Sdim#include "llvm/IR/LLVMContext.h" 18284184Sdim#include "llvm/Object/Archive.h" 19284184Sdim#include "llvm/Object/ObjectFile.h" 20284184Sdim#include "llvm/Object/SymbolicFile.h" 21284734Sdim#include "llvm/Support/EndianStream.h" 22284734Sdim#include "llvm/Support/Errc.h" 23284184Sdim#include "llvm/Support/ErrorHandling.h" 24284184Sdim#include "llvm/Support/Format.h" 25284184Sdim#include "llvm/Support/Path.h" 26284184Sdim#include "llvm/Support/ToolOutputFile.h" 27284184Sdim#include "llvm/Support/raw_ostream.h" 28284184Sdim 29284184Sdim#if !defined(_MSC_VER) && !defined(__MINGW32__) 30284184Sdim#include <unistd.h> 31284184Sdim#else 32284184Sdim#include <io.h> 33284184Sdim#endif 34284184Sdim 35284184Sdimusing namespace llvm; 36284184Sdim 37296417SdimNewArchiveIterator::NewArchiveIterator(const object::Archive::Child &OldMember, 38284184Sdim StringRef Name) 39296417Sdim : IsNewMember(false), Name(Name), OldMember(OldMember) {} 40284184Sdim 41296417SdimNewArchiveIterator::NewArchiveIterator(StringRef FileName) 42296417Sdim : IsNewMember(true), Name(FileName), OldMember(nullptr, nullptr, nullptr) {} 43284184Sdim 44284184SdimStringRef NewArchiveIterator::getName() const { return Name; } 45284184Sdim 46284184Sdimbool NewArchiveIterator::isNewMember() const { return IsNewMember; } 47284184Sdim 48296417Sdimconst object::Archive::Child &NewArchiveIterator::getOld() const { 49284184Sdim assert(!IsNewMember); 50296417Sdim return OldMember; 51284184Sdim} 52284184Sdim 53284184SdimStringRef NewArchiveIterator::getNew() const { 54284184Sdim assert(IsNewMember); 55296417Sdim return Name; 56284184Sdim} 57284184Sdim 58284184Sdimllvm::ErrorOr<int> 59284184SdimNewArchiveIterator::getFD(sys::fs::file_status &NewStatus) const { 60284184Sdim assert(IsNewMember); 61284184Sdim int NewFD; 62296417Sdim if (auto EC = sys::fs::openFileForRead(Name, NewFD)) 63284184Sdim return EC; 64284184Sdim assert(NewFD != -1); 65284184Sdim 66284184Sdim if (auto EC = sys::fs::status(NewFD, NewStatus)) 67284184Sdim return EC; 68284184Sdim 69284184Sdim // Opening a directory doesn't make sense. Let it fail. 70284184Sdim // Linux cannot open directories with open(2), although 71284184Sdim // cygwin and *bsd can. 72284184Sdim if (NewStatus.type() == sys::fs::file_type::directory_file) 73284734Sdim return make_error_code(errc::is_a_directory); 74284184Sdim 75284184Sdim return NewFD; 76284184Sdim} 77284184Sdim 78284184Sdimtemplate <typename T> 79284184Sdimstatic void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size, 80296417Sdim bool MayTruncate = false) { 81284184Sdim uint64_t OldPos = OS.tell(); 82284184Sdim OS << Data; 83284184Sdim unsigned SizeSoFar = OS.tell() - OldPos; 84284184Sdim if (Size > SizeSoFar) { 85284734Sdim OS.indent(Size - SizeSoFar); 86284184Sdim } else if (Size < SizeSoFar) { 87284184Sdim assert(MayTruncate && "Data doesn't fit in Size"); 88284184Sdim // Some of the data this is used for (like UID) can be larger than the 89284184Sdim // space available in the archive format. Truncate in that case. 90284184Sdim OS.seek(OldPos + Size); 91284184Sdim } 92284184Sdim} 93284184Sdim 94286684Sdimstatic void print32(raw_ostream &Out, object::Archive::Kind Kind, 95286684Sdim uint32_t Val) { 96286684Sdim if (Kind == object::Archive::K_GNU) 97286684Sdim support::endian::Writer<support::big>(Out).write(Val); 98286684Sdim else 99286684Sdim support::endian::Writer<support::little>(Out).write(Val); 100284184Sdim} 101284184Sdim 102284184Sdimstatic void printRestOfMemberHeader(raw_fd_ostream &Out, 103284184Sdim const sys::TimeValue &ModTime, unsigned UID, 104284184Sdim unsigned GID, unsigned Perms, 105284184Sdim unsigned Size) { 106284184Sdim printWithSpacePadding(Out, ModTime.toEpochTime(), 12); 107284184Sdim printWithSpacePadding(Out, UID, 6, true); 108284184Sdim printWithSpacePadding(Out, GID, 6, true); 109284184Sdim printWithSpacePadding(Out, format("%o", Perms), 8); 110284184Sdim printWithSpacePadding(Out, Size, 10); 111284184Sdim Out << "`\n"; 112284184Sdim} 113284184Sdim 114286684Sdimstatic void printGNUSmallMemberHeader(raw_fd_ostream &Out, StringRef Name, 115286684Sdim const sys::TimeValue &ModTime, 116286684Sdim unsigned UID, unsigned GID, 117286684Sdim unsigned Perms, unsigned Size) { 118284184Sdim printWithSpacePadding(Out, Twine(Name) + "/", 16); 119284184Sdim printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); 120284184Sdim} 121284184Sdim 122286684Sdimstatic void printBSDMemberHeader(raw_fd_ostream &Out, StringRef Name, 123286684Sdim const sys::TimeValue &ModTime, unsigned UID, 124286684Sdim unsigned GID, unsigned Perms, unsigned Size) { 125286684Sdim uint64_t PosAfterHeader = Out.tell() + 60 + Name.size(); 126286684Sdim // Pad so that even 64 bit object files are aligned. 127286684Sdim unsigned Pad = OffsetToAlignment(PosAfterHeader, 8); 128286684Sdim unsigned NameWithPadding = Name.size() + Pad; 129286684Sdim printWithSpacePadding(Out, Twine("#1/") + Twine(NameWithPadding), 16); 130286684Sdim printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, 131286684Sdim NameWithPadding + Size); 132286684Sdim Out << Name; 133286684Sdim assert(PosAfterHeader == Out.tell()); 134286684Sdim while (Pad--) 135286684Sdim Out.write(uint8_t(0)); 136286684Sdim} 137286684Sdim 138296417Sdimstatic bool useStringTable(bool Thin, StringRef Name) { 139296417Sdim return Thin || Name.size() >= 16; 140296417Sdim} 141296417Sdim 142286684Sdimstatic void 143296417SdimprintMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin, 144286684Sdim StringRef Name, 145286684Sdim std::vector<unsigned>::iterator &StringMapIndexIter, 146286684Sdim const sys::TimeValue &ModTime, unsigned UID, unsigned GID, 147286684Sdim unsigned Perms, unsigned Size) { 148286684Sdim if (Kind == object::Archive::K_BSD) 149286684Sdim return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size); 150296417Sdim if (!useStringTable(Thin, Name)) 151286684Sdim return printGNUSmallMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size); 152284184Sdim Out << '/'; 153286684Sdim printWithSpacePadding(Out, *StringMapIndexIter++, 15); 154284184Sdim printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); 155284184Sdim} 156284184Sdim 157296417Sdim// Compute the relative path from From to To. 158296417Sdimstatic std::string computeRelativePath(StringRef From, StringRef To) { 159296417Sdim if (sys::path::is_absolute(From) || sys::path::is_absolute(To)) 160296417Sdim return To; 161296417Sdim 162296417Sdim StringRef DirFrom = sys::path::parent_path(From); 163296417Sdim auto FromI = sys::path::begin(DirFrom); 164296417Sdim auto ToI = sys::path::begin(To); 165296417Sdim while (*FromI == *ToI) { 166296417Sdim ++FromI; 167296417Sdim ++ToI; 168296417Sdim } 169296417Sdim 170296417Sdim SmallString<128> Relative; 171296417Sdim for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI) 172296417Sdim sys::path::append(Relative, ".."); 173296417Sdim 174296417Sdim for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI) 175296417Sdim sys::path::append(Relative, *ToI); 176296417Sdim 177296417Sdim return Relative.str(); 178296417Sdim} 179296417Sdim 180296417Sdimstatic void writeStringTable(raw_fd_ostream &Out, StringRef ArcName, 181284184Sdim ArrayRef<NewArchiveIterator> Members, 182296417Sdim std::vector<unsigned> &StringMapIndexes, 183296417Sdim bool Thin) { 184284184Sdim unsigned StartOffset = 0; 185296417Sdim for (const NewArchiveIterator &I : Members) { 186296417Sdim StringRef Name = sys::path::filename(I.getName()); 187296417Sdim if (!useStringTable(Thin, Name)) 188284184Sdim continue; 189284184Sdim if (StartOffset == 0) { 190284184Sdim printWithSpacePadding(Out, "//", 58); 191284184Sdim Out << "`\n"; 192284184Sdim StartOffset = Out.tell(); 193284184Sdim } 194284184Sdim StringMapIndexes.push_back(Out.tell() - StartOffset); 195296417Sdim 196296417Sdim if (Thin) 197296417Sdim Out << computeRelativePath(ArcName, I.getName()); 198296417Sdim else 199296417Sdim Out << Name; 200296417Sdim 201296417Sdim Out << "/\n"; 202284184Sdim } 203284184Sdim if (StartOffset == 0) 204284184Sdim return; 205284184Sdim if (Out.tell() % 2) 206284184Sdim Out << '\n'; 207284184Sdim int Pos = Out.tell(); 208284184Sdim Out.seek(StartOffset - 12); 209284184Sdim printWithSpacePadding(Out, Pos - StartOffset, 10); 210284184Sdim Out.seek(Pos); 211284184Sdim} 212284184Sdim 213286684Sdimstatic sys::TimeValue now(bool Deterministic) { 214286684Sdim if (!Deterministic) 215286684Sdim return sys::TimeValue::now(); 216286684Sdim sys::TimeValue TV; 217286684Sdim TV.fromEpochTime(0); 218286684Sdim return TV; 219286684Sdim} 220286684Sdim 221284184Sdim// Returns the offset of the first reference to a member offset. 222284184Sdimstatic ErrorOr<unsigned> 223286684SdimwriteSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind, 224286684Sdim ArrayRef<NewArchiveIterator> Members, 225284184Sdim ArrayRef<MemoryBufferRef> Buffers, 226286684Sdim std::vector<unsigned> &MemberOffsetRefs, bool Deterministic) { 227286684Sdim unsigned HeaderStartOffset = 0; 228286684Sdim unsigned BodyStartOffset = 0; 229286684Sdim SmallString<128> NameBuf; 230286684Sdim raw_svector_ostream NameOS(NameBuf); 231284184Sdim LLVMContext Context; 232286684Sdim for (unsigned MemberNum = 0, N = Members.size(); MemberNum < N; ++MemberNum) { 233284184Sdim MemoryBufferRef MemberBuffer = Buffers[MemberNum]; 234284184Sdim ErrorOr<std::unique_ptr<object::SymbolicFile>> ObjOrErr = 235284184Sdim object::SymbolicFile::createSymbolicFile( 236284184Sdim MemberBuffer, sys::fs::file_magic::unknown, &Context); 237284184Sdim if (!ObjOrErr) 238284184Sdim continue; // FIXME: check only for "not an object file" errors. 239284184Sdim object::SymbolicFile &Obj = *ObjOrErr.get(); 240284184Sdim 241286684Sdim if (!HeaderStartOffset) { 242286684Sdim HeaderStartOffset = Out.tell(); 243286684Sdim if (Kind == object::Archive::K_GNU) 244286684Sdim printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0); 245286684Sdim else 246286684Sdim printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0); 247286684Sdim BodyStartOffset = Out.tell(); 248286684Sdim print32(Out, Kind, 0); // number of entries or bytes 249284184Sdim } 250284184Sdim 251284184Sdim for (const object::BasicSymbolRef &S : Obj.symbols()) { 252284184Sdim uint32_t Symflags = S.getFlags(); 253284184Sdim if (Symflags & object::SymbolRef::SF_FormatSpecific) 254284184Sdim continue; 255284184Sdim if (!(Symflags & object::SymbolRef::SF_Global)) 256284184Sdim continue; 257284184Sdim if (Symflags & object::SymbolRef::SF_Undefined) 258284184Sdim continue; 259286684Sdim 260286684Sdim unsigned NameOffset = NameOS.tell(); 261284184Sdim if (auto EC = S.printName(NameOS)) 262284184Sdim return EC; 263284184Sdim NameOS << '\0'; 264284184Sdim MemberOffsetRefs.push_back(MemberNum); 265286684Sdim if (Kind == object::Archive::K_BSD) 266286684Sdim print32(Out, Kind, NameOffset); 267286684Sdim print32(Out, Kind, 0); // member offset 268284184Sdim } 269284184Sdim } 270284184Sdim 271286684Sdim if (HeaderStartOffset == 0) 272284184Sdim return 0; 273284184Sdim 274286684Sdim StringRef StringTable = NameOS.str(); 275286684Sdim if (Kind == object::Archive::K_BSD) 276286684Sdim print32(Out, Kind, StringTable.size()); // byte count of the string table 277286684Sdim Out << StringTable; 278284184Sdim 279286684Sdim // ld64 requires the next member header to start at an offset that is 280286684Sdim // 4 bytes aligned. 281286684Sdim unsigned Pad = OffsetToAlignment(Out.tell(), 4); 282286684Sdim while (Pad--) 283286684Sdim Out.write(uint8_t(0)); 284286684Sdim 285286684Sdim // Patch up the size of the symbol table now that we know how big it is. 286284184Sdim unsigned Pos = Out.tell(); 287286684Sdim const unsigned MemberHeaderSize = 60; 288286684Sdim Out.seek(HeaderStartOffset + 48); // offset of the size field. 289286684Sdim printWithSpacePadding(Out, Pos - MemberHeaderSize - HeaderStartOffset, 10); 290286684Sdim 291286684Sdim // Patch up the number of symbols. 292286684Sdim Out.seek(BodyStartOffset); 293286684Sdim unsigned NumSyms = MemberOffsetRefs.size(); 294286684Sdim if (Kind == object::Archive::K_GNU) 295286684Sdim print32(Out, Kind, NumSyms); 296286684Sdim else 297286684Sdim print32(Out, Kind, NumSyms * 8); 298286684Sdim 299284184Sdim Out.seek(Pos); 300286684Sdim return BodyStartOffset + 4; 301284184Sdim} 302284184Sdim 303296417Sdimstd::pair<StringRef, std::error_code> 304296417Sdimllvm::writeArchive(StringRef ArcName, 305296417Sdim std::vector<NewArchiveIterator> &NewMembers, 306296417Sdim bool WriteSymtab, object::Archive::Kind Kind, 307296417Sdim bool Deterministic, bool Thin) { 308284184Sdim SmallString<128> TmpArchive; 309284184Sdim int TmpArchiveFD; 310284184Sdim if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a", 311284184Sdim TmpArchiveFD, TmpArchive)) 312284184Sdim return std::make_pair(ArcName, EC); 313284184Sdim 314284184Sdim tool_output_file Output(TmpArchive, TmpArchiveFD); 315284184Sdim raw_fd_ostream &Out = Output.os(); 316296417Sdim if (Thin) 317296417Sdim Out << "!<thin>\n"; 318296417Sdim else 319296417Sdim Out << "!<arch>\n"; 320284184Sdim 321284184Sdim std::vector<unsigned> MemberOffsetRefs; 322284184Sdim 323284184Sdim std::vector<std::unique_ptr<MemoryBuffer>> Buffers; 324284184Sdim std::vector<MemoryBufferRef> Members; 325284184Sdim std::vector<sys::fs::file_status> NewMemberStatus; 326284184Sdim 327284184Sdim for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) { 328284184Sdim NewArchiveIterator &Member = NewMembers[I]; 329284184Sdim MemoryBufferRef MemberRef; 330284184Sdim 331284184Sdim if (Member.isNewMember()) { 332284184Sdim StringRef Filename = Member.getNew(); 333284184Sdim NewMemberStatus.resize(NewMemberStatus.size() + 1); 334284184Sdim sys::fs::file_status &Status = NewMemberStatus.back(); 335284184Sdim ErrorOr<int> FD = Member.getFD(Status); 336284184Sdim if (auto EC = FD.getError()) 337284184Sdim return std::make_pair(Filename, EC); 338284184Sdim ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr = 339284184Sdim MemoryBuffer::getOpenFile(FD.get(), Filename, Status.getSize(), 340284184Sdim false); 341284184Sdim if (auto EC = MemberBufferOrErr.getError()) 342284184Sdim return std::make_pair(Filename, EC); 343284184Sdim if (close(FD.get()) != 0) 344284184Sdim return std::make_pair(Filename, 345284184Sdim std::error_code(errno, std::generic_category())); 346284184Sdim Buffers.push_back(std::move(MemberBufferOrErr.get())); 347284184Sdim MemberRef = Buffers.back()->getMemBufferRef(); 348284184Sdim } else { 349296417Sdim const object::Archive::Child &OldMember = Member.getOld(); 350296417Sdim assert((!Thin || OldMember.getParent()->isThin()) && 351296417Sdim "Thin archives cannot refers to member of other archives"); 352284184Sdim ErrorOr<MemoryBufferRef> MemberBufferOrErr = 353296417Sdim OldMember.getMemoryBufferRef(); 354284184Sdim if (auto EC = MemberBufferOrErr.getError()) 355284184Sdim return std::make_pair("", EC); 356284184Sdim MemberRef = MemberBufferOrErr.get(); 357284184Sdim } 358284184Sdim Members.push_back(MemberRef); 359284184Sdim } 360284184Sdim 361284184Sdim unsigned MemberReferenceOffset = 0; 362284184Sdim if (WriteSymtab) { 363286684Sdim ErrorOr<unsigned> MemberReferenceOffsetOrErr = writeSymbolTable( 364286684Sdim Out, Kind, NewMembers, Members, MemberOffsetRefs, Deterministic); 365284184Sdim if (auto EC = MemberReferenceOffsetOrErr.getError()) 366284184Sdim return std::make_pair(ArcName, EC); 367284184Sdim MemberReferenceOffset = MemberReferenceOffsetOrErr.get(); 368284184Sdim } 369284184Sdim 370284184Sdim std::vector<unsigned> StringMapIndexes; 371286684Sdim if (Kind != object::Archive::K_BSD) 372296417Sdim writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin); 373284184Sdim 374284184Sdim unsigned MemberNum = 0; 375284184Sdim unsigned NewMemberNum = 0; 376286684Sdim std::vector<unsigned>::iterator StringMapIndexIter = StringMapIndexes.begin(); 377284184Sdim std::vector<unsigned> MemberOffset; 378286684Sdim for (const NewArchiveIterator &I : NewMembers) { 379286684Sdim MemoryBufferRef File = Members[MemberNum++]; 380284184Sdim 381284184Sdim unsigned Pos = Out.tell(); 382284184Sdim MemberOffset.push_back(Pos); 383284184Sdim 384286684Sdim sys::TimeValue ModTime; 385286684Sdim unsigned UID; 386286684Sdim unsigned GID; 387286684Sdim unsigned Perms; 388286684Sdim if (Deterministic) { 389286684Sdim ModTime.fromEpochTime(0); 390286684Sdim UID = 0; 391286684Sdim GID = 0; 392286684Sdim Perms = 0644; 393286684Sdim } else if (I.isNewMember()) { 394284184Sdim const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum]; 395286684Sdim ModTime = Status.getLastModificationTime(); 396286684Sdim UID = Status.getUser(); 397286684Sdim GID = Status.getGroup(); 398286684Sdim Perms = Status.permissions(); 399286684Sdim } else { 400296417Sdim const object::Archive::Child &OldMember = I.getOld(); 401296417Sdim ModTime = OldMember.getLastModified(); 402296417Sdim UID = OldMember.getUID(); 403296417Sdim GID = OldMember.getGID(); 404296417Sdim Perms = OldMember.getAccessMode(); 405286684Sdim } 406284184Sdim 407286684Sdim if (I.isNewMember()) { 408286684Sdim StringRef FileName = I.getNew(); 409286684Sdim const sys::fs::file_status &Status = NewMemberStatus[NewMemberNum++]; 410296417Sdim printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName), 411286684Sdim StringMapIndexIter, ModTime, UID, GID, Perms, 412286684Sdim Status.getSize()); 413284184Sdim } else { 414296417Sdim const object::Archive::Child &OldMember = I.getOld(); 415296417Sdim ErrorOr<uint32_t> Size = OldMember.getSize(); 416296417Sdim if (std::error_code EC = Size.getError()) 417296417Sdim return std::make_pair("", EC); 418296417Sdim StringRef FileName = I.getName(); 419296417Sdim printMemberHeader(Out, Kind, Thin, sys::path::filename(FileName), 420296417Sdim StringMapIndexIter, ModTime, UID, GID, Perms, 421296417Sdim Size.get()); 422284184Sdim } 423284184Sdim 424296417Sdim if (!Thin) 425296417Sdim Out << File.getBuffer(); 426284184Sdim 427284184Sdim if (Out.tell() % 2) 428284184Sdim Out << '\n'; 429284184Sdim } 430284184Sdim 431284184Sdim if (MemberReferenceOffset) { 432284184Sdim Out.seek(MemberReferenceOffset); 433286684Sdim for (unsigned MemberNum : MemberOffsetRefs) { 434286684Sdim if (Kind == object::Archive::K_BSD) 435286684Sdim Out.seek(Out.tell() + 4); // skip over the string offset 436286684Sdim print32(Out, Kind, MemberOffset[MemberNum]); 437286684Sdim } 438284184Sdim } 439284184Sdim 440284184Sdim Output.keep(); 441284184Sdim Out.close(); 442284184Sdim sys::fs::rename(TmpArchive, ArcName); 443284184Sdim return std::make_pair("", std::error_code()); 444284184Sdim} 445