[ELF] Add support for reading dynamic libraries.

llvm-svn: 174916
This commit is contained in:
Michael J. Spencer 2013-02-11 23:03:35 +00:00
parent 6ae564b4a0
commit c3c8bc1e5c
16 changed files with 377 additions and 103 deletions

View File

@ -165,22 +165,26 @@ protected:
class atom_collection_vector : public atom_collection<T> { class atom_collection_vector : public atom_collection<T> {
public: public:
virtual atom_iterator<T> begin() const { virtual atom_iterator<T> begin() const {
return atom_iterator<T>(*this, reinterpret_cast<const void*> return atom_iterator<T>(*this,
(_atoms.data())); _atoms.empty() ? 0 : reinterpret_cast<const void *>(_atoms.data()));
} }
virtual atom_iterator<T> end() const{ virtual atom_iterator<T> end() const{
return atom_iterator<T>(*this, reinterpret_cast<const void*> return atom_iterator<T>(*this, _atoms.empty() ? 0 :
(_atoms.data() + _atoms.size())); reinterpret_cast<const void *>(_atoms.data() + _atoms.size()));
} }
virtual const T *deref(const void *it) const { virtual const T *deref(const void *it) const {
return *reinterpret_cast<const T* const*>(it); return *reinterpret_cast<const T* const*>(it);
} }
virtual void next(const void *&it) const { virtual void next(const void *&it) const {
const T *const *p = reinterpret_cast<const T *const*>(it); const T *const *p = reinterpret_cast<const T *const*>(it);
++p; ++p;
it = reinterpret_cast<const void*>(p); it = reinterpret_cast<const void*>(p);
} }
std::vector<const T*> _atoms;
std::vector<const T *> _atoms;
}; };
/// \brief This is a convenience class for File subclasses which need to /// \brief This is a convenience class for File subclasses which need to

View File

@ -17,8 +17,11 @@
// This should be the only #include, force #includes of all the others on // This should be the only #include, force #includes of all the others on
// clients. // clients.
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Casting.h" #include "llvm/Support/Casting.h"
#include <utility>
namespace llvm { namespace llvm {
// ADT's. // ADT's.
class StringRef; class StringRef;
@ -77,4 +80,13 @@ namespace lld {
using llvm::raw_ostream; using llvm::raw_ostream;
} // end namespace clang. } // end namespace clang.
namespace std {
template <> struct hash<llvm::StringRef> {
public:
size_t operator()(const llvm::StringRef &s) const {
return llvm::hash_value(s);
}
};
}
#endif #endif

View File

@ -41,15 +41,10 @@ public:
/// If so, return a SharedLibraryAtom which represents that exported /// If so, return a SharedLibraryAtom which represents that exported
/// symbol. Otherwise return nullptr. /// symbol. Otherwise return nullptr.
virtual const SharedLibraryAtom *exports(StringRef name, virtual const SharedLibraryAtom *exports(StringRef name,
bool dataSymbolOnly) const; bool dataSymbolOnly) const = 0;
protected: protected:
/// only subclasses of SharedLibraryFile can be instantiated /// only subclasses of SharedLibraryFile can be instantiated
SharedLibraryFile(const TargetInfo &ti, StringRef path) SharedLibraryFile(StringRef path) : File(path) {}
: File(path), _targetInfo(ti) {
}
private:
const TargetInfo &_targetInfo;
}; };
} // namespace lld } // namespace lld

View File

