[llvm] r186934 - Add an initial implementation of archive symbol table generation.

Rafael Espindola rafael.espindola at gmail.com
Tue Jul 23 03:47:01 PDT 2013


Author: rafael
Date: Tue Jul 23 05:47:01 2013
New Revision: 186934

URL: http://llvm.org/viewvc/llvm-project?rev=186934&view=rev
Log:
Add an initial implementation of archive symbol table generation.

The symbol table has forward references in the file. Instead of allocating
a temporary buffer or counting the size and then writing, this implementation
writes a dummy value first and patches it once the final value is known.

There is room for performance improvement. I will implement them as soon as I
get some other features (like a ranlib mode) in.

Added:
    llvm/trunk/test/Object/Inputs/trivial-object-test2.elf-x86-64
    llvm/trunk/test/Object/archive-symtab.test
Modified:
    llvm/trunk/tools/llvm-ar/llvm-ar.cpp

Added: llvm/trunk/test/Object/Inputs/trivial-object-test2.elf-x86-64
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Object/Inputs/trivial-object-test2.elf-x86-64?rev=186934&view=auto
==============================================================================
Binary files llvm/trunk/test/Object/Inputs/trivial-object-test2.elf-x86-64 (added) and llvm/trunk/test/Object/Inputs/trivial-object-test2.elf-x86-64 Tue Jul 23 05:47:01 2013 differ

Added: llvm/trunk/test/Object/archive-symtab.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Object/archive-symtab.test?rev=186934&view=auto
==============================================================================
--- llvm/trunk/test/Object/archive-symtab.test (added)
+++ llvm/trunk/test/Object/archive-symtab.test Tue Jul 23 05:47:01 2013
@@ -0,0 +1,18 @@
+RUN: rm -f %t.a
+RUN: llvm-ar rcs %t.a %p/Inputs/trivial-object-test.elf-x86-64 %p/Inputs/trivial-object-test2.elf-x86-64
+RUN: llvm-nm -s %t.a | FileCheck %s
+
+CHECK: Archive map
+CHECK-NEXT: main in trivial-object-test.elf-x86-64
+CHECK-NEXT: foo in trivial-object-test2.elf-x86-64
+CHECK-NEXT: main in trivial-object-test2.elf-x86-64
+CHECK-NOT: bar
+
+CHECK: trivial-object-test.elf-x86-64:
+CHECK-NEXT:         U SomeOtherFunction
+CHECK-NEXT: 00000000 T main
+CHECK-NEXT:         U puts
+CHECK-NEXT: trivial-object-test2.elf-x86-64:
+CHECK-NEXT: 00000000 t bar
+CHECK-NEXT: 00000006 T foo
+CHECK-NEXT: 00000016 T main

Modified: llvm/trunk/tools/llvm-ar/llvm-ar.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-ar/llvm-ar.cpp?rev=186934&r1=186933&r2=186934&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-ar/llvm-ar.cpp (original)
+++ llvm/trunk/tools/llvm-ar/llvm-ar.cpp Tue Jul 23 05:47:01 2013
@@ -15,6 +15,7 @@
 #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/FileSystem.h"
 #include "llvm/Support/Format.h"
@@ -121,6 +122,7 @@ static bool Create = false;        ///<
 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
@@ -196,8 +198,12 @@ static ArchiveOperation parseCommandLine
     case 'c': Create = true; break;
     case 'l': /* accepted but unused */ break;
     case 'o': OriginalDates = true; break;
-    case 's': break; // Ignore for now.
-    case 'S': break; // Ignore for now.
+    case 's':
+      Symtab = true;
+      break;
+    case 'S':
+      Symtab = false;
+      break;
     case 'u': OnlyUpdate = true; break;
     case 'v': Verbose = true; break;
     case 'a':
