[llvm-commits] [lld] Support reading archive libraries

Shankar Easwaran shankare at codeaurora.org
Thu Nov 1 12:33:21 PDT 2012


Hi,

Please review the patch to add reading archive libraries in lld.

  * added support in lld core to read archive libraries
  * changed llvm lib to support reading GNU style archives
  * added a new flag in llvm-nm to display archive index
  * added testsuite for llvm-nm

Thanks

Shankar Easwaran

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by the Linux Foundation

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20121101/a51793eb/attachment.html>
-------------- next part --------------
Index: include/llvm/Object/Archive.h
===================================================================
--- include/llvm/Object/Archive.h	(revision 166683)
+++ include/llvm/Object/Archive.h	(working copy)
@@ -122,9 +122,21 @@
 
   Archive(MemoryBuffer *source, error_code &ec);
 
+  enum Kind {
+      kindGNUArchive,
+      kindBSDArchive,
+      kindCOFFArchive
+  };
+
+  Kind kind() const { 
+      return _kind;
+  }
+
   child_iterator begin_children(bool skip_internal = true) const;
   child_iterator end_children() const;
 
+  child_iterator begin_archiveidx() const;
+
   symbol_iterator begin_symbols() const;
   symbol_iterator end_symbols() const;
 
@@ -136,6 +148,7 @@
 private:
   child_iterator SymbolTable;
   child_iterator StringTable;
+  Kind _kind;
 };
 
 }