@ -21,6 +21,7 @@
namespace lld { namespace lld {
namespace elf { namespace elf {
template <class ELFT> class DynamicFile;
template <typename ELFT> class ELFFile; template <typename ELFT> class ELFFile;
template <typename ELFT> class TargetAtomHandler; template <typename ELFT> class TargetAtomHandler;
@ -95,7 +96,7 @@ public:
: _owningFile(file), _name(name), _symbol(symbol), _value(value) { : _owningFile(file), _name(name), _symbol(symbol), _value(value) {
} }
virtual const class ELFFile<ELFT> &file() const { virtual const ELFFile<ELFT> &file() const {
return _owningFile; return _owningFile;
} virtual Scope scope() const { } virtual Scope scope() const {
if (_symbol->st_other == llvm::ELF::STV_HIDDEN) if (_symbol->st_other == llvm::ELF::STV_HIDDEN)
@ -132,7 +133,7 @@ public:
const Elf_Sym *symbol) const Elf_Sym *symbol)
: _owningFile(file), _name(name), _symbol(symbol) {} : _owningFile(file), _name(name), _symbol(symbol) {}
virtual const class ELFFile<ELFT> &file() const { virtual const ELFFile<ELFT> &file() const {
return _owningFile; return _owningFile;
} }
@ -187,7 +188,7 @@ public:
_ordinal = ++orderNumber; _ordinal = ++orderNumber;
} }
virtual const class ELFFile<ELFT> &file() const { virtual const ELFFile<ELFT> &file() const {
return _owningFile; return _owningFile;
} }
@ -440,6 +441,49 @@ private:
std::vector<ELFReference<ELFT>*> & std::vector<ELFReference<ELFT>*> &
_referenceList; _referenceList;
}; };
/// \brief An atom from a shared library.
template <class ELFT>
class ELFDynamicAtom LLVM_FINAL : public SharedLibraryAtom {
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
public:
ELFDynamicAtom(const DynamicFile<ELFT> &file, StringRef symbolName,
StringRef loadName, const Elf_Sym *symbol)
: _owningFile(file), _symbolName(symbolName), _loadName(loadName),
_symbol(symbol) {
}
virtual const DynamicFile<ELFT> &file() const {
return _owningFile;
}
virtual StringRef name() const {
return _symbolName;
}
virtual Scope scope() const {
if (_symbol->st_other == llvm::ELF::STV_HIDDEN)
return scopeLinkageUnit;
else if (_symbol->getBinding() != llvm::ELF::STB_LOCAL)
return scopeGlobal;
else
return scopeTranslationUnit;
}
virtual StringRef loadName() const { return _loadName; }
virtual bool canBeNullAtRuntime() const {
return _symbol->getBinding() == llvm::ELF::STB_WEAK;
}
private:
const DynamicFile<ELFT> &_owningFile;
StringRef _symbolName;
StringRef _loadName;
const Elf_Sym *_symbol;
};
} // end namespace elf } // end namespace elf
} // end namespace lld } // end namespace lld

View File

@ -0,0 +1,109 @@
//===- lib/ReaderWriter/ELF/CreateELF.h -----------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides a simple way to create an object templated on
/// ELFType depending on the runtime type needed.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_CREATE_ELF_H
#define LLD_READER_WRITER_ELF_CREATE_ELF_H
#include "llvm/Object/ELF.h"
#include "llvm/Support/Compiler.h"
namespace {
using llvm::object::ELFType;
/// \func createELF
/// \brief Create an object depending on the runtime attributes and alignment
/// of an ELF file.
///
/// \param Traits
/// Traits::result_type must be a type convertable from what create returns.
/// Traits::create must be a template function which takes an ELFType and
/// returns something convertable to Traits::result_type.
///
/// \param ident pair of EI_CLASS and EI_DATA.
/// \param maxAlignment the maximum alignment of the file.
/// \param args arguments forwarded to CreateELFTraits<T>::create.
#define LLVM_CREATE_ELF_CreateELFTraits(endian, align, is64, ...) \
Traits::template create<ELFType<llvm::support::endian, align, is64>>( \
__VA_ARGS__);
#if !LLVM_IS_UNALIGNED_ACCESS_FAST
# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \
if (maxAlignment >= normal) \
return LLVM_CREATE_ELF_CreateELFTraits(endian, normal, is64, __VA_ARGS__) \
else if (maxAlignment >= low) \
return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \
else \
llvm_unreachable("Invalid alignment for ELF file!");
#else
# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \
if (maxAlignment >= low) \
return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \
else \
llvm_unreachable("Invalid alignment for ELF file!");
#endif
#define LLVM_CREATE_ELF_IMPL(...) \
if (ident.first == llvm::ELF::ELFCLASS32 && \
ident.second == llvm::ELF::ELFDATA2LSB) { \
LLVM_CREATE_ELF_MaxAlignCheck(4, 2, little, false, __VA_ARGS__) \
} else if (ident.first == llvm::ELF::ELFCLASS32 && \
ident.second == llvm::ELF::ELFDATA2MSB) { \
LLVM_CREATE_ELF_MaxAlignCheck(4, 2, big, false, __VA_ARGS__) \
} else if (ident.first == llvm::ELF::ELFCLASS64 && \
ident.second == llvm::ELF::ELFDATA2MSB) { \
LLVM_CREATE_ELF_MaxAlignCheck(8, 2, big, true, __VA_ARGS__) \
} else if (ident.first == llvm::ELF::ELFCLASS64 && \
ident.second == llvm::ELF::ELFDATA2LSB) { \
LLVM_CREATE_ELF_MaxAlignCheck(8, 2, little, true, __VA_ARGS__) \
} \
llvm_unreachable("Invalid ELF type!");
#if LLVM_HAS_VARIADIC_TEMPLATES
template <class Traits, class ...Args>
typename Traits::result_type createELF(
std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment,
Args &&...args) {
LLVM_CREATE_ELF_IMPL(std::forward<Args>(args)...)
}
#else
template <class Traits, class T1>
typename Traits::result_type createELF(
std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment,
T1 &&t1) {
LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1))
}
template <class Traits, class T1, class T2>
typename Traits::result_type createELF(
std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment,
T1 &&t1, T2 &&t2) {
LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2))
}
template <class Traits, class T1, class T2, class T3>
typename Traits::result_type createELF(
std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment,
T1 &&t1, T2 &&t2, T3 &&t3) {
LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2),
std::forward<T3>(t3))
}
#endif // LLVM_HAS_VARIADIC_TEMPLATES
} // end anon namespace
#undef LLVM_CREATE_ELF_CreateELFTraits
#undef LLVM_CREATE_ELF_MaxAlignCheck
#undef LLVM_CREATE_ELF_IMPL
#endif

