[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