Index: tools/llvm-nm/test/archive-index.objtxt
===================================================================
--- tools/llvm-nm/test/archive-index.objtxt	(revision 0)
+++ tools/llvm-nm/test/archive-index.objtxt	(revision 0)
@@ -0,0 +1,12 @@
+#
+# Check if the index is appearing properly in the output file 
+#
+RUN: llvm-nm -s %p/Inputs/lib1.a | FileCheck -check-prefix=CHECKIDX %s
+
+CHECKIDX: archive index
+CHECKIDX: abcdefghijklmnopqrstuvwxyz12345678 in 1.o
+CHECKIDX: main in 1.o
+CHECKIDX: 1.o:
+CHECKIDX: 00000000 D abcdefghijklmnopqrstuvwxyz12345678
+CHECKIDX:          U fn1
+CHECKIDX: 00000000 T main
Index: tools/llvm-nm/test/Inputs/liblong.a
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: tools/llvm-nm/test/Inputs/liblong.a
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: tools/llvm-nm/test/Inputs/lib1.a
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: tools/llvm-nm/test/Inputs/lib1.a
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: tools/llvm-nm/test/lit.cfg
===================================================================
--- tools/llvm-nm/test/lit.cfg	(revision 0)
+++ tools/llvm-nm/test/lit.cfg	(revision 0)
@@ -0,0 +1,112 @@
+# -*- Python -*-
+
+import os
+import platform
+import re
+import subprocess
+
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'llvm-nm'
+
+# testFormat: The test format to use to interpret tests.
+#
+# For now we require '&&' between commands, until they get globally killed and
+# the test runner updated.
+execute_external = (platform.system() != 'Windows'
+                    or lit.getBashPath() not in [None, ""])
+config.test_format = lit.formats.ShTest(execute_external)
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.objtxt']
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+# test_exec_root: The root path where tests should be run.
+llvm_nm_obj_root = getattr(config, 'llvm_nm_obj_root', None)
+if llvm_nm_obj_root is not None:
+    config.test_exec_root = os.path.join(llvm_nm_obj_root, 'test')
+
+# Set llvm_{src,obj}_root for use by others.
+config.llvm_src_root = getattr(config, 'llvm_src_root', None)
+config.llvm_obj_root = getattr(config, 'llvm_obj_root', None)
+
+# Tweak the PATH to include the tools dir and the scripts dir.
+if llvm_nm_obj_root is not None:
+    llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+    if not llvm_tools_dir:
+        lit.fatal('No LLVM tools dir set!')
+    path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH']))
+    path = os.path.pathsep.join((os.path.join(getattr(config, 'llvm_src_root', None),'test','Scripts'),path))
+
+    config.environment['PATH'] = path
+
+    llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
+    if not llvm_libs_dir:
+        lit.fatal('No LLVM libs dir set!')
+    path = os.path.pathsep.join((llvm_libs_dir,
+                                 config.environment.get('LD_LIBRARY_PATH','')))
+    config.environment['LD_LIBRARY_PATH'] = path
+
+###
+
+# Check that the object root is known.
+if config.test_exec_root is None:
+    # Otherwise, we haven't loaded the site specific configuration (the user is
+    # probably trying to run on a test file directly, and either the site
+    # configuration hasn't been created by the build system, or we are in an
+    # out-of-tree build situation).
+
+    # Check for 'llvm_nm_site_config' user parameter, and use that if available.
+    site_cfg = lit.params.get('llvm_nm_site_config', None)
+    if site_cfg and os.path.exists(site_cfg):
+        lit.load_config(config, site_cfg)
+        raise SystemExit
+
+    # Try to detect the situation where we are using an out-of-tree build by
+    # looking for 'llvm-config'.
+    #
+    # FIXME: I debated (i.e., wrote and threw away) adding logic to
+    # automagically generate the lit.site.cfg if we are in some kind of fresh
+    # build situation. This means knowing how to invoke the build system though,
+    # and I decided it was too much magic. We should solve this by just having
+    # the .cfg files generated during the configuration step.
+
+    llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
+    if not llvm_config:
+        lit.fatal('No site specific configuration available!')
+
+    # Get the source and object roots.
+    llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
+    llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
+    llvm_nm_src_root = os.path.join(llvm_src_root, "tools", "llvm_nm")
+    llvm_nm_obj_root = os.path.join(llvm_obj_root, "tools", "llvm_nm")
+
+    # Validate that we got a tree which points to here, using the standard
+    # tools/llvm_nm layout.
+    this_src_root = os.path.dirname(config.test_source_root)
+    if os.path.realpath(llvm_nm_src_root) != os.path.realpath(this_src_root):
+        lit.fatal('No site specific configuration available!')
+
+    # Check that the site specific configuration exists.
+    site_cfg = os.path.join(llvm_nm_obj_root, 'test', 'lit.site.cfg')
+    if not os.path.exists(site_cfg):
+        lit.fatal('No site specific configuration available! You may need to '
+                  'run "make test" in your llvm-nm build directory.')
+
+    # Okay, that worked. Notify the user of the automagic, and reconfigure.
+    lit.note('using out-of-tree build at %r' % llvm_nm_obj_root)
+    lit.load_config(config, site_cfg)
+    raise SystemExit
+
+# When running under valgrind, we mangle '-vg' onto the end of the triple so we
+# can check it with XFAIL and XTARGET.
+if lit.useValgrind:
+    config.target_triple += '-vg'
+
+# Shell execution
+if platform.system() not in ['Windows'] or lit.getBashPath() != '':
+    config.available_features.add('shell')
Index: tools/llvm-nm/test/archive-long-index.objtxt
===================================================================
--- tools/llvm-nm/test/archive-long-index.objtxt	(revision 0)
+++ tools/llvm-nm/test/archive-long-index.objtxt	(revision 0)
@@ -0,0 +1,40 @@
+#
+# Check if the index is appearing properly in the output file 
+#
+RUN: llvm-nm -s %p/Inputs/liblong.a | FileCheck -check-prefix=CHECKIDX %s
+
+CHECKIDX: archive index
+CHECKIDX: abcdefghijklmnopqrstuvwxyz12345678 in 1.o
+CHECKIDX: main in 1.o
+CHECKIDX: fn1 in 2.o
+CHECKIDX: fn3 in 3.o
+CHECKIDX: fn1 in 3.o
+CHECKIDX: shankar in 4.o
+CHECKIDX: a in 5.o
+CHECKIDX: b in 6.o
+CHECKIDX: a in abcdefghijklmnopqrstuvwxyz1.o
+CHECKIDX: b in abcdefghijklmnopqrstuvwxyz2.o
+CHECKIDX: bda in abcdefghijklmnopqrstuvwxyz2.o
+CHECKIDX: b in abcdefghijklmnopq.o
+CHECKIDX: 1.o:
+CHECKIDX: 00000000 D abcdefghijklmnopqrstuvwxyz12345678
+CHECKIDX:          U bda
+CHECKIDX: 00000000 T main
+CHECKIDX: 2.o:
+CHECKIDX: 00000000 T fn1
+CHECKIDX: 3.o:
+CHECKIDX: 0000000b T fn1
+CHECKIDX: 00000000 T fn3
+CHECKIDX: 4.o:
+CHECKIDX:          C shankar
+CHECKIDX: 5.o:
+CHECKIDX:          C a
+CHECKIDX: 6.o:
+CHECKIDX:          C b
+CHECKIDX: abcdefghijklmnopqrstuvwxyz1.o:
+CHECKIDX:          C a
+CHECKIDX: abcdefghijklmnopqrstuvwxyz2.o:
+CHECKIDX:          C b
+CHECKIDX: 00000000 T bda
+CHECKIDX: abcdefghijklmnopq.o:
+CHECKIDX:          C b
Index: tools/llvm-nm/test/CMakeLists.txt
===================================================================
--- tools/llvm-nm/test/CMakeLists.txt	(revision 0)
+++ tools/llvm-nm/test/CMakeLists.txt	(revision 0)
@@ -0,0 +1,79 @@
+set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}")
+set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}")
+set(LLVM_BUILD_MODE "%(build_mode)s")
+set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s")
+set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib/%(build_config)s")
+set(CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
+set(CLANG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
+if(BUILD_SHARED_LIBS)
+  set(ENABLE_SHARED 1)
+else()
+  set(ENABLE_SHARED 0)
+endif(BUILD_SHARED_LIBS)
+
+configure_file(
+  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+
+if ( NOT LLVM_NM_BUILT_STANDALONE )
+
+  set(LLVM_NM_TEST_DEPS
+    llvm-nm llvm-nm-test.deps
+    FileCheck not llvm-nm
+    )
+  set(LLVM_NM_TEST_PARAMS
+    llvm-nm_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+    )
+
+  add_lit_testsuite(check-llvm-nm "Running llvm-nm regression tests"
+    ${CMAKE_CURRENT_BINARY_DIR}
+    PARAMS ${LLVM_NM_TEST_PARAMS}
+    DEPENDS ${LLVM_NM_TEST_DEPS}
+    )
+
+  set_target_properties(check-llvm-nm PROPERTIES FOLDER "llvm-nm tests")
+
+else()
+
+  include(FindPythonInterp)
+  if (PYTHONINTERP_FOUND)
+    if (LLVM_MAIN_SRC_DIR)
+      set(LIT "${LLVM_SOURCE_DIR}/utils/lit/lit.py")
+    else()
+      set(LIT "${PATH_TO_LLVM_BUILD}/bin/${CMAKE_CFG_INTDIR}/llvm-lit")
+      # Installed LLVM does not contain ${CMAKE_CFG_INTDIR} in paths.
+      if (NOT EXISTS ${LIT})
+        set(LIT "${PATH_TO_LLVM_BUILD}/bin/llvm-lit")
+      endif()
+    endif()
+
+    if (PATH_TO_LLVM_BUILD)
+      set(LLVM_NM_TEST_EXTRA_ARGS "--path=${LLVM_NM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}")
+    endif()
+
+    set(LIT_ARGS "${LLVM_NM_TEST_EXTRA_ARGS} ${LLVM_LIT_ARGS}")
+    separate_arguments(LIT_ARGS)
+
+    add_custom_target(llvm-nm-test.deps)
+    set_target_properties(llvm-nm-test.deps PROPERTIES FOLDER "llvm-nm tests")
+
+    add_custom_target(check-llvm-nm
+      COMMAND ${PYTHON_EXECUTABLE}
+                  ${LIT}
+                  --param llvm-nm_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+                  --param build_config=${CMAKE_CFG_INTDIR}
+                  --param build_mode=${RUNTIME_BUILD_MODE}
+                  ${LIT_ARGS}
+                  ${CMAKE_CURRENT_BINARY_DIR}
+      COMMENT "Running llvm-nm regression tests"
+      DEPENDS llvm-nm llvm-nm-test.deps
+      )
+    set_target_properties(check-llvm-nm PROPERTIES FOLDER "llvm-nm tests")
+  endif()
+
+endif()
+
+# Add a legacy target spelling: llvm-test
+add_custom_target(llvm-nm-test)
+add_dependencies(llvm-nm-test check-llvm-nm)
+set_target_properties(llvm-nm-test PROPERTIES FOLDER "llvm-nm tests")
Index: tools/llvm-nm/test/lit.site.cfg.in
===================================================================
--- tools/llvm-nm/test/lit.site.cfg.in	(revision 0)
+++ tools/llvm-nm/test/lit.site.cfg.in	(revision 0)
@@ -0,0 +1,21 @@
+## Autogenerated by LLVM/llvm-nm configuration.
+# Do not edit!
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
+config.llvm_nm_obj_root = "@LLVM_NM_BINARY_DIR@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+# Support substitution of the tools and libs dirs with user parameters. This is
+# used when we can't determine the tool dir at configuration time.
+try:
+    config.llvm_tools_dir = config.llvm_tools_dir % lit.params
+    config.llvm_libs_dir = config.llvm_libs_dir % lit.params
+except KeyError,e:
+    key, = e.args
+    lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
+
+# Let the main config do the real work.
+lit.load_config(config, "@LLVM_NM_SOURCE_DIR@/test/lit.cfg")
Index: tools/llvm-nm/llvm-nm.cpp
===================================================================
--- tools/llvm-nm/llvm-nm.cpp	(revision 166683)
+++ tools/llvm-nm/llvm-nm.cpp	(working copy)
@@ -113,6 +113,10 @@
   cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden,
                                cl::desc("Exclude aliases from output"));
 
+  cl::opt<bool> ArchiveIndex("archive-index",
+    cl::desc("Print the archive index"));
+  cl::alias ArchiveIndexs("s", cl::desc("Alias for --archive-index"),
+                              cl::aliasopt(ArchiveIndex));
   bool PrintAddress = true;
 
   bool MultipleFiles = false;
@@ -346,6 +350,23 @@
       return;
 
     if (object::Archive *a = dyn_cast<object::Archive>(arch.get())) {
+      if (ArchiveIndex) {
+        outs() << "archive index" << "\n";
+        for (object::Archive::symbol_iterator i = a->begin_symbols(), e = a->end_symbols(); i != e; ++i) {
+          object::Archive::child_iterator c;
+          StringRef symname;
+          StringRef filename;
+          if (error(i->getMember(c))) 
+              return;
+          if (error(i->getName(symname)))
+              return;
+          if (error(c->getName(filename)))
+              return;
+          outs() << symname << " in " << filename << "\n";
+        }
+        outs() << "\n";
+      }
+
       for (object::Archive::child_iterator i = a->begin_children(),
                                            e = a->end_children(); i != e; ++i) {
         OwningPtr<Binary> child;
Index: tools/llvm-nm/CMakeLists.txt
===================================================================
--- tools/llvm-nm/CMakeLists.txt	(revision 166683)
+++ tools/llvm-nm/CMakeLists.txt	(working copy)
@@ -1,5 +1,10 @@
 set(LLVM_LINK_COMPONENTS archive bitreader object)
 
+set(LLVM_NM_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(LLVM_NM_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
 add_llvm_tool(llvm-nm
   llvm-nm.cpp
   )
+
+add_subdirectory(test)
Index: lib/Object/Archive.cpp
===================================================================
--- lib/Object/Archive.cpp	(revision 166683)
+++ lib/Object/Archive.cpp	(working copy)
@@ -122,7 +122,17 @@
                    + sizeof(ArchiveMemberHeader)
                    + Parent->StringTable->getSize()))
       return object_error::parse_failed;
-    Result = addr;
+
+    // If the archive is a GNU archive, then the string table would have
+    // the name and /, if the filename exists in the string table. it would only 
+    // exist in the string table if the filename is a long filename
+    if (Parent->kind() == kindGNUArchive) {
+      StringRef::size_type end = StringRef(addr).find('/');
+      Result = StringRef(addr, end);
+    } 
+    else {
+      Result = addr;
+    }
     return object_error::success;
   } else if (name.startswith("#1/")) {
     APInt name_size;
@@ -187,15 +197,36 @@
   child_iterator i = begin_children(false);
   child_iterator e = end_children();
 
-  if (i != e) ++i; // Nobody cares about the first member.
-  if (i != e) {
+  StringRef name;
+  if ((ec = i->getName(name)))
+    return;
+  if (name == "/") {
+    // If the archive format is a GNU archive
+    // the first member is important, it would be the symbol table 
+    _kind = kindGNUArchive;
     SymbolTable = i;
-    ++i;
-  }
-  if (i != e) {
     StringTable = i;
+    if (i != e) ++i;
+    if ((ec = i->getName(name)))
+      return;
+    if (name == "//") 
+      StringTable = i;
+  } else if (name == "__.SYMDEF") {
+    _kind = kindBSDArchive;
+    SymbolTable = i;
+    StringTable = i;
+  } else {
+    _kind = kindCOFFArchive;
+    
+    if (i != e) ++i; // Nobody cares about the first member.
+    if (i != e) {
+      SymbolTable = i;
+      ++i;
+    }
+    if (i != e) {
+      StringTable = i;
+    }
   }
-
   ec = object_error::success;
 }
 
@@ -222,17 +253,37 @@
 
 error_code Archive::Symbol::getMember(child_iterator &Result) const {
   const char *buf = Parent->SymbolTable->getBuffer()->getBufferStart();
-  uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
   const char *offsets = buf + 4;
-  buf += 4 + (member_count * 4); // Skip offsets.
-  const char *indicies = buf + 4;
 
-  uint16_t offsetindex =
-    *(reinterpret_cast<const support::ulittle16_t*>(indicies)
-      + SymbolIndex);
+  uint32_t member_count = 0;
+  uint16_t offsetindex = 0;
+  uint32_t offset = 0;
 
-  uint32_t offset = *(reinterpret_cast<const support::ulittle32_t*>(offsets)
+  // If the archive type is a GNU archive
+  // the fields would be 
+  // member count
+  // symbol offset
+  // symbol offset
+  // ...
+  // symbol name\0
+  // symbol name\0
+  // ...
+  if (Parent->kind() == kindGNUArchive) {
+    member_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+    offsetindex = SymbolIndex;
+    offset = *(reinterpret_cast<const support::ubig32_t*>(offsets) 
+                          + offsetindex);
+  }
+  else {
+    member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+    buf += 4 + (member_count * 4); // Skip offsets.
+    const char *indicies = buf + 4;
+    offsetindex =
+      *(reinterpret_cast<const support::ulittle16_t*>(indicies)
+        + SymbolIndex);
+    offset = *(reinterpret_cast<const support::ulittle32_t*>(offsets)
                       + (offsetindex - 1));
+  }
 
   const char *Loc = Parent->getData().begin() + offset;
   size_t Size = sizeof(ArchiveMemberHeader) +
@@ -253,10 +304,21 @@
 
 Archive::symbol_iterator Archive::begin_symbols() const {
   const char *buf = SymbolTable->getBuffer()->getBufferStart();
-  uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
-  buf += 4 + (member_count * 4); // Skip offsets.
-  uint32_t symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
-  buf += 4 + (symbol_count * 2); // Skip indices.
+  uint32_t member_count = 0;
+  uint32_t symbol_count = 0;
+  // If the archive type is a GNU archive
+  // symbol count is equal to member count
+  if (kind() == kindGNUArchive) {
+    member_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+    symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+    buf += 4 + (symbol_count * 4);
+  }
+  else {
+    member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+    buf += 4 + (member_count * 4); // Skip offsets.
+    symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+    buf += 4 + (symbol_count * 2); // Skip indices.
+  }
   uint32_t string_start_offset =
     buf - SymbolTable->getBuffer()->getBufferStart();
   return symbol_iterator(Symbol(this, 0, string_start_offset));
@@ -264,9 +326,20 @@
 
 Archive::symbol_iterator Archive::end_symbols() const {
   const char *buf = SymbolTable->getBuffer()->getBufferStart();
-  uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
-  buf += 4 + (member_count * 4); // Skip offsets.
-  uint32_t symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+  uint32_t member_count = 0;
+  uint32_t symbol_count = 0;
+  // If the archive type is a GNU archive
+  // symbol count is equal to member count
+  if (kind() == kindGNUArchive) {
+    member_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+    symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf);
+    buf += 4 + (symbol_count * 4);
+  }
+  else {
+    member_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+    buf += 4 + (member_count * 4); // Skip offsets.
+    symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf);
+  }
   return symbol_iterator(
     Symbol(this, symbol_count, 0));
 }
-------------- next part --------------
Index: tools/lld-core/lld-core.cpp
===================================================================
--- tools/lld-core/lld-core.cpp	(revision 166441)
+++ tools/lld-core/lld-core.cpp	(working copy)
@@ -9,9 +9,11 @@
 
 #include "lld/Core/Atom.h"
 #include "lld/Core/LLVM.h"
+#include "lld/Core/File.h"
 #include "lld/Core/Pass.h"
 #include "lld/Core/Resolver.h"
 #include "lld/ReaderWriter/Reader.h"
+#include "lld/ReaderWriter/ReaderArchive.h"
 #include "lld/ReaderWriter/ReaderNative.h"
 #include "lld/ReaderWriter/ReaderYAML.h"
 #include "lld/ReaderWriter/ReaderELF.h"
@@ -195,6 +197,7 @@
   WriterOptionsMachO        writerOptionsMachO;
   WriterOptionsPECOFF       writerOptionsPECOFF;
 
+
   Writer* writer = nullptr;
   switch ( writeSelected ) {
     case writeYAML:
@@ -238,10 +241,43 @@
       break;
   }
 
+  ReaderOptionsArchive readerOptionsArchive(reader, argc, argv);
+  ReaderArchive *readerArchive = new ReaderArchive(readerOptionsArchive);
+
+
   for (auto path : cmdLineInputFilePaths) {
     std::vector<std::unique_ptr<File>> files;
-    if ( error(reader->readFile(path, files)) )
-      return 1;
+    File::Kind kind;
+    error_code EC;
+    
+    if (readerSelected == readerYAML) {
+        kind = File::kindYAML;
+    }
+    else 
+    if ((EC = (File::FileType(path, kind)))) {
+    	return 1;
+    }
+    switch(kind)
+    {
+      case File::kindObject:
+      case File::kindYAML:
+        if ( error(reader->readFile(path, files)))
+        	return 1;
+        break;
+      
+      case File::kindArchiveLibrary:
+        if (error(readerArchive->parseFile(path, files)))
+        	return 1;
+        break;
+      
+      case File::kindSharedLibrary:
+        if (error(readerArchive->parseFile(path, files)))
+        	return 1;
+        break;
+      
+      default:
+        return 1;
+    }
     inputFiles.appendFiles(files);
   }
 
Index: include/lld/ReaderWriter/ReaderArchive.h
===================================================================
--- include/lld/ReaderWriter/ReaderArchive.h	(revision 0)
+++ include/lld/ReaderWriter/ReaderArchive.h	(revision 0)
@@ -0,0 +1,84 @@
+//===- ReaderWriter/ReaderArchive.h - Archive Library Reader ------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+
+#ifndef LLD_READER_ARCHIVE_H
+#define LLD_READER_ARCHIVE_H
+
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/system_error.h"
+#include "llvm/Object/Archive.h"
+#include "lld/Core/File.h"
+#include "lld/Core/LLVM.h"
+#include "lld/ReaderWriter/Reader.h"
+#include "lld/ReaderWriter/ReaderArchive.h"
+#include <memory>
+#include <bits/unique_ptr.h>
+#include <vector>
+
+namespace lld
+{
+///
+/// The ReaderOptionsArchive encapsulates the options used by the ReaderArchive.
+/// The option objects are the only way to control the behaviour of Readers.
+///
+class ReaderOptionsArchive
+{
+public:
+  /* Create a reader options archive object with the parameters
+   * reader - the reader object to read files
+   * argc,argv - for iterating through the command line options
+   */
+  ReaderOptionsArchive(Reader *reader, int argc, char *argv[]):
+                                 _is_force_load(false),
+                                 _reader(reader)
+  {
+  }
+  
+  bool is_force_load() const
+  {
+    return _is_force_load;
+  }
+  
+  Reader *reader() const
+  {
+    return _reader;
+  }
+  
+private:
+  bool _is_force_load;
+  Reader *_reader;
+};
+
+// ReaderArchive is a class for reading archive libraries
+class ReaderArchive final
+{
+public:
+  ReaderArchive(ReaderOptionsArchive &options) : _options(options),
+                                                 _archive(NULL)
+  {
+  }
+
+  /* Returns a vector of Files that are contained in the archive file */
+  virtual error_code parseFile(StringRef path, 
+                               std::vector<std::unique_ptr<File>> &result);
+  
+  virtual ~ReaderArchive() { delete _archive; }
+
+private:
+  ReaderOptionsArchive &_options;
+  llvm::object::Archive *_archive;
+
+};
+
+} // namespace lld
+
+#endif // LLD_READER_ARCHIVE_H
Index: include/lld/Core/File.h
===================================================================
--- include/lld/Core/File.h	(revision 166441)
+++ include/lld/Core/File.h	(working copy)
@@ -10,6 +10,14 @@
 #ifndef LLD_CORE_FILE_H_
 #define LLD_CORE_FILE_H_
 
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/system_error.h"
+#include "llvm/Object/Error.h"
+#include "lld/Core/LLVM.h"
 #include "lld/Core/AbsoluteAtom.h"
 #include "lld/Core/DefinedAtom.h"
 #include "lld/Core/SharedLibraryAtom.h"
@@ -42,6 +50,7 @@
 
   /// Kinds of files that are supported.
   enum Kind {
+    kindYAML,              /// < yaml file (.yaml)
     kindObject,            ///< object file (.o)
     kindSharedLibrary,     ///< shared library (.so)
     kindArchiveLibrary,    ///< archive (.a)
@@ -52,6 +61,63 @@
     return kindObject;
   }
 
+  static llvm::error_code FileType(StringRef fileName, Kind &kind) {
+    OwningPtr<MemoryBuffer> File;
+    
+    if (MemoryBuffer::getFile(fileName, File))
+      return llvm::object::object_error::invalid_file_type;
+    
+    MemoryBuffer *Object = File.take();
+    
+    if (!Object || Object->getBufferSize() < 64)
+      return llvm::object::object_error::invalid_file_type;
+    
+    llvm::sys::LLVMFileType type = llvm::sys::IdentifyFileType(Object->getBufferStart(),
+                                    static_cast<unsigned>(Object->getBufferSize()));
+    switch (type) {
+      case llvm::sys::ELF_Relocatable_FileType:
+        kind = kindObject;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::ELF_SharedObject_FileType:
+        kind = kindSharedLibrary;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::Mach_O_Object_FileType:
+        kind = kindObject;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::Mach_O_FixedVirtualMemorySharedLib_FileType:
+      case llvm::sys::Mach_O_DynamicallyLinkedSharedLib_FileType:
+      case llvm::sys::Mach_O_DynamicLinker_FileType:
+      case llvm::sys::Mach_O_Bundle_FileType:
+      case llvm::sys::Mach_O_DynamicallyLinkedSharedLibStub_FileType:
+      case llvm::sys::Mach_O_DSYMCompanion_FileType:
+        kind = kindSharedLibrary;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::COFF_FileType:
+        kind = kindObject;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::Archive_FileType:
+        kind = kindArchiveLibrary;
+        return llvm::object::object_error::success;
+      
+      case llvm::sys::Mach_O_Core_FileType:
+      case llvm::sys::Mach_O_Executable_FileType:
+      case llvm::sys::Mach_O_PreloadExecutable_FileType:
+        return llvm::object::object_error::invalid_file_type;
+      
+      case llvm::sys::ELF_Executable_FileType:
+      case llvm::sys::ELF_Core_FileType:
+        return llvm::object::object_error::invalid_file_type;
+      
+      default:
+        return llvm::object::object_error::invalid_file_type;
+    }
+  }
+
   /// For error messages and debugging, this returns the path to the file
   /// which was used to create this object (e.g. "/tmp/foo.o").
   StringRef path() const  {
Index: include/lld/Core/FileArchive.h
===================================================================
--- include/lld/Core/FileArchive.h	(revision 0)
+++ include/lld/Core/FileArchive.h	(revision 0)
@@ -0,0 +1,156 @@
+//===- Core/FileArchive.h - Support static library -----------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_CORE_FILE_ARCHIVE_FILE_H_
+#define LLD_CORE_FILE_ARCHIVE_FILE_H_
+
+#include "lld/ReaderWriter/ReaderArchive.h"
+#include "lld/Core/ArchiveLibraryFile.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/system_error.h"
+#include "llvm/Object/Archive.h"
+#include "lld/Core/File.h"
+#include "lld/Core/LLVM.h"
+
+namespace lld {
+
+// The FileArchive class represents an Archive Library file
+class FileArchive : public ArchiveLibraryFile {
+public:
+
+  virtual ~FileArchive() { }
+
+  /// Check if any member of the archive contains an Atom with the
+  /// specified name and return the File object for that member, or nullptr.
+  virtual const File *find(StringRef name, bool dataSymbolOnly) const 
+  {
+    error_code ec;  
+    StringRef symname;
+    std::vector<std::unique_ptr<File>> result;
+    llvm::object::Archive *_archive;
+    
+    _archive = new llvm::object::Archive(_mb, ec);
+    
+    if (ec)
+      return NULL;
+    
+    for (auto bs = _archive->begin_symbols(), 
+              es = _archive->end_symbols(); bs != es; ++bs)
+    {
+      if ((ec = bs->getName(symname)))
+          return NULL;
+      
+      if (symname == name) {
+        llvm::object::Archive::child_iterator ci;
+        if ((ec = bs->getMember(ci)))
+          return NULL;
+        
+        if ((ec = getSymbolForType(ci->getBuffer(), symname, dataSymbolOnly)))
+          return NULL;
+        
+        if ((ec = _options.reader()->parseFile(std::unique_ptr<MemoryBuffer>
+                                               (ci->getBuffer()), result)))
+          return NULL;
+        
+        for (std::unique_ptr<File> &f : result) {
+          return f.release();
+        }
+      }
+    }
+    return NULL;
+  }
+
+  virtual void addAtom(const Atom&) {
+    llvm_unreachable("cannot add atoms to native .o files");
+  }
+
+  virtual const atom_collection<DefinedAtom> &defined() const {
+    return _definedAtoms;
+  }
+
+  virtual const atom_collection<UndefinedAtom> &undefined() const {
+    return _undefinedAtoms;
+  }
+
+  virtual const atom_collection<SharedLibraryAtom> &sharedLibrary() const {
+    return _sharedLibraryAtoms;
+  }
+
+  virtual const atom_collection<AbsoluteAtom> &absolute() const {
+    return _absoluteAtoms;
+  }
+
+protected:
+  error_code getSymbolForType(MemoryBuffer *mb, StringRef &symbol, 
+                              bool &isDataSym) const
+  {
+    llvm::object::ObjectFile *obj = 
+                  llvm::object::ObjectFile::createObjectFile(mb);
+    error_code ec;
+    llvm::object::SymbolRef::Type symtype;
+    uint32_t symflags;
+    llvm::object::symbol_iterator ibegin = obj->begin_symbols();
+    llvm::object::symbol_iterator iend = obj->end_symbols();
+    StringRef symbolname;
+
+    for (llvm::object::symbol_iterator i = ibegin; i != iend; i.increment(ec)) {
+      if (ec) return ec;
+      
+      // Get symbol name
+      if ((ec = (i->getName(symbolname)))) return ec;
+      
+      if (symbolname != symbol) 
+          continue;
+      
+      // Get symbol flags
+      if ((ec = (i->getFlags(symflags)))) return ec;
+      
+      if (symflags <= llvm::object::SymbolRef::SF_Undefined)
+          continue;
+      
+      // Get Symbol Type
+      if ((ec = (i->getType(symtype)))) return ec;
+      
+      // If the symbol is a data symbol and we need a data symbol
+      // return success if there is a match
+      if (isDataSym) {
+          if (symtype == llvm::object::SymbolRef::ST_Data) {
+            return error_code::success();
+          }
+          continue;
+      }
+      return error_code::success();
+    }
+    return llvm::object::object_error::parse_failed;
+  }
+
+private:
+  llvm::MemoryBuffer *_mb;
+  ReaderOptionsArchive _options;
+  atom_collection_vector<DefinedAtom>       _definedAtoms;
+  atom_collection_vector<UndefinedAtom>     _undefinedAtoms;
+  atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
+  atom_collection_vector<AbsoluteAtom>      _absoluteAtoms;
+
+public:
+  /// only subclasses of ArchiveLibraryFile can be instantiated 
+  FileArchive(llvm::MemoryBuffer *mb, 
+    ReaderOptionsArchive &options, StringRef path) : ArchiveLibraryFile(path),
+                                                     _mb(mb),
+                                                     _options(options)
+  { 
+  }
+};
+
+} // namespace lld
+
+#endif // LLD_CORE_FILE_ARCHIVE_FILE_H_
Index: lib/ReaderWriter/ReaderArchive.cpp
===================================================================
--- lib/ReaderWriter/ReaderArchive.cpp	(revision 0)
+++ lib/ReaderWriter/ReaderArchive.cpp	(revision 0)
@@ -0,0 +1,44 @@
+//===- lib/ReaderWriter/ReaderArchive.cpp - Archive Library Reader--------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+#include "lld/ReaderWriter/ReaderArchive.h"
+#include "lld/Core/FileArchive.h"
+
+namespace lld 
+{
+  /* Returns a vector of Files that are contained in the archive file */
+  error_code ReaderArchive::parseFile(StringRef path,
+  		std::vector<std::unique_ptr<File>> &result)
+  {
+    error_code ec;
+    OwningPtr<llvm::MemoryBuffer> opmb;
+    if ((ec = llvm::MemoryBuffer::getFile(path, opmb)))
+    	return ec;
+    
+    if (_options.is_force_load())
+    {
+      _archive = new llvm::object::Archive(opmb.take(), ec);
+      if (ec)
+        return ec;
+      
+      for (auto mf = _archive->begin_children(), 
+                me = _archive->end_children(); mf != me; ++mf)
+      {
+      	if ((ec = _options.reader()->parseFile(std::unique_ptr<MemoryBuffer>
+                                               (mf->getBuffer()), result)))
+          return ec;
+      }
+    }
+    else {
+      std::unique_ptr<File> f;
+      f.reset(new FileArchive(opmb.take(), _options, path));
+      result.push_back(std::move(f));
+    }
+    return llvm::error_code::success();
+  }
+}
Index: lib/ReaderWriter/CMakeLists.txt
===================================================================
--- lib/ReaderWriter/CMakeLists.txt	(revision 166441)
+++ lib/ReaderWriter/CMakeLists.txt	(working copy)
@@ -6,4 +6,5 @@
 add_lld_library(lldReaderWriter
   Reader.cpp
   Writer.cpp
+  ReaderArchive.cpp
   )


More information about the llvm-commits mailing list