Fix a FIXME about the format and add a test.
[oota-llvm.git] / lib / Object / Archive.cpp
index 5585bc44f7e7141fd63f5d934ef907cf27d7ad87..60c6d21f48237484e2b1493f1d04909844c2ec45 100644 (file)
@@ -54,20 +54,61 @@ StringRef ArchiveMemberHeader::getName() const {
   return llvm::StringRef(Name, end);
 }
 
-uint64_t ArchiveMemberHeader::getSize() const {
-  uint64_t ret;
-  if (llvm::StringRef(Size, sizeof(Size)).rtrim(" ").getAsInteger(10, ret))
-    llvm_unreachable("Size is not an integer.");
-  return ret;
+uint32_t ArchiveMemberHeader::getSize() const {
+  uint32_t Ret;
+  if (llvm::StringRef(Size, sizeof(Size)).rtrim(" ").getAsInteger(10, Ret))
+    llvm_unreachable("Size is not a decimal number.");
+  return Ret;
 }
 
-Archive::Child::Child(const Archive *p, StringRef d) : Parent(p), Data(d) {
-  if (!p || d.empty())
+sys::fs::perms ArchiveMemberHeader::getAccessMode() const {
+  unsigned Ret;
+  if (StringRef(AccessMode, sizeof(AccessMode)).rtrim(" ").getAsInteger(8, Ret))
+    llvm_unreachable("Access mode is not an octal number.");
+  return static_cast<sys::fs::perms>(Ret);
+}
+
+sys::TimeValue ArchiveMemberHeader::getLastModified() const {
+  unsigned Seconds;
+  if (StringRef(LastModified, sizeof(LastModified)).rtrim(" ")
+          .getAsInteger(10, Seconds))
+    llvm_unreachable("Last modified time not a decimal number.");
+
+  sys::TimeValue Ret;
+  Ret.fromEpochTime(Seconds);
+  return Ret;
+}
+
+unsigned ArchiveMemberHeader::getUID() const {
+  unsigned Ret;
+  if (StringRef(UID, sizeof(UID)).rtrim(" ").getAsInteger(10, Ret))
+    llvm_unreachable("UID time not a decimal number.");
+  return Ret;
+}
+
+unsigned ArchiveMemberHeader::getGID() const {
+  unsigned Ret;
+  if (StringRef(GID, sizeof(GID)).rtrim(" ").getAsInteger(10, Ret))
+    llvm_unreachable("GID time not a decimal number.");
+  return Ret;
+}
+
+static const ArchiveMemberHeader *toHeader(const char *base) {
+  return reinterpret_cast<const ArchiveMemberHeader *>(base);
+}
+
+Archive::Child::Child(const Archive *Parent, const char *Start)
+    : Parent(Parent) {
+  if (!Start)
     return;
+
+  const ArchiveMemberHeader *Header = toHeader(Start);
+  Data = StringRef(Start, sizeof(ArchiveMemberHeader) + Header->getSize());
+
   // Setup StartOfFile and PaddingBytes.
   StartOfFile = sizeof(ArchiveMemberHeader);
   // Don't include attached name.
-  StringRef Name = ToHeader(Data.data())->getName();
+  StringRef Name = Header->getName();
   if (Name.startswith("#1/")) {
     uint64_t NameSize;
     if (Name.substr(3).rtrim(" ").getAsInteger(10, NameSize))
@@ -86,11 +127,9 @@ Archive::Child Archive::Child::getNext() const {
 
   // Check to see if this is past the end of the archive.
   if (NextLoc >= Parent->Data->getBufferEnd())
-    return Child(Parent, StringRef(0, 0));
-
-  size_t NextSize = sizeof(ArchiveMemberHeader) + ToHeader(NextLoc)->getSize();
+    return Child(Parent, NULL);
 
-  return Child(Parent, StringRef(NextLoc, NextSize));
+  return Child(Parent, NextLoc);
 }
 
 error_code Archive::Child::getName(StringRef &Result) const {
@@ -171,7 +210,7 @@ error_code Archive::Child::getAsBinary(OwningPtr<Binary> &Result) const {
 }
 
 Archive::Archive(MemoryBuffer *source, error_code &ec)
-  : Binary(Binary::ID_Archive, source) {
+  : Binary(Binary::ID_Archive, source), SymbolTable(end_children()) {
   // Check for sufficient magic.
   if (!source || source->getBufferSize()
                  < (8 + sizeof(ArchiveMemberHeader)) // Smallest archive.
@@ -197,9 +236,9 @@ Archive::Archive(MemoryBuffer *source, error_code &ec)
   //  Second member : // (may exist, if it exists, points to the string table)
   //  Note : The string table is used if the filename exceeds 15 characters
   // BSD archive format
-  //  First member : __.SYMDEF (points to the symbol table)
-  //  There is no string table, if the filename exceeds 15 characters or has a 
-  //  embedded space, the filename has #1/<size>, The size represents the size 
+  //  First member : __.SYMDEF or "__.SYMDEF SORTED" (the symbol table)
+  //  There is no string table, if the filename exceeds 15 characters or has a
+  //  embedded space, the filename has #1/<size>, The size represents the size
   //  of the filename that needs to be read after the archive header
   // COFF archive format
   //  First member : /
@@ -217,6 +256,17 @@ Archive::Archive(MemoryBuffer *source, error_code &ec)
     return;
   }
 
+  if (Name.startswith("#1/")) {
+    Format = K_BSD;
+    // We know this is BSD, so getName will work since there is no string table.
+    ec = i->getName(Name);
+    if (ec)
+      return;
+    if (Name == StringRef("__.SYMDEF SORTED\0\0\0", 20))
+      SymbolTable = i;
+    return;
+  }
+
   if (Name == "/") {
     SymbolTable = i;
 
@@ -265,17 +315,15 @@ Archive::Archive(MemoryBuffer *source, error_code &ec)
 
 Archive::child_iterator Archive::begin_children(bool skip_internal) const {
   const char *Loc = Data->getBufferStart() + strlen(Magic);
-  size_t Size = sizeof(ArchiveMemberHeader) +
-    ToHeader(Loc)->getSize();
-  Child c(this, StringRef(Loc, Size));
+  Child c(this, Loc);
   // Skip internals at the beginning of an archive.
-  if (skip_internal && isInternalMember(*ToHeader(Loc)))
+  if (skip_internal && isInternalMember(*toHeader(Loc)))
     return c.getNext();
   return c;
 }
 
 Archive::child_iterator Archive::end_children() const {
-  return Child(this, StringRef(0, 0));
+  return Child(this, NULL);
 }
 
 error_code Archive::Symbol::getName(StringRef &Result) const {
@@ -323,9 +371,7 @@ error_code Archive::Symbol::getMember(child_iterator &Result) const {
   }
 
   const char *Loc = Parent->getData().begin() + Offset;
-  size_t Size = sizeof(ArchiveMemberHeader) +
-    ToHeader(Loc)->getSize();
-  Result = Child(Parent, StringRef(Loc, Size));
+  Result = Child(Parent, Loc);
 
   return object_error::success;
 }
@@ -340,6 +386,9 @@ Archive::Symbol Archive::Symbol::getNext() const {
 }
 
 Archive::symbol_iterator Archive::begin_symbols() const {
+  if (SymbolTable == end_children())
+    return symbol_iterator(Symbol(this, 0, 0));
+
   const char *buf = SymbolTable->getBuffer().begin();
   if (kind() == K_GNU) {
     uint32_t symbol_count = 0;
@@ -360,6 +409,9 @@ Archive::symbol_iterator Archive::begin_symbols() const {
 }
 
 Archive::symbol_iterator Archive::end_symbols() const {
+  if (SymbolTable == end_children())
+    return symbol_iterator(Symbol(this, 0, 0));
+
   const char *buf = SymbolTable->getBuffer().begin();
   uint32_t symbol_count = 0;
   if (kind() == K_GNU) {