View File

@ -0,0 +1,132 @@
//===- lib/ReaderWriter/ELF/DynamicFile.h ---------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_DYNAMIC_FILE_H
#define LLD_READER_WRITER_ELF_DYNAMIC_FILE_H
#include "lld/Core/SharedLibraryFile.h"
#include "lld/ReaderWriter/ELFTargetInfo.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Path.h"
#include <unordered_map>
namespace lld {
namespace elf {
template <class ELFT> class DynamicFile LLVM_FINAL : public SharedLibraryFile {
public:
static ErrorOr<std::unique_ptr<DynamicFile> > create(
const ELFTargetInfo &ti, std::unique_ptr<llvm::MemoryBuffer> mb) {
std::unique_ptr<DynamicFile> file(
new DynamicFile(ti, mb->getBufferIdentifier()));
static uint32_t lastOrdinal = 0;
file->_ordinal = lastOrdinal++;
llvm::OwningPtr<llvm::object::Binary> binaryFile;
if (error_code ec = createBinary(mb.release(), binaryFile))
return ec;
// Point Obj to correct class and bitwidth ELF object
file->_objFile.reset(
dyn_cast<llvm::object::ELFObjectFile<ELFT>>(binaryFile.get()));
if (!file->_objFile)
return make_error_code(llvm::object::object_error::invalid_file_type);
binaryFile.take();
llvm::object::ELFObjectFile<ELFT> &obj = *file->_objFile;
file->_soname = obj.getLoadName();
if (file->_soname.empty())
file->_soname = llvm::sys::path::filename(file->path());
// Create a map from names to dynamic symbol table entries.
// TODO: This should use the object file's build in hash table instead if
// it exists.
for (auto i = obj.begin_elf_dynamic_symbols(),
e = obj.end_elf_dynamic_symbols();
i != e; ++i) {
// Don't expose undefined or absolute symbols to export.
if (i->st_shndx == llvm::ELF::SHN_ABS ||
i->st_shndx == llvm::ELF::SHN_UNDEF)
continue;
StringRef name;
if (error_code ec =
obj.getSymbolName(obj.getDynamicSymbolTableSectionHeader(), &*i,
name))
return ec;
file->_nameToSym[name]._symbol = &*i;
// TODO: Read undefined dynamic symbols into _undefinedAtoms.
}
return std::move(file);
}
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;
}
virtual const SharedLibraryAtom *exports(StringRef name,
bool dataSymbolOnly) const {
assert(!dataSymbolOnly && "Invalid option for ELF exports!");
// See if we have the symbol.
auto sym = _nameToSym.find(name);
if (sym == _nameToSym.end())
return nullptr;
// Have we already created a SharedLibraryAtom for it?
if (sym->second._atom)
return sym->second._atom;
// Create a SharedLibraryAtom for this symbol.
return sym->second._atom = new (_alloc) ELFDynamicAtom<ELFT>(
*this, name, _soname, sym->second._symbol);
}
virtual const ELFTargetInfo &getTargetInfo() const { return _targetInfo; }
private:
DynamicFile(const ELFTargetInfo &ti, StringRef name)
: SharedLibraryFile(name), _targetInfo(ti) {}
const ELFTargetInfo &_targetInfo;
std::unique_ptr<llvm::object::ELFObjectFile<ELFT>> _objFile;
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
/// \brief DT_SONAME
StringRef _soname;
struct SymAtomPair {
const typename llvm::object::ELFObjectFile<ELFT>::Elf_Sym *_symbol;
const SharedLibraryAtom *_atom;
};
mutable std::unordered_map<StringRef, SymAtomPair> _nameToSym;
mutable llvm::BumpPtrAllocator _alloc;
};
} // end namespace elf
} // end namespace lld
#endif