@@ -375,33 +381,31 @@ static void performReadOperation(Archive
 namespace {
 class NewArchiveIterator {
   bool IsNewMember;
-  SmallString<16> MemberName;
+  StringRef Name;
   object::Archive::child_iterator OldI;
   std::string NewFilename;
 
 public:
-  NewArchiveIterator(object::Archive::child_iterator I, Twine Name);
-  NewArchiveIterator(std::string *I, Twine Name);
+  NewArchiveIterator(object::Archive::child_iterator I, StringRef Name);
+  NewArchiveIterator(std::string *I, StringRef Name);
   NewArchiveIterator();
   bool isNewMember() const;
   object::Archive::child_iterator getOld() const;
   const char *getNew() const;
-  StringRef getMemberName() const { return MemberName; }
+  StringRef getName() 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::string *NewFilename, Twine Name)
-    : IsNewMember(true), NewFilename(*NewFilename) {
-  Name.toVector(MemberName);
-}
+NewArchiveIterator::NewArchiveIterator(std::string *NewFilename, StringRef Name)
+    : IsNewMember(true), Name(Name), NewFilename(*NewFilename) {}
+
+StringRef NewArchiveIterator::getName() const { return Name; }
 
 bool NewArchiveIterator::isNewMember() const { return IsNewMember; }
 
@@ -416,23 +420,13 @@ const char *NewArchiveIterator::getNew()
 }
 
 template <typename T>
-void addMember(std::vector<NewArchiveIterator> &Members,
-               std::string &StringTable, T I, StringRef Name, int Pos = -1) {
-  if (Pos == -1) {
-    Pos = Members.size();
-    Members.resize(Pos + 1);
-  }
-
-  if (Name.size() < 16) {
-    NewArchiveIterator NI(I, Twine(Name) + "/");
-    Members[Pos] = NI;
-  } else {
-    int MapIndex = StringTable.size();
-    NewArchiveIterator NI(I, Twine("/") + Twine(MapIndex));
+void addMember(std::vector<NewArchiveIterator> &Members, T I, StringRef Name,
+               int Pos = -1) {
+  NewArchiveIterator NI(I, Name);
+  if (Pos == -1)
+    Members.push_back(NI);
+  else
     Members[Pos] = NI;
-    StringTable += Name;
-    StringTable += "/\n";
-  }
 }
 
 namespace {
@@ -503,8 +497,7 @@ computeInsertAction(ArchiveOperation Ope
 // explicit std::vector is actually fairly efficient.
 static std::vector<NewArchiveIterator>
 computeNewArchiveMembers(ArchiveOperation Operation,
-                         object::Archive *OldArchive,
-                         std::string &StringTable) {
+                         object::Archive *OldArchive) {
   std::vector<NewArchiveIterator> Ret;
   std::vector<NewArchiveIterator> Moved;
   int InsertPos = -1;
@@ -528,18 +521,18 @@ computeNewArchiveMembers(ArchiveOperatio
       InsertAction Action = computeInsertAction(Operation, I, Name, MemberI);
       switch (Action) {
       case IA_AddOldMember:
-        addMember(Ret, StringTable, I, Name);
+        addMember(Ret, I, Name);
         break;
       case IA_AddNewMeber:
-        addMember(Ret, StringTable, &*MemberI, Name);
+        addMember(Ret, &*MemberI, Name);
         break;
       case IA_Delete:
         break;
       case IA_MoveOldMember:
-        addMember(Moved, StringTable, I, Name);
+        addMember(Moved, I, Name);
         break;
       case IA_MoveNewMember:
-        addMember(Moved, StringTable, &*MemberI, Name);
+        addMember(Moved, &*MemberI, Name);
         break;
       }
       if (MemberI != Members.end())
@@ -565,7 +558,7 @@ computeNewArchiveMembers(ArchiveOperatio
          E = Members.end();
        I != E; ++I, ++Pos) {
     StringRef Name = sys::path::filename(*I);
-    addMember(Ret, StringTable, &*I, Name, Pos);
+    addMember(Ret, &*I, Name, Pos);
   }
 
   return Ret;
@@ -582,6 +575,143 @@ static void printWithSpacePadding(raw_os
     OS << ' ';
 }
 
+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);
+  printWithSpacePadding(Out, GID, 6);
+  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<NewArchiveIterator> Members,
+                             std::vector<unsigned> &StringMapIndexes) {
+  unsigned StartOffset = 0;
+  for (ArrayRef<NewArchiveIterator>::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<NewArchiveIterator> Members,
+    std::vector<std::pair<unsigned, unsigned> > &MemberOffsetRefs) {
+  unsigned StartOffset = 0;
+  unsigned MemberNum = 0;
+  std::vector<StringRef> SymNames;
+  std::vector<OwningPtr<object::ObjectFile> > DeleteIt;
+  for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(),
+                                              E = Members.end();
+       I != E; ++I, ++MemberNum) {
+    object::ObjectFile *Obj;
+    if (I->isNewMember()) {
+      const char *Filename = I->getNew();
+      Obj = object::ObjectFile::createObjectFile(Filename);
+    } else {
+      object::Archive::child_iterator OldMember = I->getOld();
+      OwningPtr<object::Binary> Binary;
+      error_code EC = OldMember->getAsBinary(Binary);
+      if (EC) { // FIXME: check only for "not an object file" errors.
+        Obj = NULL;
+      } else {
+        Obj = dyn_cast<object::ObjectFile>(Binary.get());
+        if (Obj)
+          Binary.take();
+      }
+    }
+    if (!Obj)
+      continue;
+    DeleteIt.push_back(OwningPtr<object::ObjectFile>(Obj));
+    if (!StartOffset) {
+      printMemberHeader(Out, "", sys::TimeValue::now(), 0, 0, 0, 0);
+      StartOffset = Out.tell();
+      print32BE(Out, 0);
+    }
+
+    error_code Err;
+    for (object::symbol_iterator I = Obj->begin_symbols(),
+                                 E = Obj->end_symbols();
+         I != E; I.increment(Err), failIfError(Err)) {
+      uint32_t Symflags;
+      failIfError(I->getFlags(Symflags));
+      if (Symflags & object::SymbolRef::SF_FormatSpecific)
+        continue;
+      if (!(Symflags & object::SymbolRef::SF_Global))
+        continue;
+      if (Symflags & object::SymbolRef::SF_Undefined)
+        continue;
+      StringRef Name;
+      failIfError(I->getName(Name));
+      SymNames.push_back(Name);
+      MemberOffsetRefs.push_back(std::make_pair(Out.tell(), MemberNum));
+      print32BE(Out, 0);
+    }
+  }
+  for (std::vector<StringRef>::iterator I = SymNames.begin(),
+                                        E = SymNames.end();
+       I != E; ++I) {
+    Out << *I;
+    Out << '\0';
+  }
+
+  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, SymNames.size());
+  Out.seek(Pos);
+}
+
 static void performWriteOperation(ArchiveOperation Operation,
                                   object::Archive *OldArchive) {
   SmallString<128> TmpArchive;
@@ -593,23 +723,35 @@ static void performWriteOperation(Archiv
   raw_fd_ostream &Out = Output.os();
   Out << "!<arch>\n";
 
-  std::string StringTable;
   std::vector<NewArchiveIterator> 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<std::pair<unsigned, unsigned> > MemberOffsetRefs;
+
+  if (Symtab) {
+    writeSymbolTable(Out, NewMembers, MemberOffsetRefs);
   }
 
+  std::vector<unsigned> StringMapIndexes;
+  writeStringTable(Out, NewMembers, StringMapIndexes);
+
+  std::vector<std::pair<unsigned, unsigned> >::iterator MemberRefsI =
+      MemberOffsetRefs.begin();
+
+  unsigned MemberNum = 0;
+  unsigned LongNameMemberNum = 0;
   for (std::vector<NewArchiveIterator>::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);
 
     if (I->isNewMember()) {
       const char *FileName = I->getNew();
@@ -625,29 +767,30 @@ static void performWriteOperation(Archiv
           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";
-
+      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());
       Out << File->getBuffer();
     } else {
       object::Archive::child_iterator OldMember = I->getOld();
+      StringRef Name = I->getName();
 
-      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";
-
+      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 << OldMember->getBuffer();
     }
 





More information about the llvm-commits mailing list