diff --git a/include/llvm/Object/Archive.h b/include/llvm/Object/Archive.h index 0be0c1579d8..c0513a7c20f 100644 --- a/include/llvm/Object/Archive.h +++ b/include/llvm/Object/Archive.h @@ -174,7 +174,7 @@ public: return Format; } - child_iterator begin_children(bool skip_internal = true) const; + child_iterator begin_children(bool SkipInternal = true) const; child_iterator end_children() const; symbol_iterator begin_symbols() const; @@ -191,6 +191,7 @@ public: private: child_iterator SymbolTable; child_iterator StringTable; + child_iterator FirstRegular; Kind Format; }; diff --git a/lib/Object/Archive.cpp b/lib/Object/Archive.cpp index 7579a9ac98d..719d1042e5d 100644 --- a/lib/Object/Archive.cpp +++ b/lib/Object/Archive.cpp @@ -23,20 +23,6 @@ using namespace object; static const char *Magic = "!\n"; -static bool isInternalMember(const ArchiveMemberHeader &amh) { - static const char *const internals[] = { - "/", - "//" - }; - - StringRef name = amh.getName(); - for (std::size_t i = 0; i < sizeof(internals) / sizeof(*internals); ++i) { - if (name == internals[i]) - return true; - } - return false; -} - void Archive::anchor() { } StringRef ArchiveMemberHeader::getName() const { @@ -93,16 +79,13 @@ unsigned ArchiveMemberHeader::getGID() const { return Ret; } -static const ArchiveMemberHeader *toHeader(const char *base) { - return reinterpret_cast(base); -} - Archive::Child::Child(const Archive *Parent, const char *Start) : Parent(Parent) { if (!Start) return; - const ArchiveMemberHeader *Header = toHeader(Start); + const ArchiveMemberHeader *Header = + reinterpret_cast(Start); Data = StringRef(Start, sizeof(ArchiveMemberHeader) + Header->getSize()); // Setup StartOfFile and PaddingBytes. @@ -173,7 +156,8 @@ error_code Archive::Child::getName(StringRef &Result) const { uint64_t name_size; if (name.substr(3).rtrim(" ").getAsInteger(10, name_size)) llvm_unreachable("Long name length is not an ingeter"); - Result = Data.substr(sizeof(ArchiveMemberHeader), name_size); + Result = Data.substr(sizeof(ArchiveMemberHeader), name_size) + .rtrim(StringRef("\0", 1)); return object_error::success; } // It's a simple name. @@ -252,6 +236,8 @@ Archive::Archive(MemoryBuffer *source, error_code &ec) if (Name == "__.SYMDEF") { Format = K_BSD; SymbolTable = i; + ++i; + FirstRegular = i; ec = object_error::success; return; } @@ -262,8 +248,11 @@ Archive::Archive(MemoryBuffer *source, error_code &ec) ec = i->getName(Name); if (ec) return; - if (Name == StringRef("__.SYMDEF SORTED\0\0\0", 20)) + if (Name == "__.SYMDEF SORTED") { SymbolTable = i; + ++i; + } + FirstRegular = i; return; } @@ -281,12 +270,15 @@ Archive::Archive(MemoryBuffer *source, error_code &ec) if (Name == "//") { Format = K_GNU; StringTable = i; + ++i; + FirstRegular = i; ec = object_error::success; return; } if (Name[0] != '/') { Format = K_GNU; + FirstRegular = i; ec = object_error::success; return; } @@ -301,26 +293,31 @@ Archive::Archive(MemoryBuffer *source, error_code &ec) ++i; if (i == e) { + FirstRegular = i; ec = object_error::success; return; } Name = i->getRawName(); - if (Name == "//") + if (Name == "//") { StringTable = i; + ++i; + } + FirstRegular = i; ec = object_error::success; } -Archive::child_iterator Archive::begin_children(bool skip_internal) const { +Archive::child_iterator Archive::begin_children(bool SkipInternal) const { if (Data->getBufferSize() == 8) // empty archive. return end_children(); + + if (SkipInternal) + return FirstRegular; + const char *Loc = Data->getBufferStart() + strlen(Magic); Child c(this, Loc); - // Skip internals at the beginning of an archive. - if (skip_internal && isInternalMember(*toHeader(Loc))) - return c.getNext(); return c; } diff --git a/test/Object/archive-toc.test b/test/Object/archive-toc.test index b0cf585c0ef..0a5e72b61dc 100644 --- a/test/Object/archive-toc.test +++ b/test/Object/archive-toc.test @@ -10,6 +10,7 @@ GNU-NEXT: rw-r--r-- 500/500 2280 Nov 19 03:04 2004 IsNAN.o Test reading an archive createdy by Mac OS X ar RUN: env TZ=GMT llvm-ar tv %p/Inputs/MacOSX.a | FileCheck %s --check-prefix=OSX -strict-whitespace +OSX-NOT: __.SYMDEF OSX: rw-r--r-- 501/501 8 Nov 19 02:57 2004 evenlen OSX-NEXT: rw-r--r-- 501/501 8 Nov 19 02:57 2004 oddlen OSX-NEXT: rw-r--r-- 502/502 1465 Feb 4 06:59 2010 very_long_bytecode_file_name.bc diff --git a/test/Object/directory.ll b/test/Object/directory.ll index 336d218fd0f..48eefcb6ecb 100644 --- a/test/Object/directory.ll +++ b/test/Object/directory.ll @@ -1,12 +1,13 @@ +;RUN: rm -f %T/test.a ;RUN: not llvm-ar r %T/test.a . 2>&1 | FileCheck %s -;CHECK: . Is a directory +;CHECK: .: Is a directory ;RUN: rm -f %T/test.a ;RUN: touch %T/a-very-long-file-name ;RUN: llvm-ar r %T/test.a %s %T/a-very-long-file-name ;RUN: llvm-ar r %T/test.a %T/a-very-long-file-name -;RUN: llvm-ar t %T/test.a | sort | FileCheck -check-prefix=MEMBERS %s +;RUN: llvm-ar t %T/test.a | FileCheck -check-prefix=MEMBERS %s ;MEMBERS-NOT: / -;MEMBERS: a-very-long-file-name ;MEMBERS: directory.ll +;MEMBERS: a-very-long-file-name ;MEMBERS-NOT: a-very-long-file-name diff --git a/tools/llvm-ar/Archive.cpp b/tools/llvm-ar/Archive.cpp deleted file mode 100644 index 179e6e683e2..00000000000 --- a/tools/llvm-ar/Archive.cpp +++ /dev/null @@ -1,171 +0,0 @@ -//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file contains the implementation of the Archive and ArchiveMember -// classes that is common to both reading and writing archives.. -// -//===----------------------------------------------------------------------===// - -#include "Archive.h" -#include "ArchiveInternals.h" -#include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/system_error.h" -#include -#include -using namespace llvm; - -// getMemberSize - compute the actual physical size of the file member as seen -// on disk. This isn't the size of member's payload. Use getSize() for that. -unsigned -ArchiveMember::getMemberSize() const { - // Basically its the file size plus the header size - unsigned result = Size + sizeof(ArchiveMemberHeader); - - // If it has a long filename, include the name length - if (hasLongFilename()) - result += path.length() + 1; - - // If its now odd lengthed, include the padding byte - if (result % 2 != 0 ) - result++; - - return result; -} - -// This default constructor is only use by the ilist when it creates its -// sentry node. We give it specific static values to make it stand out a bit. -ArchiveMember::ArchiveMember() - : parent(0), path("--invalid--"), flags(0), data(0) -{ - User = sys::Process::GetCurrentUserId(); - Group = sys::Process::GetCurrentGroupId(); - Mode = 0777; - Size = 0; - ModTime = sys::TimeValue::now(); -} - -// This is the constructor that the Archive class uses when it is building or -// reading an archive. It just defaults a few things and ensures the parent is -// set for the iplist. The Archive class fills in the ArchiveMember's data. -// This is required because correctly setting the data may depend on other -// things in the Archive. -ArchiveMember::ArchiveMember(Archive* PAR) - : parent(PAR), path(), flags(0), data(0) -{ -} - -// This method allows an ArchiveMember to be replaced with the data for a -// different file, presumably as an update to the member. It also makes sure -// the flags are reset correctly. -bool ArchiveMember::replaceWith(StringRef newFile, std::string* ErrMsg) { - bool Exists; - if (sys::fs::exists(newFile.str(), Exists) || !Exists) { - if (ErrMsg) - *ErrMsg = "Can not replace an archive member with a non-existent file"; - return true; - } - - data = 0; - path = newFile.str(); - - // SVR4 symbol tables have an empty name - if (path == ARFILE_SVR4_SYMTAB_NAME) - flags |= SVR4SymbolTableFlag; - else - flags &= ~SVR4SymbolTableFlag; - - // BSD4.4 symbol tables have a special name - if (path == ARFILE_BSD4_SYMTAB_NAME) - flags |= BSD4SymbolTableFlag; - else - flags &= ~BSD4SymbolTableFlag; - - // String table name - if (path == ARFILE_STRTAB_NAME) - flags |= StringTableFlag; - else - flags &= ~StringTableFlag; - - // If it has a slash or its over 15 chars then its a long filename format - if (path.length() > 15) - flags |= HasLongFilenameFlag; - else - flags &= ~HasLongFilenameFlag; - - // Get the signature and status info - const char* signature = (const char*) data; - SmallString<4> magic; - if (!signature) { - sys::fs::get_magic(path, magic.capacity(), magic); - signature = magic.c_str(); - - sys::fs::file_status Status; - error_code EC = sys::fs::status(path, Status); - if (EC) - return true; - - User = Status.getUser(); - Group = Status.getGroup(); - Mode = Status.permissions(); - ModTime = Status.getLastModificationTime(); - - // FIXME: On posix this is a second stat. - EC = sys::fs::file_size(path, Size); - if (EC) - return true; - } - - return false; -} - -// Archive constructor - this is the only constructor that gets used for the -// Archive class. Everything else (default,copy) is deprecated. This just -// initializes and maps the file into memory, if requested. -Archive::Archive(StringRef filename, LLVMContext &C) - : archPath(filename), members(), mapfile(0), base(0), strtab(), - firstFileOffset(0), modules(), Context(C) {} - -bool -Archive::mapToMemory(std::string* ErrMsg) { - OwningPtr File; - if (error_code ec = MemoryBuffer::getFile(archPath.c_str(), File)) { - if (ErrMsg) - *ErrMsg = ec.message(); - return true; - } - mapfile = File.take(); - base = mapfile->getBufferStart(); - return false; -} - -void Archive::cleanUpMemory() { - // Shutdown the file mapping - delete mapfile; - mapfile = 0; - base = 0; - - firstFileOffset = 0; - - // Delete any Modules and ArchiveMember's we've allocated as a result of - // symbol table searches. - for (ModuleMap::iterator I=modules.begin(), E=modules.end(); I != E; ++I ) { - delete I->second.first; - delete I->second.second; - } -} - -// Archive destructor - just clean up memory -Archive::~Archive() { - cleanUpMemory(); -} - diff --git a/tools/llvm-ar/ArchiveInternals.h b/tools/llvm-ar/ArchiveInternals.h deleted file mode 100644 index a2a3077b10f..00000000000 --- a/tools/llvm-ar/ArchiveInternals.h +++ /dev/null @@ -1,75 +0,0 @@ -//===-- lib/Archive/ArchiveInternals.h -------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Internal implementation header for LLVM Archive files. -// -//===----------------------------------------------------------------------===// - -#ifndef TOOLS_LLVM_AR_ARCHIVEINTERNALS_H -#define TOOLS_LLVM_AR_ARCHIVEINTERNALS_H - -#include "Archive.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/TimeValue.h" -#include - -#define ARFILE_MAGIC "!\n" ///< magic string -#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1) ///< length of magic string -#define ARFILE_SVR4_SYMTAB_NAME "/ " ///< SVR4 symtab entry name -#define ARFILE_BSD4_SYMTAB_NAME "__.SYMDEF SORTED" ///< BSD4 symtab entry name -#define ARFILE_STRTAB_NAME "// " ///< Name of string table -#define ARFILE_PAD "\n" ///< inter-file align padding -#define ARFILE_MEMBER_MAGIC "`\n" ///< fmag field magic # - -namespace llvm { - - class LLVMContext; - - /// The ArchiveMemberHeader structure is used internally for bitcode - /// archives. - /// The header precedes each file member in the archive. This structure is - /// defined using character arrays for direct and correct interpretation - /// regardless of the endianess of the machine that produced it. - /// @brief Archive File Member Header - class ArchiveMemberHeader { - /// @name Data - /// @{ - public: - char name[16]; ///< Name of the file member. - char date[12]; ///< File date, decimal seconds since Epoch - char uid[6]; ///< user id in ASCII decimal - char gid[6]; ///< group id in ASCII decimal - char mode[8]; ///< file mode in ASCII octal - char size[10]; ///< file size in ASCII decimal - char fmag[2]; ///< Always contains ARFILE_MAGIC_TERMINATOR - - /// @} - /// @name Methods - /// @{ - public: - void init() { - memset(name,' ',16); - memset(date,' ',12); - memset(uid,' ',6); - memset(gid,' ',6); - memset(mode,' ',8); - memset(size,' ',10); - fmag[0] = '`'; - fmag[1] = '\n'; - } - - bool checkSignature() const { - return 0 == memcmp(fmag, ARFILE_MEMBER_MAGIC,2); - } - }; -} - -#endif - -// vim: sw=2 ai diff --git a/tools/llvm-ar/ArchiveReader.cpp b/tools/llvm-ar/ArchiveReader.cpp deleted file mode 100644 index a041cb881c3..00000000000 --- a/tools/llvm-ar/ArchiveReader.cpp +++ /dev/null @@ -1,317 +0,0 @@ -//===-- ArchiveReader.cpp - Read LLVM archive files -------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Builds up standard unix archive files (.a) containing LLVM bitcode. -// -//===----------------------------------------------------------------------===// - -#include "Archive.h" -#include "ArchiveInternals.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include -#include -using namespace llvm; - -/// Read a variable-bit-rate encoded unsigned integer -static inline unsigned readInteger(const char*&At, const char*End) { - unsigned Shift = 0; - unsigned Result = 0; - - do { - if (At == End) - return Result; - Result |= (unsigned)((*At++) & 0x7F) << Shift; - Shift += 7; - } while (At[-1] & 0x80); - return Result; -} - -// This member parses an ArchiveMemberHeader that is presumed to be pointed to -// by At. The At pointer is updated to the byte just after the header, which -// can be variable in size. -ArchiveMember* -Archive::parseMemberHeader(const char*& At, const char* End, std::string* error) -{ - if (At + sizeof(ArchiveMemberHeader) >= End) { - if (error) - *error = "Unexpected end of file"; - return 0; - } - - // Cast archive member header - const ArchiveMemberHeader* Hdr = (const ArchiveMemberHeader*)At; - At += sizeof(ArchiveMemberHeader); - - int flags = 0; - int MemberSize = atoi(Hdr->size); - assert(MemberSize >= 0); - - // Check the size of the member for sanity - if (At + MemberSize > End) { - if (error) - *error = "invalid member length in archive file"; - return 0; - } - - // Check the member signature - if (!Hdr->checkSignature()) { - if (error) - *error = "invalid file member signature"; - return 0; - } - - // Convert and check the member name - // The empty name ( '/' and 15 blanks) is for a foreign (non-LLVM) symbol - // table. The special name "//" and 14 blanks is for a string table, used - // for long file names. This library doesn't generate either of those but - // it will accept them. If the name starts with #1/ and the remainder is - // digits, then those digits specify the length of the name that is - // stored immediately following the header. Anything else is a regular, short - // filename that is terminated with a '/' and blanks. - - std::string pathname; - switch (Hdr->name[0]) { - case '#': - if (Hdr->name[1] == '1' && Hdr->name[2] == '/') { - if (isdigit(Hdr->name[3])) { - unsigned len = atoi(&Hdr->name[3]); - const char *nulp = (const char *)memchr(At, '\0', len); - pathname.assign(At, nulp != 0 ? (uintptr_t)(nulp - At) : len); - At += len; - MemberSize -= len; - flags |= ArchiveMember::HasLongFilenameFlag; - } else { - if (error) - *error = "invalid long filename"; - return 0; - } - } - break; - case '/': - if (Hdr->name[1]== '/') { - if (0 == memcmp(Hdr->name, ARFILE_STRTAB_NAME, 16)) { - pathname.assign(ARFILE_STRTAB_NAME); - flags |= ArchiveMember::StringTableFlag; - } else { - if (error) - *error = "invalid string table name"; - return 0; - } - } else if (Hdr->name[1] == ' ') { - if (0 == memcmp(Hdr->name, ARFILE_SVR4_SYMTAB_NAME, 16)) { - pathname.assign(ARFILE_SVR4_SYMTAB_NAME); - flags |= ArchiveMember::SVR4SymbolTableFlag; - } else { - if (error) - *error = "invalid SVR4 symbol table name"; - return 0; - } - } else if (isdigit(Hdr->name[1])) { - unsigned index = atoi(&Hdr->name[1]); - if (index < strtab.length()) { - const char* namep = strtab.c_str() + index; - const char* endp = strtab.c_str() + strtab.length(); - const char* p = namep; - const char* last_p = p; - while (p < endp) { - if (*p == '\n' && *last_p == '/') { - pathname.assign(namep, last_p - namep); - flags |= ArchiveMember::HasLongFilenameFlag; - break; - } - last_p = p; - p++; - } - if (p >= endp) { - if (error) - *error = "missing name terminator in string table"; - return 0; - } - } else { - if (error) - *error = "name index beyond string table"; - return 0; - } - } - break; - case '_': - if (Hdr->name[1] == '_' && - (0 == memcmp(Hdr->name, ARFILE_BSD4_SYMTAB_NAME, 16))) { - pathname.assign(ARFILE_BSD4_SYMTAB_NAME); - flags |= ArchiveMember::BSD4SymbolTableFlag; - break; - } - /* FALL THROUGH */ - - default: - const char* slash = (const char*) memchr(Hdr->name, '/', 16); - if (slash == 0) - slash = Hdr->name + 16; - pathname.assign(Hdr->name, slash - Hdr->name); - break; - } - - // Instantiate the ArchiveMember to be filled - ArchiveMember* member = new ArchiveMember(this); - - // Fill in fields of the ArchiveMember - member->parent = this; - member->path = pathname; - member->Size = MemberSize; - member->ModTime.fromEpochTime(atoi(Hdr->date)); - unsigned int mode; - sscanf(Hdr->mode, "%o", &mode); - member->Mode = mode; - member->User = atoi(Hdr->uid); - member->Group = atoi(Hdr->gid); - member->flags = flags; - member->data = At; - - return member; -} - -bool -Archive::checkSignature(std::string* error) { - // Check the magic string at file's header - if (mapfile->getBufferSize() < 8 || memcmp(base, ARFILE_MAGIC, 8)) { - if (error) - *error = "invalid signature for an archive file"; - return false; - } - return true; -} - -// This function loads the entire archive and fully populates its ilist with -// the members of the archive file. This is typically used in preparation for -// editing the contents of the archive. -bool -Archive::loadArchive(std::string* error) { - - // Set up parsing - members.clear(); - const char *At = base; - const char *End = mapfile->getBufferEnd(); - - if (!checkSignature(error)) - return false; - - At += 8; // Skip the magic string. - - bool foundFirstFile = false; - while (At < End) { - // parse the member header - const char* Save = At; - OwningPtr mbr(parseMemberHeader(At, End, error)); - if (!mbr) - return false; - - // check if this is the foreign symbol table - if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) { - At += mbr->getSize(); - if ((intptr_t(At) & 1) == 1) - At++; - } else if (mbr->isStringTable()) { - // Simply suck the entire string table into a string - // variable. This will be used to get the names of the - // members that use the "/ddd" format for their names - // (SVR4 style long names). - strtab.assign(At, mbr->getSize()); - At += mbr->getSize(); - if ((intptr_t(At) & 1) == 1) - At++; - } else { - // This is just a regular file. If its the first one, save its offset. - // Otherwise just push it on the list and move on to the next file. - if (!foundFirstFile) { - firstFileOffset = Save - base; - foundFirstFile = true; - } - At += mbr->getSize(); - members.push_back(mbr.take()); - if ((intptr_t(At) & 1) == 1) - At++; - } - } - return true; -} - -// Open and completely load the archive file. -Archive* -Archive::OpenAndLoad(StringRef File, LLVMContext& C, - std::string* ErrorMessage) { - OwningPtr result(new Archive(File, C)); - if (result->mapToMemory(ErrorMessage)) - return NULL; - if (!result->loadArchive(ErrorMessage)) - return NULL; - return result.take(); -} - -// Load just the symbol table from the archive file -bool -Archive::loadSymbolTable(std::string* ErrorMsg) { - - // Set up parsing - members.clear(); - const char *At = base; - const char *End = mapfile->getBufferEnd(); - - // Make sure we're dealing with an archive - if (!checkSignature(ErrorMsg)) - return false; - - At += 8; // Skip signature - - // Parse the first file member header - const char* FirstFile = At; - OwningPtr mbr(parseMemberHeader(At, End, ErrorMsg)); - if (!mbr) - return false; - - if (mbr->isSVR4SymbolTable() || mbr->isBSD4SymbolTable()) { - // Skip the foreign symbol table, we don't do anything with it - At += mbr->getSize(); - if ((intptr_t(At) & 1) == 1) - At++; - - // Read the next one - FirstFile = At; - mbr.reset(parseMemberHeader(At, End, ErrorMsg)); - if (!mbr) - return false; - } - - if (mbr->isStringTable()) { - // Process the string table entry - strtab.assign((const char*)mbr->getData(), mbr->getSize()); - At += mbr->getSize(); - if ((intptr_t(At) & 1) == 1) - At++; - - // Get the next one - FirstFile = At; - mbr.reset(parseMemberHeader(At, End, ErrorMsg)); - if (!mbr) - return false; - } - - // There's no symbol table in the file. We have to rebuild it from scratch - // because the intent of this method is to get the symbol table loaded so - // it can be searched efficiently. - // Add the member to the members list - members.push_back(mbr.take()); - - firstFileOffset = FirstFile - base; - return true; -} diff --git a/tools/llvm-ar/ArchiveWriter.cpp b/tools/llvm-ar/ArchiveWriter.cpp deleted file mode 100644 index bdccf3e0d91..00000000000 --- a/tools/llvm-ar/ArchiveWriter.cpp +++ /dev/null @@ -1,288 +0,0 @@ -//===-- ArchiveWriter.cpp - Write LLVM archive files ----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Builds up an LLVM archive file (.a) containing LLVM bitcode. -// -//===----------------------------------------------------------------------===// - -#include "Archive.h" -#include "ArchiveInternals.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/system_error.h" -#include -#include -#include -using namespace llvm; - -// Write an integer using variable bit rate encoding. This saves a few bytes -// per entry in the symbol table. -static inline void writeInteger(unsigned num, std::ofstream& ARFile) { - while (1) { - if (num < 0x80) { // done? - ARFile << (unsigned char)num; - return; - } - - // Nope, we are bigger than a character, output the next 7 bits and set the - // high bit to say that there is more coming... - ARFile << (unsigned char)(0x80 | ((unsigned char)num & 0x7F)); - num >>= 7; // Shift out 7 bits now... - } -} - -// Compute how many bytes are taken by a given VBR encoded value. This is needed -// to pre-compute the size of the symbol table. -static inline unsigned numVbrBytes(unsigned num) { - - // Note that the following nested ifs are somewhat equivalent to a binary - // search. We split it in half by comparing against 2^14 first. This allows - // most reasonable values to be done in 2 comparisons instead of 1 for - // small ones and four for large ones. We expect this to access file offsets - // in the 2^10 to 2^24 range and symbol lengths in the 2^0 to 2^8 range, - // so this approach is reasonable. - if (num < 1<<14) { - if (num < 1<<7) - return 1; - else - return 2; - } - if (num < 1<<21) - return 3; - - if (num < 1<<28) - return 4; - return 5; // anything >= 2^28 takes 5 bytes -} - -// Create an empty archive. -Archive* Archive::CreateEmpty(StringRef FilePath, LLVMContext& C) { - Archive* result = new Archive(FilePath, C); - return result; -} - -// Fill the ArchiveMemberHeader with the information from a member. If -// TruncateNames is true, names are flattened to 15 chars or less. The sz field -// is provided here instead of coming from the mbr because the member might be -// stored compressed and the compressed size is not the ArchiveMember's size. -// Furthermore compressed files have negative size fields to identify them as -// compressed. -bool -Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr, - int sz) const { - - // Set the permissions mode, uid and gid - hdr.init(); - char buffer[32]; - sprintf(buffer, "%-8o", mbr.getMode()); - memcpy(hdr.mode,buffer,8); - sprintf(buffer, "%-6u", mbr.getUser()); - memcpy(hdr.uid,buffer,6); - sprintf(buffer, "%-6u", mbr.getGroup()); - memcpy(hdr.gid,buffer,6); - - // Set the last modification date - uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime(); - sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch)); - memcpy(hdr.date,buffer,12); - - std::string mbrPath = sys::path::filename(mbr.getPath()); - - // Set the name field in one of its various flavors. - bool writeLongName = false; - if (mbr.isStringTable()) { - memcpy(hdr.name,ARFILE_STRTAB_NAME,16); - } else if (mbr.isSVR4SymbolTable()) { - memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16); - } else if (mbr.isBSD4SymbolTable()) { - memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16); - } else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) { - memcpy(hdr.name,mbrPath.c_str(),mbrPath.length()); - hdr.name[mbrPath.length()] = '/'; - } else { - std::string nm = "#1/"; - nm += utostr(mbrPath.length()); - memcpy(hdr.name,nm.data(),nm.length()); - if (sz < 0) - sz -= mbrPath.length(); - else - sz += mbrPath.length(); - writeLongName = true; - } - - // Set the size field - if (sz < 0) { - buffer[0] = '-'; - sprintf(&buffer[1],"%-9u",(unsigned)-sz); - } else { - sprintf(buffer, "%-10u", (unsigned)sz); - } - memcpy(hdr.size,buffer,10); - - return writeLongName; -} - -// Insert a file into the archive before some other member. This also takes care -// of extracting the necessary flags and information from the file. -bool Archive::addFileBefore(StringRef filePath, iterator where, - std::string *ErrMsg) { - if (!sys::fs::exists(filePath)) { - if (ErrMsg) - *ErrMsg = "Can not add a non-existent file to archive"; - return true; - } - - ArchiveMember* mbr = new ArchiveMember(this); - - mbr->data = 0; - mbr->path = filePath; - sys::fs::file_status Status; - error_code EC = sys::fs::status(filePath, Status); - if (EC) { - delete mbr; - return true; - } - mbr->User = Status.getUser(); - mbr->Group = Status.getGroup(); - mbr->Mode = Status.permissions(); - mbr->ModTime = Status.getLastModificationTime(); - // FIXME: On posix this is a second stat. - EC = sys::fs::file_size(filePath, mbr->Size); - if (EC) { - delete mbr; - return true; - } - - unsigned flags = 0; - if (sys::path::filename(filePath).size() > 15) - flags |= ArchiveMember::HasLongFilenameFlag; - - sys::fs::file_magic type; - if (sys::fs::identify_magic(mbr->path, type)) - type = sys::fs::file_magic::unknown; - mbr->flags = flags; - members.insert(where,mbr); - return false; -} - -// Write one member out to the file. -bool -Archive::writeMember( - const ArchiveMember& member, - raw_fd_ostream& ARFile, - std::string* ErrMsg -) { - - uint64_t filepos = ARFile.tell(); - filepos -= 8; - - // Get the data and its size either from the - // member's in-memory data or directly from the file. - size_t fSize = member.getSize(); - const char *data = (const char*)member.getData(); - MemoryBuffer *mFile = 0; - if (!data) { - OwningPtr File; - if (error_code ec = MemoryBuffer::getFile(member.getPath(), File)) { - if (ErrMsg) - *ErrMsg = ec.message(); - return true; - } - mFile = File.take(); - data = mFile->getBufferStart(); - fSize = mFile->getBufferSize(); - } - - int hdrSize = fSize; - - // Compute the fields of the header - ArchiveMemberHeader Hdr; - bool writeLongName = fillHeader(member,Hdr,hdrSize); - - // Write header to archive file - ARFile.write((char*)&Hdr, sizeof(Hdr)); - - // Write the long filename if its long - if (writeLongName) { - StringRef Name = sys::path::filename(member.getPath()); - ARFile.write(Name.data(), Name.size()); - } - - // Write the (possibly compressed) member's content to the file. - ARFile.write(data,fSize); - - // Make sure the member is an even length - if ((ARFile.tell() & 1) == 1) - ARFile << ARFILE_PAD; - - // Close the mapped file if it was opened - delete mFile; - return false; -} - -// Write the entire archive to the file specified when the archive was created. -// This writes to a temporary file first. Options are for creating a symbol -// table, flattening the file names (no directories, 15 chars max) and -// compressing each archive member. -bool Archive::writeToDisk(std::string *ErrMsg) { - // Make sure they haven't opened up the file, not loaded it, - // but are now trying to write it which would wipe out the file. - if (members.empty() && mapfile && mapfile->getBufferSize() > 8) { - if (ErrMsg) - *ErrMsg = "Can't write an archive not opened for writing"; - return true; - } - - // Create a temporary file to store the archive in - int TmpArchiveFD; - SmallString<128> TmpArchive; - error_code EC = sys::fs::createUniqueFile( - archPath + ".temp-archive-%%%%%%%.a", TmpArchiveFD, TmpArchive); - if (EC) - return true; - - // Make sure the temporary gets removed if we crash - sys::RemoveFileOnSignal(TmpArchive); - - // Create archive file for output. - raw_fd_ostream ArchiveFile(TmpArchiveFD, true); - - // Write magic string to archive. - ArchiveFile << ARFILE_MAGIC; - - // Loop over all member files, and write them out. Note that this also - // builds the symbol table, symTab. - for (MembersList::iterator I = begin(), E = end(); I != E; ++I) { - if (writeMember(*I, ArchiveFile, ErrMsg)) { - sys::fs::remove(Twine(TmpArchive)); - ArchiveFile.close(); - return true; - } - } - - // Close archive file. - ArchiveFile.close(); - - // Before we replace the actual archive, we need to forget all the - // members, since they point to data in that old archive. We need to do - // this because we cannot replace an open file on Windows. - cleanUpMemory(); - - if (sys::fs::rename(Twine(TmpArchive), archPath)) { - *ErrMsg = EC.message(); - return true; - } - - return false; -} diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt index 68095df7142..503999c2629 100644 --- a/tools/llvm-ar/CMakeLists.txt +++ b/tools/llvm-ar/CMakeLists.txt @@ -1,10 +1,7 @@ -set(LLVM_LINK_COMPONENTS support bitreader) +set(LLVM_LINK_COMPONENTS support object bitreader) add_llvm_tool(llvm-ar llvm-ar.cpp - ArchiveWriter.cpp - ArchiveReader.cpp - Archive.cpp ) # TODO: Support check-local. diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index c4b06a8854c..34663c250a5 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -12,15 +12,17 @@ // //===----------------------------------------------------------------------===// -#include "Archive.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Object/Archive.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -35,6 +37,29 @@ using namespace llvm; +// The name this program was invoked as. +static StringRef ToolName; + +static const char *TemporaryOutput; + +// fail - Show the error message and exit. +LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { + outs() << ToolName << ": " << Error << ".\n"; + if (TemporaryOutput) + sys::fs::remove(TemporaryOutput); + exit(1); +} + +static void failIfError(error_code EC, Twine Context = "") { + if (!EC) + return; + + std::string ContextStr = Context.str(); + if (ContextStr == "") + fail(EC.message()); + 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, @@ -109,32 +134,11 @@ static std::string ArchiveName; // on the command line. static std::vector Members; -// This variable holds the (possibly expanded) list of path objects that -// correspond to files we will -static std::set Paths; - -// The Archive object to which all the editing operations will be sent. -static Archive *TheArchive = 0; - -// The name this program was invoked as. -static const char *program_name; - // show_help - Show the error message, the help message and exit. LLVM_ATTRIBUTE_NORETURN static void show_help(const std::string &msg) { - errs() << program_name << ": " << msg << "\n\n"; + errs() << ToolName << ": " << msg << "\n\n"; cl::PrintHelpMessage(); - if (TheArchive) - delete TheArchive; - std::exit(1); -} - -// fail - Show the error message and exit. -LLVM_ATTRIBUTE_NORETURN static void -fail(const std::string &msg) { - errs() << program_name << ": " << msg << "\n\n"; - if (TheArchive) - delete TheArchive; std::exit(1); } @@ -243,53 +247,14 @@ static ArchiveOperation parseCommandLine() { return Operation; } -// buildPaths - Convert the strings in the Members vector to sys::Path objects -// and make sure they are valid and exist exist. This check is only needed for -// the operations that add/replace files to the archive ('q' and 'r') -static bool buildPaths(bool checkExistence, std::string *ErrMsg) { - for (unsigned i = 0; i < Members.size(); i++) { - std::string aPath = Members[i]; - if (checkExistence) { - bool IsDirectory; - error_code EC = sys::fs::is_directory(aPath, IsDirectory); - if (EC) - fail(aPath + ": " + EC.message()); - if (IsDirectory) - fail(aPath + " Is a directory"); +// Implements the 'p' operation. This function traverses the archive +// looking for members that match the path list. +static void doPrint(StringRef Name, object::Archive::child_iterator I) { + if (Verbose) + outs() << "Printing " << Name << "\n"; - Paths.insert(aPath); - } else { - Paths.insert(aPath); - } - } - return false; -} - -// doPrint - Implements the 'p' operation. This function traverses the archive -// looking for members that match the path list. It is careful to uncompress -// things that should be and to skip bitcode files unless the 'k' modifier was -// given. -static bool doPrint(std::string *ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E; ++I ) { - if (Paths.empty() || - (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { - const char *data = reinterpret_cast(I->getData()); - - // Skip things that don't make sense to print - if (I->isSVR4SymbolTable() || I->isBSD4SymbolTable()) - continue; - - if (Verbose) - outs() << "Printing " << I->getPath().str() << "\n"; - - unsigned len = I->getSize(); - outs().write(data, len); - } - } - return false; + StringRef Data = I->getBuffer(); + outs().write(Data.data(), Data.size()); } // putMode - utility function for printing out the file mode when the 't' @@ -309,265 +274,60 @@ static void printMode(unsigned mode) { outs() << "-"; } -// doDisplayTable - Implement the 't' operation. This function prints out just +// Implement the 't' operation. This function prints out just // the file names of each of the members. However, if verbose mode is requested // ('v' modifier) then the file type, permission mode, user, group, size, and // modification time are also printed. -static bool doDisplayTable(std::string *ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E; ++I ) { - if (Paths.empty() || - (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { - if (Verbose) { - unsigned mode = I->getMode(); - printMode((mode >> 6) & 007); - printMode((mode >> 3) & 007); - printMode(mode & 007); - outs() << ' ' << I->getUser(); - outs() << "/" << I->getGroup(); - outs() << ' ' << format("%6llu", I->getSize()); - sys::TimeValue ModTime = I->getModTime(); - outs() << " " << ModTime.str(); - outs() << " " << I->getPath().str() << "\n"; - } else { - outs() << I->getPath().str() << "\n"; - } - } +static void doDisplayTable(StringRef Name, object::Archive::child_iterator I) { + if (Verbose) { + sys::fs::perms Mode = I->getAccessMode(); + printMode((Mode >> 6) & 007); + printMode((Mode >> 3) & 007); + printMode(Mode & 007); + outs() << ' ' << I->getUID(); + outs() << '/' << I->getGID(); + outs() << ' ' << format("%6llu", I->getSize()); + outs() << ' ' << I->getLastModified().str(); + outs() << ' '; } - return false; + outs() << Name << "\n"; } -// doExtract - Implement the 'x' operation. This function extracts files back to -// the file system. -static bool doExtract(std::string *ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E; ++I ) { - if (Paths.empty() || - (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end())) { - - // Open up a file stream for writing - int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT; +// Implement the 'x' operation. This function extracts files back to the file +// system. +static void doExtract(StringRef Name, object::Archive::child_iterator I) { + // Open up a file stream for writing + // FIXME: we should abstract this, O_BINARY in particular. + int OpenFlags = O_TRUNC | O_WRONLY | O_CREAT; #ifdef O_BINARY - OpenFlags |= O_BINARY; + OpenFlags |= O_BINARY; #endif - // Retain the original mode. - sys::fs::perms Mode = sys::fs::perms(I->getMode()); + // Retain the original mode. + sys::fs::perms Mode = I->getAccessMode(); - int FD = open(I->getPath().str().c_str(), OpenFlags, Mode); - if (FD < 0) - return true; + int FD = open(Name.str().c_str(), OpenFlags, Mode); + if (FD < 0) + fail("Could not open output file"); - { - raw_fd_ostream file(FD, false); + { + raw_fd_ostream file(FD, false); - // Get the data and its length - const char* data = reinterpret_cast(I->getData()); - unsigned len = I->getSize(); + // Get the data and its length + StringRef Data = I->getBuffer(); - // Write the data. - file.write(data, len); - } - - // If we're supposed to retain the original modification times, etc. do so - // now. - if (OriginalDates) { - error_code EC = - sys::fs::setLastModificationAndAccessTime(FD, I->getModTime()); - if (EC) - fail(EC.message()); - } - if (close(FD)) - return true; - } - } - return false; -} - -// doDelete - Implement the delete operation. This function deletes zero or more -// members from the archive. Note that if the count is specified, there should -// be no more than one path in the Paths list or else this algorithm breaks. -// That check is enforced in parseCommandLine (above). -static bool doDelete(std::string *ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - if (Paths.empty()) - return false; - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E; ) { - if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) { - Archive::iterator J = I; - ++I; - TheArchive->erase(J); - } else { - ++I; - } + // Write the data. + file.write(Data.data(), Data.size()); } - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(ErrMsg)) - return true; - return false; -} + // If we're supposed to retain the original modification times, etc. do so + // now. + if (OriginalDates) + failIfError( + sys::fs::setLastModificationAndAccessTime(FD, I->getLastModified())); -// doMore - Implement the move operation. This function re-arranges just the -// order of the archive members so that when the archive is written the move -// of the members is accomplished. Note the use of the RelPos variable to -// determine where the items should be moved to. -static bool doMove(std::string *ErrMsg) { - if (buildPaths(false, ErrMsg)) - return true; - - // By default and convention the place to move members to is the end of the - // archive. - Archive::iterator moveto_spot = TheArchive->end(); - - // However, if the relative positioning modifiers were used, we need to scan - // the archive to find the member in question. If we don't find it, its no - // crime, we just move to the end. - if (AddBefore || AddAfter) { - for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); - I != E; ++I ) { - if (RelPos == I->getPath().str()) { - if (AddAfter) { - moveto_spot = I; - moveto_spot++; - } else { - moveto_spot = I; - } - break; - } - } - } - - // Keep a list of the paths remaining to be moved - std::set remaining(Paths); - - // Scan the archive again, this time looking for the members to move to the - // moveto_spot. - for (Archive::iterator I = TheArchive->begin(), E= TheArchive->end(); - I != E && !remaining.empty(); ++I ) { - std::set::iterator found = - std::find(remaining.begin(),remaining.end(), I->getPath()); - if (found != remaining.end()) { - if (I != moveto_spot) - TheArchive->splice(moveto_spot,*TheArchive,I); - remaining.erase(found); - } - } - - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(ErrMsg)) - return true; - return false; -} - -// doQuickAppend - Implements the 'q' operation. This function just -// indiscriminantly adds the members to the archive and rebuilds it. -static bool doQuickAppend(std::string *ErrMsg) { - // Get the list of paths to append. - if (buildPaths(true, ErrMsg)) - return true; - if (Paths.empty()) - return false; - - // Append them quickly. - for (std::set::iterator PI = Paths.begin(), PE = Paths.end(); - PI != PE; ++PI) { - if (TheArchive->addFileBefore(*PI, TheArchive->end(), ErrMsg)) - return true; - } - - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(ErrMsg)) - return true; - return false; -} - -// doReplaceOrInsert - Implements the 'r' operation. This function will replace -// any existing files or insert new ones into the archive. -static bool doReplaceOrInsert(std::string *ErrMsg) { - - // Build the list of files to be added/replaced. - if (buildPaths(true, ErrMsg)) - return true; - if (Paths.empty()) - return false; - - // Keep track of the paths that remain to be inserted. - std::set remaining(Paths); - - // Default the insertion spot to the end of the archive - Archive::iterator insert_spot = TheArchive->end(); - - // Iterate over the archive contents - for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end(); - I != E && !remaining.empty(); ++I ) { - - // Determine if this archive member matches one of the paths we're trying - // to replace. - - std::set::iterator found = remaining.end(); - for (std::set::iterator RI = remaining.begin(), - RE = remaining.end(); RI != RE; ++RI ) { - std::string compare(sys::path::filename(*RI)); - if (compare == I->getPath().str()) { - found = RI; - break; - } - } - - if (found != remaining.end()) { - sys::fs::file_status Status; - error_code EC = sys::fs::status(*found, Status); - if (EC) - return true; - if (!sys::fs::is_directory(Status)) { - if (OnlyUpdate) { - // Replace the item only if it is newer. - if (Status.getLastModificationTime() > I->getModTime()) - if (I->replaceWith(*found, ErrMsg)) - return true; - } else { - // Replace the item regardless of time stamp - if (I->replaceWith(*found, ErrMsg)) - return true; - } - } else { - // We purposefully ignore directories. - } - - // Remove it from our "to do" list - remaining.erase(found); - } - - // Determine if this is the place where we should insert - if (AddBefore && RelPos == I->getPath().str()) - insert_spot = I; - else if (AddAfter && RelPos == I->getPath().str()) { - insert_spot = I; - insert_spot++; - } - } - - // If we didn't replace all the members, some will remain and need to be - // inserted at the previously computed insert-spot. - if (!remaining.empty()) { - for (std::set::iterator PI = remaining.begin(), - PE = remaining.end(); PI != PE; ++PI) { - if (TheArchive->addFileBefore(*PI, insert_spot, ErrMsg)) - return true; - } - } - - // We're done editting, reconstruct the archive. - if (TheArchive->writeToDisk(ErrMsg)) - return true; - return false; + if (close(FD)) + fail("Could not close the file"); } static bool shouldCreateArchive(ArchiveOperation Op) { @@ -587,13 +347,283 @@ static bool shouldCreateArchive(ArchiveOperation Op) { llvm_unreachable("Missing entry in covered switch."); } +static void performReadOperation(ArchiveOperation Operation, + object::Archive *OldArchive) { + for (object::Archive::child_iterator I = OldArchive->begin_children(), + E = OldArchive->end_children(); + I != E; ++I) { + StringRef Name; + failIfError(I->getName(Name)); + + if (!Members.empty() && + std::find(Members.begin(), Members.end(), Name) == Members.end()) + continue; + + switch (Operation) { + default: + llvm_unreachable("Not a read operation"); + case Print: + doPrint(Name, I); + break; + case DisplayTable: + doDisplayTable(Name, I); + break; + case Extract: + doExtract(Name, I); + break; + } + } +} + +namespace { +class NewArchiveIterator { + bool IsNewMember; + SmallString<16> MemberName; + union { + object::Archive::child_iterator OldI; + std::vector::const_iterator NewI; + }; + +public: + NewArchiveIterator(object::Archive::child_iterator I, Twine Name); + NewArchiveIterator(std::vector::const_iterator I, Twine Name); + bool isNewMember() const; + object::Archive::child_iterator getOld() const; + StringRef getNew() const; + StringRef getMemberName() const { return MemberName; } +}; +} + +NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I, + Twine Name) + : IsNewMember(false), OldI(I) { + Name.toVector(MemberName); +} + +NewArchiveIterator::NewArchiveIterator( + std::vector::const_iterator I, Twine Name) + : IsNewMember(true), NewI(I) { + Name.toVector(MemberName); +} + +bool NewArchiveIterator::isNewMember() const { return IsNewMember; } + +object::Archive::child_iterator NewArchiveIterator::getOld() const { + assert(!IsNewMember); + return OldI; +} + +StringRef NewArchiveIterator::getNew() const { + assert(IsNewMember); + return *NewI; +} + +template +void addMember(std::vector &Members, + std::string &StringTable, T I, StringRef Name) { + if (Name.size() < 15) { + NewArchiveIterator NI(I, Twine(Name) + "/"); + Members.push_back(NI); + } else { + int MapIndex = StringTable.size(); + NewArchiveIterator NI(I, Twine("/") + Twine(MapIndex)); + Members.push_back(NI); + StringTable += Name; + StringTable += "/\n"; + } +} + +namespace { +class HasName { + StringRef Name; + +public: + HasName(StringRef Name) : Name(Name) {} + bool operator()(StringRef Path) { return Name == sys::path::filename(Path); } +}; +} + +// 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) { + 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)); + if (Name == PosName) { + assert(AddAfter || AddBefore); + if (AddBefore) + InsertPos = Pos; + 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; + } + } + addMember(Ret, StringTable, I, Name); + } + } + + 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; + } + + for (std::vector::iterator I = Members.begin(), + E = Members.end(); + I != E; ++I) { + StringRef Name = sys::path::filename(*I); + addMember(Ret, StringTable, I, Name); + } + + return Ret; +} + +template +static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) { + 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 << ' '; +} + +static void performWriteOperation(ArchiveOperation Operation, + object::Archive *OldArchive) { + int TmpArchiveFD; + SmallString<128> TmpArchive; + failIfError(sys::fs::createUniqueFile(ArchiveName + ".temp-archive-%%%%%%%.a", + TmpArchiveFD, TmpArchive)); + + TemporaryOutput = TmpArchive.c_str(); + tool_output_file Output(TemporaryOutput, TmpArchiveFD); + 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; + } + + for (std::vector::iterator I = NewMembers.begin(), + E = NewMembers.end(); + I != E; ++I) { + StringRef Name = I->getMemberName(); + printWithSpacePadding(Out, Name, 16); + + if (I->isNewMember()) { + // FIXME: we do a stat + open. We should do a open + fstat. + StringRef FileName = I->getNew(); + sys::fs::file_status Status; + failIfError(sys::fs::status(FileName, Status), 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"; + + OwningPtr File; + failIfError(MemoryBuffer::getFile(FileName, File), FileName); + Out << File->getBuffer(); + } 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(); + } + + if (Out.tell() % 2) + Out << '\n'; + } + Output.keep(); + Out.close(); + sys::fs::rename(TemporaryOutput, ArchiveName); + TemporaryOutput = NULL; +} + +static void performOperation(ArchiveOperation Operation, + object::Archive *OldArchive) { + switch (Operation) { + case Print: + case DisplayTable: + case Extract: + performReadOperation(Operation, OldArchive); + return; + + case Delete: + case Move: + case QuickAppend: + case ReplaceOrInsert: + performWriteOperation(Operation, OldArchive); + return; + } + llvm_unreachable("Unknown operation."); +} + // main - main program for llvm-ar .. see comments in the code int main(int argc, char **argv) { - program_name = argv[0]; + ToolName = argv[0]; // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); PrettyStackTraceProgram X(argc, argv); - LLVMContext &Context = getGlobalContext(); llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. // Have the command line options parsed and handle things @@ -603,54 +633,42 @@ int main(int argc, char **argv) { " This program archives bitcode files into single libraries\n" ); - int exitCode = 0; - // 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(); // Create or open the archive object. - if (shouldCreateArchive(Operation) && !llvm::sys::fs::exists(ArchiveName)) { - // Produce a warning if we should and we're creating the archive - if (!Create) - errs() << argv[0] << ": creating " << ArchiveName << "\n"; - TheArchive = Archive::CreateEmpty(ArchiveName, Context); - TheArchive->writeToDisk(); - } - - if (!TheArchive) { - std::string Error; - TheArchive = Archive::OpenAndLoad(ArchiveName, Context, &Error); - if (TheArchive == 0) { - errs() << argv[0] << ": error loading '" << ArchiveName << "': " - << Error << "!\n"; - return 1; - } - } - - // Make sure we're not fooling ourselves. - assert(TheArchive && "Unable to instantiate the archive"); - - // Perform the operation - std::string ErrMsg; - bool haveError = false; - switch (Operation) { - case Print: haveError = doPrint(&ErrMsg); break; - case Delete: haveError = doDelete(&ErrMsg); break; - case Move: haveError = doMove(&ErrMsg); break; - case QuickAppend: haveError = doQuickAppend(&ErrMsg); break; - case ReplaceOrInsert: haveError = doReplaceOrInsert(&ErrMsg); break; - case DisplayTable: haveError = doDisplayTable(&ErrMsg); break; - case Extract: haveError = doExtract(&ErrMsg); break; - } - if (haveError) { - errs() << argv[0] << ": " << ErrMsg << "\n"; + 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 + << "': " << EC.message() << "!\n"; return 1; } - delete TheArchive; - TheArchive = 0; + if (!EC) { + object::Archive Archive(Buf.take(), EC); - // Return result code back to operating system. - return exitCode; + if (EC) { + errs() << argv[0] << ": error loading '" << ArchiveName + << "': " << EC.message() << "!\n"; + return 1; + } + performOperation(Operation, &Archive); + return 0; + } + + assert(EC == llvm::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"; + } + } + + performOperation(Operation, NULL); + return 0; }