View File

@ -38,16 +38,6 @@
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
namespace std {
template <> struct hash<llvm::StringRef> {
public:
size_t operator()(const llvm::StringRef &s) const {
using llvm::hash_value;
return hash_value(s);
}
};
}
namespace lld { namespace lld {
namespace elf { namespace elf {
/// \brief Read a binary, find out based on the symbol table contents what kind /// \brief Read a binary, find out based on the symbol table contents what kind

View File

@ -16,6 +16,8 @@
#include "lld/ReaderWriter/Reader.h" #include "lld/ReaderWriter/Reader.h"
#include "Atoms.h" #include "Atoms.h"
#include "CreateELF.h"
#include "DynamicFile.h"
#include "File.h" #include "File.h"
#include "lld/Core/Reference.h" #include "lld/Core/Reference.h"
@ -46,6 +48,30 @@
using llvm::support::endianness; using llvm::support::endianness;
using namespace llvm::object; using namespace llvm::object;
namespace {
struct DynamicFileCreateELFTraits {
typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type;
template <class ELFT>
static result_type create(const lld::ELFTargetInfo &ti,
std::unique_ptr<llvm::MemoryBuffer> mb) {
return lld::elf::DynamicFile<ELFT>::create(ti, std::move(mb));
}
};
struct ELFFileCreateELFTraits {
typedef std::unique_ptr<lld::File> result_type;
template <class ELFT>
static result_type create(const lld::ELFTargetInfo &ti,
std::unique_ptr<llvm::MemoryBuffer> mb,
lld::error_code &ec) {
return std::unique_ptr<lld::File>(
new lld::elf::ELFFile<ELFT>(ti, std::move(mb), ec));
}
};
}
namespace lld { namespace lld {
namespace elf { namespace elf {
/// \brief A reader object that will instantiate correct File by examining the /// \brief A reader object that will instantiate correct File by examining the
@ -69,66 +95,20 @@ public:
llvm::error_code ec; llvm::error_code ec;
switch (fileType) { switch (fileType) {
case llvm::sys::ELF_Relocatable_FileType: { case llvm::sys::ELF_Relocatable_FileType: {
std::pair<unsigned char, unsigned char> Ident = getElfArchType(&*mb); std::unique_ptr<File> f(createELF<ELFFileCreateELFTraits>(
std::unique_ptr<File> f; getElfArchType(&*mb), MaxAlignment, _elfTargetInfo, std::move(mb),
// Instantiate the correct File template instance based on the Ident ec));
// pair. Once the File is created we push the file to the vector of files if (ec)
// already created during parser's life. return ec;
if (Ident.first == llvm::ELF::ELFCLASS32 && result.push_back(std::move(f));
Ident.second == llvm::ELF::ELFDATA2LSB) { break;
#if !LLVM_IS_UNALIGNED_ACCESS_FAST }
if (MaxAlignment >= 4) case llvm::sys::ELF_SharedObject_FileType: {
f.reset(new ELFFile<ELFType<llvm::support::little, 4, false> >( auto f = createELF<DynamicFileCreateELFTraits>(
_elfTargetInfo, std::move(mb), ec)); getElfArchType(&*mb), MaxAlignment, _elfTargetInfo, std::move(mb));
else if (!f)
#endif return f;
if (MaxAlignment >= 2) result.push_back(std::move(*f));
f.reset(new ELFFile<ELFType<llvm::support::little, 2, false> >(
_elfTargetInfo, std::move(mb), ec));
else
llvm_unreachable("Invalid alignment for ELF file!");
} else if (Ident.first == llvm::ELF::ELFCLASS32 &&
Ident.second == llvm::ELF::ELFDATA2MSB) {
#if !LLVM_IS_UNALIGNED_ACCESS_FAST
if (MaxAlignment >= 4)
f.reset(new ELFFile<ELFType<llvm::support::big, 4, false> >(
_elfTargetInfo, std::move(mb), ec));
else
#endif
if (MaxAlignment >= 2)
f.reset(new ELFFile<ELFType<llvm::support::big, 2, false> >(
_elfTargetInfo, std::move(mb), ec));
else
llvm_unreachable("Invalid alignment for ELF file!");
} else if (Ident.first == llvm::ELF::ELFCLASS64 &&
Ident.second == llvm::ELF::ELFDATA2MSB) {
#if !LLVM_IS_UNALIGNED_ACCESS_FAST
if (MaxAlignment >= 8)
f.reset(new ELFFile<ELFType<llvm::support::big, 8, true> >(
_elfTargetInfo, std::move(mb), ec));
else
#endif
if (MaxAlignment >= 2)
f.reset(new ELFFile<ELFType<llvm::support::big, 2, true> >(
_elfTargetInfo, std::move(mb), ec));
else
llvm_unreachable("Invalid alignment for ELF file!");
} else if (Ident.first == llvm::ELF::ELFCLASS64 &&
Ident.second == llvm::ELF::ELFDATA2LSB) {
#if !LLVM_IS_UNALIGNED_ACCESS_FAST
if (MaxAlignment >= 8)
f.reset(new ELFFile<ELFType<llvm::support::little, 8, true> >(
_elfTargetInfo, std::move(mb), ec));
else
#endif
if (MaxAlignment >= 2)
f.reset(new ELFFile<ELFType<llvm::support::little, 2, true> >(
_elfTargetInfo, std::move(mb), ec));
else
llvm_unreachable("Invalid alignment for ELF file!");
}
if (!ec)
result.push_back(std::move(f));
break; break;
} }
case llvm::sys::Archive_FileType: case llvm::sys::Archive_FileType:

View File

@ -17,17 +17,6 @@
#include <unordered_map> #include <unordered_map>
namespace std {
template<>
struct hash<llvm::StringRef> {
public:
size_t operator()(const llvm::StringRef &s) const {
using llvm::hash_value;
return hash_value(s);
}
};
}
namespace lld { namespace lld {
/// \brief The FileArchive class represents an Archive Library file /// \brief The FileArchive class represents an Archive Library file
class FileArchive : public ArchiveLibraryFile { class FileArchive : public ArchiveLibraryFile {

View File

@ -220,13 +220,15 @@ private:
template <typename T> template <typename T>
class AtomList : public lld::File::atom_collection<T> { class AtomList : public lld::File::atom_collection<T> {
public: public:
virtual lld::File::atom_iterator<T> begin() const { virtual lld::File::atom_iterator<T> begin() const {
return lld::File::atom_iterator<T>(*this, reinterpret_cast<const void*> return lld::File::atom_iterator<
(_atoms.data())); T>(*this,
_atoms.empty() ? 0 : reinterpret_cast<const void *>(_atoms.data()));
} }
virtual lld::File::atom_iterator<T> end() const{ virtual lld::File::atom_iterator<T> end() const{
return lld::File::atom_iterator<T>(*this, reinterpret_cast<const void*> return lld::File::atom_iterator<
(_atoms.data() + _atoms.size())); T>(*this, _atoms.empty() ? 0 :
reinterpret_cast<const void *>(_atoms.data() + _atoms.size()));
} }
virtual const T *deref(const void *it) const { virtual const T *deref(const void *it) const {
return *reinterpret_cast<const T *const*>(it); return *reinterpret_cast<const T *const*>(it);

View File

@ -20,7 +20,7 @@ if ( NOT LLD_BUILT_STANDALONE )
set(LLD_TEST_DEPS set(LLD_TEST_DEPS
lld-core lld-test.deps lld-core lld-test.deps
FileCheck not llvm-nm FileCheck not llvm-nm
lld llvm-objdump lld llvm-objdump llvm-readobj
) )
set(LLD_TEST_PARAMS set(LLD_TEST_PARAMS
lld_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg lld_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg

View File

@ -0,0 +1,5 @@
#include <stdio.h>
void foo() {
puts("Fooo!!");
}

Binary file not shown.

View File

@ -0,0 +1,5 @@
void foo();
int main() {
foo();
}

Binary file not shown.

View File

@ -0,0 +1,7 @@
RUN: lld -core -target x86_64-linux %p/Inputs/use-shared.x86-64 \
RUN: %p/Inputs/shared.so-x86-64 -emit-yaml -output=- -noinhibit-exec \
RUN: | FileCheck %s
CHECK: shared-library-atoms:
CHECK: name: foo
CHECK: load-name: shared.so-x86-64