X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=blobdiff_plain;f=tools%2Fllvm-ar%2Fllvm-ar.cpp;h=22fa291fbabafda4028d986de999ec9774928a60;hp=d65f9ecc9fe51a8b784bf554f353f37ee433744d;hb=c6961f286bb2028474e4005c667e1090e55debba;hpb=95779b65cf7b7d7c22bf2d22d03c661c8130cbe2 diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index d65f9ecc9fe..22fa291fbab 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -12,21 +12,25 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/StringSwitch.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/LineIterator.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include #include -#include #include #if !defined(_MSC_VER) && !defined(__MINGW32__) @@ -43,7 +47,7 @@ static StringRef ToolName; static const char *TemporaryOutput; static int TmpArchiveFD = -1; -// fail - Show the error message and exit. +// Show the error message and exit. LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { outs() << ToolName << ": " << Error << ".\n"; if (TmpArchiveFD != -1) @@ -53,7 +57,7 @@ LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { exit(1); } -static void failIfError(error_code EC, Twine Context = "") { +static void failIfError(std::error_code EC, Twine Context = "") { if (!EC) return; @@ -63,23 +67,18 @@ static void failIfError(error_code EC, Twine Context = "") { fail(Context + ": " + EC.message()); } -// Option for compatibility with AIX, not used but must allow it to be present. -static cl::opt -X32Option ("X32_64", cl::Hidden, - cl::desc("Ignored option for compatibility with AIX")); +// llvm-ar/llvm-ranlib remaining positional arguments. +static cl::list + RestOfArgs(cl::Positional, cl::ZeroOrMore, + cl::desc("[relpos] [count] [members]...")); -// llvm-ar operation code and modifier flags. This must come first. -static cl::opt -Options(cl::Positional, cl::Required, cl::desc("{operation}[modifiers]...")); +static cl::opt MRI("M", cl::desc("")); -// llvm-ar remaining positional arguments. -static cl::list -RestOfArgs(cl::Positional, cl::OneOrMore, - cl::desc("[relpos] [count] [members]...")); +std::string Options; -// MoreHelp - Provide additional help output explaining the operations and -// modifiers of llvm-ar. This object instructs the CommandLine library -// to print the text of the constructor when the --help option is given. +// Provide additional help output explaining the operations and modifiers of +// llvm-ar. This object instructs the CommandLine library to print the text of +// the constructor when the --help option is given. static cl::extrahelp MoreHelp( "\nOPERATIONS:\n" " d[NsS] - delete file(s) from the archive\n" @@ -112,7 +111,8 @@ enum ArchiveOperation { QuickAppend, ///< Quickly append to end of archive ReplaceOrInsert, ///< Replace or Insert members DisplayTable, ///< Display the table of contents - Extract ///< Extract files back to file system + Extract, ///< Extract files back to file system + CreateSymTab ///< Create a symbol table in an existing archive }; // Modifiers to follow operation to vary behavior @@ -122,6 +122,7 @@ static bool Create = false; ///< 'c' modifier static bool OriginalDates = false; ///< 'o' modifier static bool OnlyUpdate = false; ///< 'u' modifier static bool Verbose = false; ///< 'v' modifier +static bool Symtab = true; ///< 's' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -135,9 +136,9 @@ static std::string ArchiveName; // This variable holds the list of member files to proecess, as given // on the command line. -static std::vector Members; +static std::vector Members; -// show_help - Show the error message, the help message and exit. +// Show the error message, the help message and exit. LLVM_ATTRIBUTE_NORETURN static void show_help(const std::string &msg) { errs() << ToolName << ": " << msg << "\n\n"; @@ -145,8 +146,8 @@ show_help(const std::string &msg) { std::exit(1); } -// getRelPos - Extract the member filename from the command line for -// the [relpos] argument associated with a, b, and i modifiers +// Extract the member filename from the command line for the [relpos] argument +// associated with a, b, and i modifiers static void getRelPos() { if(RestOfArgs.size() == 0) show_help("Expected [relpos] for a, b, or i modifier"); @@ -154,7 +155,14 @@ static void getRelPos() { RestOfArgs.erase(RestOfArgs.begin()); } -// getArchive - Get the archive file name from the command line +static void getOptions() { + if(RestOfArgs.size() == 0) + show_help("Expected options"); + Options = RestOfArgs[0]; + RestOfArgs.erase(RestOfArgs.begin()); +} + +// Get the archive file name from the command line static void getArchive() { if(RestOfArgs.size() == 0) show_help("An archive name must be specified"); @@ -162,17 +170,77 @@ static void getArchive() { RestOfArgs.erase(RestOfArgs.begin()); } -// getMembers - Copy over remaining items in RestOfArgs to our Members vector -// This is just for clarity. +// Copy over remaining items in RestOfArgs to our Members vector static void getMembers() { - if(RestOfArgs.size() > 0) - Members = std::vector(RestOfArgs); + for (auto &Arg : RestOfArgs) + Members.push_back(Arg); +} + +namespace { +enum class MRICommand { AddMod, Create, Save, End, Invalid }; +} + +static std::vector MRIMembers; +static ArchiveOperation parseMRIScript() { + ErrorOr> Buf = MemoryBuffer::getSTDIN(); + failIfError(Buf.getError()); + const MemoryBuffer &Ref = *Buf.get(); + bool Saved = false; + + for (line_iterator I(Ref, /*SkipBlanks*/ true, ';'), E; I != E; ++I) { + StringRef Line = *I; + StringRef CommandStr, Rest; + std::tie(CommandStr, Rest) = Line.split(' '); + auto Command = StringSwitch(CommandStr.lower()) + .Case("addmod", MRICommand::AddMod) + .Case("create", MRICommand::Create) + .Case("save", MRICommand::Save) + .Case("end", MRICommand::End) + .Default(MRICommand::Invalid); + + switch (Command) { + case MRICommand::AddMod: + MRIMembers.push_back(Rest); + break; + case MRICommand::Create: + Create = true; + if (!ArchiveName.empty()) + fail("Editing multiple archives not supported"); + if (Saved) + fail("File already saved"); + ArchiveName = Rest; + break; + case MRICommand::Save: + Saved = true; + break; + case MRICommand::End: + break; + case MRICommand::Invalid: + fail("Unknown command: " + CommandStr); + } + } + + // Nothing to do if not saved. + if (!Saved) + exit(0); + + for (auto &M : MRIMembers) + Members.push_back(M); + + return ReplaceOrInsert; } -// parseCommandLine - Parse the command line options as presented and return the -// operation specified. Process all modifiers and check to make sure that -// constraints on modifier/operation pairs have not been violated. +// Parse the command line options as presented and return the operation +// specified. Process all modifiers and check to make sure that constraints on +// modifier/operation pairs have not been violated. static ArchiveOperation parseCommandLine() { + if (MRI) { + if (!RestOfArgs.empty()) + fail("Cannot mix -M and other options"); + return parseMRIScript(); + } + + getOptions(); // Keep track of number of operations. We can only specify one // per execution. @@ -185,6 +253,8 @@ static ArchiveOperation parseCommandLine() { // Keep track of which operation was requested ArchiveOperation Operation; + bool MaybeJustCreateSymTab = false; + for(unsigned i=0; igetAccessMode(); + SmallString<128> Storage = Name; - int FD = open(Name.str().c_str(), OpenFlags, Mode); - if (FD < 0) - fail("Could not open output file"); + int FD; + failIfError( + sys::fs::openFileForWrite(Storage.c_str(), FD, sys::fs::F_None, Mode), + Storage.c_str()); { raw_fd_ostream file(FD, false); @@ -340,6 +417,7 @@ static bool shouldCreateArchive(ArchiveOperation Op) { case Move: case DisplayTable: case Extract: + case CreateSymTab: return false; case QuickAppend: @@ -352,11 +430,12 @@ static bool shouldCreateArchive(ArchiveOperation Op) { static void performReadOperation(ArchiveOperation Operation, object::Archive *OldArchive) { - for (object::Archive::child_iterator I = OldArchive->begin_children(), - E = OldArchive->end_children(); + for (object::Archive::child_iterator I = OldArchive->child_begin(), + E = OldArchive->child_end(); I != E; ++I) { - StringRef Name; - failIfError(I->getName(Name)); + ErrorOr NameOrErr = I->getName(); + failIfError(NameOrErr.getError()); + StringRef Name = NameOrErr.get(); if (!Members.empty() && std::find(Members.begin(), Members.end(), Name) == Members.end()) @@ -381,31 +460,39 @@ static void performReadOperation(ArchiveOperation Operation, namespace { class NewArchiveIterator { bool IsNewMember; - SmallString<16> MemberName; + StringRef Name; + object::Archive::child_iterator OldI; - std::vector::const_iterator NewI; + + StringRef NewFilename; + mutable int NewFD; + mutable sys::fs::file_status NewStatus; public: - NewArchiveIterator(object::Archive::child_iterator I, Twine Name); - NewArchiveIterator(std::vector::const_iterator I, Twine Name); + NewArchiveIterator(object::Archive::child_iterator I, StringRef Name); + NewArchiveIterator(StringRef I, StringRef Name); + NewArchiveIterator(); bool isNewMember() const; + StringRef getName() const; + object::Archive::child_iterator getOld() const; - const char *getNew() const; - StringRef getMemberName() const { return MemberName; } + + StringRef getNew() const; + int getFD() const; + const sys::fs::file_status &getStatus() const; }; } +NewArchiveIterator::NewArchiveIterator() {} + NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I, - Twine Name) - : IsNewMember(false), OldI(I) { - Name.toVector(MemberName); -} + StringRef Name) + : IsNewMember(false), Name(Name), OldI(I) {} -NewArchiveIterator::NewArchiveIterator( - std::vector::const_iterator I, Twine Name) - : IsNewMember(true), NewI(I) { - Name.toVector(MemberName); -} +NewArchiveIterator::NewArchiveIterator(StringRef NewFilename, StringRef Name) + : IsNewMember(true), Name(Name), NewFilename(NewFilename), NewFD(-1) {} + +StringRef NewArchiveIterator::getName() const { return Name; } bool NewArchiveIterator::isNewMember() const { return IsNewMember; } @@ -414,53 +501,118 @@ object::Archive::child_iterator NewArchiveIterator::getOld() const { return OldI; } -const char *NewArchiveIterator::getNew() const { +StringRef NewArchiveIterator::getNew() const { assert(IsNewMember); - return NewI->c_str(); + return NewFilename; +} + +int NewArchiveIterator::getFD() const { + assert(IsNewMember); + if (NewFD != -1) + return NewFD; + failIfError(sys::fs::openFileForRead(NewFilename, NewFD), NewFilename); + assert(NewFD != -1); + + failIfError(sys::fs::status(NewFD, NewStatus), NewFilename); + + // Opening a directory doesn't make sense. Let it fail. + // Linux cannot open directories with open(2), although + // cygwin and *bsd can. + if (NewStatus.type() == sys::fs::file_type::directory_file) + failIfError(make_error_code(errc::is_a_directory), NewFilename); + + return NewFD; +} + +const sys::fs::file_status &NewArchiveIterator::getStatus() const { + assert(IsNewMember); + assert(NewFD != -1 && "Must call getFD first"); + return NewStatus; } template -void addMember(std::vector &Members, - std::string &StringTable, T I, StringRef Name) { - if (Name.size() < 16) { - NewArchiveIterator NI(I, Twine(Name) + "/"); - Members.push_back(NI); - } else { - int MapIndex = StringTable.size(); - NewArchiveIterator NI(I, Twine("/") + Twine(MapIndex)); +void addMember(std::vector &Members, T I, StringRef Name, + int Pos = -1) { + NewArchiveIterator NI(I, Name); + if (Pos == -1) Members.push_back(NI); - StringTable += Name; - StringTable += "/\n"; - } + else + Members[Pos] = NI; } -namespace { -class HasName { - StringRef Name; - -public: - HasName(StringRef Name) : Name(Name) {} - bool operator()(StringRef Path) { return Name == sys::path::filename(Path); } +enum InsertAction { + IA_AddOldMember, + IA_AddNewMeber, + IA_Delete, + IA_MoveOldMember, + IA_MoveNewMember }; + +static InsertAction computeInsertAction(ArchiveOperation Operation, + object::Archive::child_iterator I, + StringRef Name, + std::vector::iterator &Pos) { + if (Operation == QuickAppend || Members.empty()) + return IA_AddOldMember; + + auto MI = + std::find_if(Members.begin(), Members.end(), [Name](StringRef Path) { + return Name == sys::path::filename(Path); + }); + + if (MI == Members.end()) + return IA_AddOldMember; + + Pos = MI; + + if (Operation == Delete) + return IA_Delete; + + if (Operation == Move) + return IA_MoveOldMember; + + if (Operation == ReplaceOrInsert) { + StringRef PosName = sys::path::filename(RelPos); + if (!OnlyUpdate) { + if (PosName.empty()) + return IA_AddNewMeber; + return IA_MoveNewMember; + } + + // We could try to optimize this to a fstat, but it is not a common + // operation. + sys::fs::file_status Status; + failIfError(sys::fs::status(*MI, Status), *MI); + if (Status.getLastModificationTime() < I->getLastModified()) { + if (PosName.empty()) + return IA_AddOldMember; + return IA_MoveOldMember; + } + + if (PosName.empty()) + return IA_AddNewMeber; + return IA_MoveNewMember; + } + llvm_unreachable("No such operation"); } // We have to walk this twice and computing it is not trivial, so creating an // explicit std::vector is actually fairly efficient. static std::vector computeNewArchiveMembers(ArchiveOperation Operation, - object::Archive *OldArchive, - std::string &StringTable) { + object::Archive *OldArchive) { std::vector Ret; std::vector Moved; int InsertPos = -1; StringRef PosName = sys::path::filename(RelPos); if (OldArchive) { - int Pos = 0; - for (object::Archive::child_iterator I = OldArchive->begin_children(), - E = OldArchive->end_children(); - I != E; ++I, ++Pos) { - StringRef Name; - failIfError(I->getName(Name)); + for (object::Archive::child_iterator I = OldArchive->child_begin(), + E = OldArchive->child_end(); + I != E; ++I) { + int Pos = Ret.size(); + ErrorOr NameOrErr = I->getName(); + failIfError(NameOrErr.getError()); + StringRef Name = NameOrErr.get(); if (Name == PosName) { assert(AddAfter || AddBefore); if (AddBefore) @@ -468,63 +620,189 @@ computeNewArchiveMembers(ArchiveOperation Operation, else InsertPos = Pos + 1; } - if (Operation != QuickAppend && !Members.empty()) { - std::vector::iterator MI = - std::find_if(Members.begin(), Members.end(), HasName(Name)); - if (MI != Members.end()) { - if (Operation == Move) { - addMember(Moved, StringTable, I, Name); - continue; - } - if (Operation != ReplaceOrInsert || !OnlyUpdate) - continue; - // Ignore if the file if it is older than the member. - sys::fs::file_status Status; - failIfError(sys::fs::status(*MI, Status)); - if (Status.getLastModificationTime() < I->getLastModified()) - Members.erase(MI); - else - continue; - } + + std::vector::iterator MemberI = Members.end(); + InsertAction Action = computeInsertAction(Operation, I, Name, MemberI); + switch (Action) { + case IA_AddOldMember: + addMember(Ret, I, Name); + break; + case IA_AddNewMeber: + addMember(Ret, *MemberI, Name); + break; + case IA_Delete: + break; + case IA_MoveOldMember: + addMember(Moved, I, Name); + break; + case IA_MoveNewMember: + addMember(Moved, *MemberI, Name); + break; } - addMember(Ret, StringTable, I, Name); + if (MemberI != Members.end()) + Members.erase(MemberI); } } if (Operation == Delete) return Ret; - if (Operation == Move) { - if (RelPos.empty()) { - Ret.insert(Ret.end(), Moved.begin(), Moved.end()); - return Ret; - } - if (InsertPos == -1) - fail("Insertion point not found"); - assert(unsigned(InsertPos) <= Ret.size()); - Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end()); - return Ret; - } + if (!RelPos.empty() && InsertPos == -1) + fail("Insertion point not found"); - for (std::vector::iterator I = Members.begin(), - E = Members.end(); - I != E; ++I) { - StringRef Name = sys::path::filename(*I); - addMember(Ret, StringTable, I, Name); + if (RelPos.empty()) + InsertPos = Ret.size(); + + assert(unsigned(InsertPos) <= Ret.size()); + Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end()); + + Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator()); + int Pos = InsertPos; + for (auto &Member : Members) { + StringRef Name = sys::path::filename(Member); + addMember(Ret, Member, Name, Pos); + ++Pos; } return Ret; } template -static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) { +static void printWithSpacePadding(raw_fd_ostream &OS, T Data, unsigned Size, + bool MayTruncate = false) { uint64_t OldPos = OS.tell(); OS << Data; unsigned SizeSoFar = OS.tell() - OldPos; - assert(Size >= SizeSoFar && "Data doesn't fit in Size"); - unsigned Remaining = Size - SizeSoFar; - for (unsigned I = 0; I < Remaining; ++I) - OS << ' '; + if (Size > SizeSoFar) { + unsigned Remaining = Size - SizeSoFar; + for (unsigned I = 0; I < Remaining; ++I) + OS << ' '; + } else if (Size < SizeSoFar) { + assert(MayTruncate && "Data doesn't fit in Size"); + // Some of the data this is used for (like UID) can be larger than the + // space available in the archive format. Truncate in that case. + OS.seek(OldPos + Size); + } +} + +static void print32BE(raw_fd_ostream &Out, unsigned Val) { + for (int I = 3; I >= 0; --I) { + char V = (Val >> (8 * I)) & 0xff; + Out << V; + } +} + +static void printRestOfMemberHeader(raw_fd_ostream &Out, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, + unsigned Size) { + printWithSpacePadding(Out, ModTime.toEpochTime(), 12); + printWithSpacePadding(Out, UID, 6, true); + printWithSpacePadding(Out, GID, 6, true); + printWithSpacePadding(Out, format("%o", Perms), 8); + printWithSpacePadding(Out, Size, 10); + Out << "`\n"; +} + +static void printMemberHeader(raw_fd_ostream &Out, StringRef Name, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, unsigned Size) { + printWithSpacePadding(Out, Twine(Name) + "/", 16); + printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); +} + +static void printMemberHeader(raw_fd_ostream &Out, unsigned NameOffset, + const sys::TimeValue &ModTime, unsigned UID, + unsigned GID, unsigned Perms, unsigned Size) { + Out << '/'; + printWithSpacePadding(Out, NameOffset, 15); + printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); +} + +static void writeStringTable(raw_fd_ostream &Out, + ArrayRef Members, + std::vector &StringMapIndexes) { + unsigned StartOffset = 0; + for (ArrayRef::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I) { + StringRef Name = I->getName(); + if (Name.size() < 16) + continue; + if (StartOffset == 0) { + printWithSpacePadding(Out, "//", 58); + Out << "`\n"; + StartOffset = Out.tell(); + } + StringMapIndexes.push_back(Out.tell() - StartOffset); + Out << Name << "/\n"; + } + if (StartOffset == 0) + return; + if (Out.tell() % 2) + Out << '\n'; + int Pos = Out.tell(); + Out.seek(StartOffset - 12); + printWithSpacePadding(Out, Pos - StartOffset, 10); + Out.seek(Pos); +} + +static void +writeSymbolTable(raw_fd_ostream &Out, ArrayRef Members, + ArrayRef Buffers, + std::vector> &MemberOffsetRefs) { + unsigned StartOffset = 0; + unsigned MemberNum = 0; + std::string NameBuf; + raw_string_ostream NameOS(NameBuf); + unsigned NumSyms = 0; + LLVMContext &Context = getGlobalContext(); + for (ArrayRef::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I, ++MemberNum) { + MemoryBufferRef MemberBuffer = Buffers[MemberNum]; + ErrorOr> ObjOrErr = + object::SymbolicFile::createSymbolicFile( + MemberBuffer, sys::fs::file_magic::unknown, &Context); + if (!ObjOrErr) + continue; // FIXME: check only for "not an object file" errors. + object::SymbolicFile &Obj = *ObjOrErr.get(); + + if (!StartOffset) { + printMemberHeader(Out, "", sys::TimeValue::now(), 0, 0, 0, 0); + StartOffset = Out.tell(); + print32BE(Out, 0); + } + + for (const object::BasicSymbolRef &S : Obj.symbols()) { + uint32_t Symflags = S.getFlags(); + if (Symflags & object::SymbolRef::SF_FormatSpecific) + continue; + if (!(Symflags & object::SymbolRef::SF_Global)) + continue; + if (Symflags & object::SymbolRef::SF_Undefined) + continue; + failIfError(S.printName(NameOS)); + NameOS << '\0'; + ++NumSyms; + MemberOffsetRefs.push_back(std::make_pair(Out.tell(), MemberNum)); + print32BE(Out, 0); + } + } + Out << NameOS.str(); + + if (StartOffset == 0) + return; + + if (Out.tell() % 2) + Out << '\0'; + + unsigned Pos = Out.tell(); + Out.seek(StartOffset - 12); + printWithSpacePadding(Out, Pos - StartOffset, 10); + Out.seek(StartOffset); + print32BE(Out, NumSyms); + Out.seek(Pos); } static void performWriteOperation(ArchiveOperation Operation, @@ -538,76 +816,115 @@ static void performWriteOperation(ArchiveOperation Operation, raw_fd_ostream &Out = Output.os(); Out << "!\n"; - std::string StringTable; std::vector NewMembers = - computeNewArchiveMembers(Operation, OldArchive, StringTable); - if (!StringTable.empty()) { - if (StringTable.size() % 2) - StringTable += '\n'; - printWithSpacePadding(Out, "//", 48); - printWithSpacePadding(Out, StringTable.size(), 10); - Out << "`\n"; - Out << StringTable; + computeNewArchiveMembers(Operation, OldArchive); + + std::vector > MemberOffsetRefs; + + std::vector> Buffers; + std::vector Members; + + for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) { + NewArchiveIterator &Member = NewMembers[I]; + MemoryBufferRef MemberRef; + + if (Member.isNewMember()) { + StringRef Filename = Member.getNew(); + int FD = Member.getFD(); + const sys::fs::file_status &Status = Member.getStatus(); + ErrorOr> MemberBufferOrErr = + MemoryBuffer::getOpenFile(FD, Filename, Status.getSize(), false); + failIfError(MemberBufferOrErr.getError(), Filename); + Buffers.push_back(std::move(MemberBufferOrErr.get())); + MemberRef = Buffers.back()->getMemBufferRef(); + } else { + object::Archive::child_iterator OldMember = Member.getOld(); + ErrorOr MemberBufferOrErr = + OldMember->getMemoryBufferRef(); + failIfError(MemberBufferOrErr.getError()); + MemberRef = MemberBufferOrErr.get(); + } + Members.push_back(MemberRef); + } + + if (Symtab) { + writeSymbolTable(Out, NewMembers, Members, MemberOffsetRefs); } + std::vector StringMapIndexes; + writeStringTable(Out, NewMembers, StringMapIndexes); + + std::vector >::iterator MemberRefsI = + MemberOffsetRefs.begin(); + + unsigned MemberNum = 0; + unsigned LongNameMemberNum = 0; for (std::vector::iterator I = NewMembers.begin(), E = NewMembers.end(); - I != E; ++I) { - StringRef Name = I->getMemberName(); - printWithSpacePadding(Out, Name, 16); + I != E; ++I, ++MemberNum) { + + unsigned Pos = Out.tell(); + while (MemberRefsI != MemberOffsetRefs.end() && + MemberRefsI->second == MemberNum) { + Out.seek(MemberRefsI->first); + print32BE(Out, Pos); + ++MemberRefsI; + } + Out.seek(Pos); + MemoryBufferRef File = Members[MemberNum]; if (I->isNewMember()) { - const char *FileName = I->getNew(); - - int OpenFlags = O_RDONLY; -#ifdef O_BINARY - OpenFlags |= O_BINARY; -#endif - int FD = ::open(FileName, OpenFlags); - if (FD == -1) - return failIfError(error_code(errno, posix_category()), FileName); - - sys::fs::file_status Status; - failIfError(sys::fs::status(FD, Status), FileName); - - OwningPtr File; - failIfError( - MemoryBuffer::getOpenFile(FD, FileName, File, Status.getSize()), - FileName); - - uint64_t secondsSinceEpoch = - Status.getLastModificationTime().toEpochTime(); - printWithSpacePadding(Out, secondsSinceEpoch, 12); - - printWithSpacePadding(Out, Status.getUser(), 6); - printWithSpacePadding(Out, Status.getGroup(), 6); - printWithSpacePadding(Out, format("%o", Status.permissions()), 8); - printWithSpacePadding(Out, Status.getSize(), 10); - Out << "`\n"; - - Out << File->getBuffer(); + StringRef FileName = I->getNew(); + const sys::fs::file_status &Status = I->getStatus(); + + StringRef Name = sys::path::filename(FileName); + if (Name.size() < 16) + printMemberHeader(Out, Name, Status.getLastModificationTime(), + Status.getUser(), Status.getGroup(), + Status.permissions(), Status.getSize()); + else + printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], + Status.getLastModificationTime(), Status.getUser(), + Status.getGroup(), Status.permissions(), + Status.getSize()); } else { object::Archive::child_iterator OldMember = I->getOld(); - - uint64_t secondsSinceEpoch = OldMember->getLastModified().toEpochTime(); - printWithSpacePadding(Out, secondsSinceEpoch, 12); - - printWithSpacePadding(Out, OldMember->getUID(), 6); - printWithSpacePadding(Out, OldMember->getGID(), 6); - printWithSpacePadding(Out, format("%o", OldMember->getAccessMode()), 8); - printWithSpacePadding(Out, OldMember->getSize(), 10); - Out << "`\n"; - - Out << OldMember->getBuffer(); + StringRef Name = I->getName(); + + if (Name.size() < 16) + printMemberHeader(Out, Name, OldMember->getLastModified(), + OldMember->getUID(), OldMember->getGID(), + OldMember->getAccessMode(), OldMember->getSize()); + else + printMemberHeader(Out, StringMapIndexes[LongNameMemberNum++], + OldMember->getLastModified(), OldMember->getUID(), + OldMember->getGID(), OldMember->getAccessMode(), + OldMember->getSize()); } + Out << File.getBuffer(); + if (Out.tell() % 2) Out << '\n'; } + Output.keep(); Out.close(); sys::fs::rename(TemporaryOutput, ArchiveName); - TemporaryOutput = NULL; + TemporaryOutput = nullptr; +} + +static void createSymbolTable(object::Archive *OldArchive) { + // When an archive is created or modified, if the s option is given, the + // resulting archive will have a current symbol table. If the S option + // is given, it will have no symbol table. + // In summary, we only need to update the symbol table if we have none. + // This is actually very common because of broken build systems that think + // they have to run ranlib. + if (OldArchive->hasSymbolTable()) + return; + + performWriteOperation(CreateSymTab, OldArchive); } static void performOperation(ArchiveOperation Operation, @@ -625,43 +942,29 @@ static void performOperation(ArchiveOperation Operation, case ReplaceOrInsert: performWriteOperation(Operation, OldArchive); return; + case CreateSymTab: + createSymbolTable(OldArchive); + return; } llvm_unreachable("Unknown operation."); } -// main - main program for llvm-ar .. see comments in the code -int main(int argc, char **argv) { - ToolName = argv[0]; - // Print a stack trace if we signal out. - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - // Have the command line options parsed and handle things - // like --help and --version. - cl::ParseCommandLineOptions(argc, argv, - "LLVM Archiver (llvm-ar)\n\n" - " This program archives bitcode files into single libraries\n" - ); - - // Do our own parsing of the command line because the CommandLine utility - // can't handle the grouped positional parameters without a dash. - ArchiveOperation Operation = parseCommandLine(); - +static int performOperation(ArchiveOperation Operation) { // Create or open the archive object. - OwningPtr Buf; - error_code EC = MemoryBuffer::getFile(ArchiveName, Buf, -1, false); - if (EC && EC != llvm::errc::no_such_file_or_directory) { - errs() << argv[0] << ": error opening '" << ArchiveName + ErrorOr> Buf = + MemoryBuffer::getFile(ArchiveName, -1, false); + std::error_code EC = Buf.getError(); + if (EC && EC != errc::no_such_file_or_directory) { + errs() << ToolName << ": error opening '" << ArchiveName << "': " << EC.message() << "!\n"; return 1; } if (!EC) { - object::Archive Archive(Buf.take(), EC); + object::Archive Archive(Buf.get()->getMemBufferRef(), EC); if (EC) { - errs() << argv[0] << ": error loading '" << ArchiveName + errs() << ToolName << ": error loading '" << ArchiveName << "': " << EC.message() << "!\n"; return 1; } @@ -669,17 +972,57 @@ int main(int argc, char **argv) { return 0; } - assert(EC == llvm::errc::no_such_file_or_directory); + assert(EC == errc::no_such_file_or_directory); if (!shouldCreateArchive(Operation)) { failIfError(EC, Twine("error loading '") + ArchiveName + "'"); } else { if (!Create) { // Produce a warning if we should and we're creating the archive - errs() << argv[0] << ": creating " << ArchiveName << "\n"; + errs() << ToolName << ": creating " << ArchiveName << "\n"; } } - performOperation(Operation, NULL); + performOperation(Operation, nullptr); return 0; } + +int ar_main(char **argv) { + // Do our own parsing of the command line because the CommandLine utility + // can't handle the grouped positional parameters without a dash. + ArchiveOperation Operation = parseCommandLine(); + return performOperation(Operation); +} + +int ranlib_main() { + if (RestOfArgs.size() != 1) + fail(ToolName + "takes just one archive as argument"); + ArchiveName = RestOfArgs[0]; + return performOperation(CreateSymTab); +} + +int main(int argc, char **argv) { + ToolName = argv[0]; + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Have the command line options parsed and handle things + // like --help and --version. + cl::ParseCommandLineOptions(argc, argv, + "LLVM Archiver (llvm-ar)\n\n" + " This program archives bitcode files into single libraries\n" + ); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + + StringRef Stem = sys::path::stem(ToolName); + if (Stem.find("ar") != StringRef::npos) + return ar_main(argv); + if (Stem.find("ranlib") != StringRef::npos) + return ranlib_main(); + fail("Not ranlib or ar!"); +}