mirror of
https://github.com/intel/llvm.git
synced 2026-02-08 17:28:30 +08:00
This adds functionality for undefined atoms from dynamic libraries to be added
to the list of undefined atoms. The processing of undefined atoms from dynamic libraries is controlled by use-shlib-undefines command line option. This patch also adds additional command line arguments to allow/disallow unresolved symbols from shared libraries and mimics GNU ld behavior. llvm-svn: 179257
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "lld/Core/File.h"
|
||||
#include "lld/Core/InputFiles.h"
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "lld/Core/SymbolTable.h"
|
||||
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
|
||||
@@ -35,44 +35,44 @@ class InputFiles;
|
||||
|
||||
/// \brief The TargetInfo class encapsulates "what and how" to link.
|
||||
///
|
||||
/// The base class TargetInfo contains the options needed by core linking.
|
||||
/// Subclasses of TargetInfo have additional options needed by specific Readers
|
||||
/// and Writers. For example, ELFTargetInfo has methods that supplies options
|
||||
/// The base class TargetInfo contains the options needed by core linking.
|
||||
/// Subclasses of TargetInfo have additional options needed by specific Readers
|
||||
/// and Writers. For example, ELFTargetInfo has methods that supplies options
|
||||
/// to the ELF Reader and Writer.
|
||||
///
|
||||
/// \todo Consider renaming to something like "LinkingOptions".
|
||||
/// \todo Consider renaming to something like "LinkingOptions".
|
||||
class TargetInfo : public Reader {
|
||||
public:
|
||||
virtual ~TargetInfo();
|
||||
|
||||
/// \name Methods needed by core linking
|
||||
/// @{
|
||||
|
||||
/// Name of symbol linker should use as "entry point" to program,
|
||||
|
||||
/// Name of symbol linker should use as "entry point" to program,
|
||||
/// usually "main" or "start".
|
||||
StringRef entrySymbolName() const {
|
||||
return _entrySymbolName;
|
||||
StringRef entrySymbolName() const {
|
||||
return _entrySymbolName;
|
||||
}
|
||||
|
||||
/// Whether core linking should remove Atoms not reachable by following
|
||||
|
||||
/// Whether core linking should remove Atoms not reachable by following
|
||||
/// References from the entry point Atom or from all global scope Atoms
|
||||
/// if globalsAreDeadStripRoots() is true.
|
||||
bool deadStrip() const {
|
||||
return _deadStrip;
|
||||
bool deadStrip() const {
|
||||
return _deadStrip;
|
||||
}
|
||||
|
||||
|
||||
/// Only used if deadStrip() returns true. Means all global scope Atoms
|
||||
/// should be marked live (along with all Atoms they reference). Usually
|
||||
/// this method returns false for main executables, but true for dynamic
|
||||
/// shared libraries.
|
||||
bool globalsAreDeadStripRoots() const {
|
||||
assert(_deadStrip && "only applicable when deadstripping enabled");
|
||||
return _globalsAreDeadStripRoots;
|
||||
return _globalsAreDeadStripRoots;
|
||||
}
|
||||
|
||||
|
||||
/// Only used if deadStrip() returns true. This method returns the names
|
||||
/// of DefinedAtoms that should be marked live (along with all Atoms they
|
||||
/// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can
|
||||
/// of DefinedAtoms that should be marked live (along with all Atoms they
|
||||
/// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can
|
||||
/// be kept live using this method.
|
||||
const std::vector<StringRef> &deadStripRoots() const {
|
||||
return _deadStripRoots;
|
||||
@@ -80,51 +80,51 @@ public:
|
||||
|
||||
/// Archive files (aka static libraries) are normally lazily loaded. That is,
|
||||
/// object files within an archive are only loaded and linked in, if the
|
||||
/// object file contains a DefinedAtom which will replace an existing
|
||||
/// object file contains a DefinedAtom which will replace an existing
|
||||
/// UndefinedAtom. If this method returns true, core linking will actively
|
||||
/// load every member object file from every archive.
|
||||
bool forceLoadAllArchives() const {
|
||||
return _forceLoadAllArchives;
|
||||
bool forceLoadAllArchives() const {
|
||||
return _forceLoadAllArchives;
|
||||
}
|
||||
|
||||
|
||||
/// Archive files (aka static libraries) are normally lazily loaded. That is,
|
||||
/// object files within an archive are only loaded and linked in, if the
|
||||
/// object file contains a DefinedAtom which will replace an existing
|
||||
/// object file contains a DefinedAtom which will replace an existing
|
||||
/// UndefinedAtom. If this method returns true, core linking will also look
|
||||
/// for archive members to replace existing tentative definitions in addition
|
||||
/// to replacing undefines. Note: a "tentative definition" (also called a
|
||||
/// "common" symbols) is a C (but not C++) concept. They are modeled in lld
|
||||
/// as a DefinedAtom with merge() of mergeAsTentative.
|
||||
bool searchArchivesToOverrideTentativeDefinitions() const {
|
||||
return _searchArchivesToOverrideTentativeDefinitions;
|
||||
bool searchArchivesToOverrideTentativeDefinitions() const {
|
||||
return _searchArchivesToOverrideTentativeDefinitions;
|
||||
}
|
||||
|
||||
|
||||
/// Normally core linking will turn a tentative definition into a real
|
||||
/// definition if not replaced by a real DefinedAtom from some object file.
|
||||
/// If this method returns true, core linking will search all supplied
|
||||
/// dynamic shared libraries for symbol names that match remaining tentative
|
||||
/// definitions. If any are found, the corresponding tentative definition
|
||||
/// atom is replaced with SharedLibraryAtom.
|
||||
bool searchSharedLibrariesToOverrideTentativeDefinitions() const {
|
||||
return _searchSharedLibrariesToOverrideTentativeDefinitions;
|
||||
/// atom is replaced with SharedLibraryAtom.
|
||||
bool searchSharedLibrariesToOverrideTentativeDefinitions() const {
|
||||
return _searchSharedLibrariesToOverrideTentativeDefinitions;
|
||||
}
|
||||
|
||||
|
||||
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
|
||||
/// SharedLibraryAtom for the link to be successful. This method controls
|
||||
/// whether core linking prints out a list of remaining UndefinedAtoms.
|
||||
///
|
||||
/// \todo This should be a method core linking calls with a list of the
|
||||
/// \todo This should be a method core linking calls with a list of the
|
||||
/// UndefinedAtoms so that different drivers can format the error message
|
||||
/// as needed.
|
||||
bool printRemainingUndefines() const {
|
||||
return _printRemainingUndefines;
|
||||
bool printRemainingUndefines() const {
|
||||
return _printRemainingUndefines;
|
||||
}
|
||||
|
||||
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
|
||||
/// SharedLibraryAtom for the link to be successful. This method controls
|
||||
/// whether core linking considers remaining undefines to be an error.
|
||||
bool allowRemainingUndefines() const {
|
||||
return _allowRemainingUndefines;
|
||||
bool allowRemainingUndefines() const {
|
||||
return _allowRemainingUndefines;
|
||||
}
|
||||
|
||||
/// In the lld model, a SharedLibraryAtom is a proxy atom for something
|
||||
@@ -135,48 +135,56 @@ public:
|
||||
/// when merging core linking will also verify that they both have the same
|
||||
/// loadName() and if not print a warning.
|
||||
///
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// format the warning as needed.
|
||||
bool warnIfCoalesableAtomsHaveDifferentLoadName() const {
|
||||
return _warnIfCoalesableAtomsHaveDifferentLoadName;
|
||||
bool warnIfCoalesableAtomsHaveDifferentLoadName() const {
|
||||
return _warnIfCoalesableAtomsHaveDifferentLoadName;
|
||||
}
|
||||
|
||||
/// In C/C++ you can mark a function's prototype with
|
||||
/// __attribute__((weak_import)) or __attribute__((weak)) to say the function
|
||||
|
||||
/// In C/C++ you can mark a function's prototype with
|
||||
/// __attribute__((weak_import)) or __attribute__((weak)) to say the function
|
||||
/// may not be available at runtime and/or build time and in which case its
|
||||
/// address will evaluate to NULL. In lld this is modeled using the
|
||||
/// address will evaluate to NULL. In lld this is modeled using the
|
||||
/// UndefinedAtom::canBeNull() method. During core linking, UndefinedAtom
|
||||
/// with the same name are automatically merged. If this method returns
|
||||
/// true, core link also verfies that the canBeNull() value for merged
|
||||
/// UndefinedAtoms are the same and warns if not.
|
||||
///
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// format the warning as needed.
|
||||
bool warnIfCoalesableAtomsHaveDifferentCanBeNull() const {
|
||||
return _warnIfCoalesableAtomsHaveDifferentCanBeNull;
|
||||
bool warnIfCoalesableAtomsHaveDifferentCanBeNull() const {
|
||||
return _warnIfCoalesableAtomsHaveDifferentCanBeNull;
|
||||
}
|
||||
|
||||
|
||||
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
|
||||
/// SharedLibraryAtom for the link to be successful. This method controls
|
||||
/// whether core linking considers remaining undefines from the shared library
|
||||
/// to be an error.
|
||||
bool allowShlibUndefines() const {
|
||||
return _allowShlibUndefines;
|
||||
}
|
||||
|
||||
/// If true, core linking will write the path to each input file to stdout
|
||||
/// (i.e. llvm::outs()) as it is used. This is used to implement the -t
|
||||
/// linker option.
|
||||
///
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// \todo This should be a method core linking calls so that drivers can
|
||||
/// format the line as needed.
|
||||
bool logInputFiles() const {
|
||||
return _logInputFiles;
|
||||
return _logInputFiles;
|
||||
}
|
||||
|
||||
|
||||
/// Parts of LLVM use global variables which are bound to command line
|
||||
/// options (see llvm::cl::Options). This method returns "command line"
|
||||
/// options which are used to configure LLVM's command line settings.
|
||||
/// options which are used to configure LLVM's command line settings.
|
||||
/// For instance the -debug-only XXX option can be used to dynamically
|
||||
/// trace different parts of LLVM and lld.
|
||||
const std::vector<const char*> &llvmOptions() const {
|
||||
return _llvmOptions;
|
||||
}
|
||||
|
||||
|
||||
/// This method returns the sequence of input files for core linking to
|
||||
/// process.
|
||||
/// process.
|
||||
///
|
||||
/// \todo Consider moving this out of TargetInfo so that the same TargetInfo
|
||||
/// object can be reused for different links.
|
||||
@@ -184,85 +192,88 @@ public:
|
||||
return _inputFiles;
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
/// \name Methods used by Drivers to configure TargetInfo
|
||||
/// @{
|
||||
void setOutputPath(StringRef str) { _outputPath = str; }
|
||||
void setEntrySymbolName(StringRef name) { _entrySymbolName = name; }
|
||||
void setDeadStripping(bool enable) { _deadStrip = enable; }
|
||||
void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; }
|
||||
void setSearchArchivesToOverrideTentativeDefinitions(bool search) {
|
||||
_searchArchivesToOverrideTentativeDefinitions = search;
|
||||
void setSearchArchivesToOverrideTentativeDefinitions(bool search) {
|
||||
_searchArchivesToOverrideTentativeDefinitions = search;
|
||||
}
|
||||
void setSearchSharedLibrariesToOverrideTentativeDefinitions(bool search) {
|
||||
_searchSharedLibrariesToOverrideTentativeDefinitions = search;
|
||||
void setSearchSharedLibrariesToOverrideTentativeDefinitions(bool search) {
|
||||
_searchSharedLibrariesToOverrideTentativeDefinitions = search;
|
||||
}
|
||||
void setWarnIfCoalesableAtomsHaveDifferentCanBeNull(bool warn) {
|
||||
_warnIfCoalesableAtomsHaveDifferentCanBeNull = warn;
|
||||
void setWarnIfCoalesableAtomsHaveDifferentCanBeNull(bool warn) {
|
||||
_warnIfCoalesableAtomsHaveDifferentCanBeNull = warn;
|
||||
}
|
||||
void setWarnIfCoalesableAtomsHaveDifferentLoadName(bool warn) {
|
||||
_warnIfCoalesableAtomsHaveDifferentLoadName = warn;
|
||||
void setWarnIfCoalesableAtomsHaveDifferentLoadName(bool warn) {
|
||||
_warnIfCoalesableAtomsHaveDifferentLoadName = warn;
|
||||
}
|
||||
void setForceLoadAllArchives(bool force) {
|
||||
_forceLoadAllArchives = force;
|
||||
void setForceLoadAllArchives(bool force) {
|
||||
_forceLoadAllArchives = force;
|
||||
}
|
||||
void setPrintRemainingUndefines(bool print) {
|
||||
_printRemainingUndefines = print;
|
||||
void setPrintRemainingUndefines(bool print) {
|
||||
_printRemainingUndefines = print;
|
||||
}
|
||||
void setAllowRemainingUndefines(bool allow) {
|
||||
_allowRemainingUndefines = allow;
|
||||
void setAllowRemainingUndefines(bool allow) {
|
||||
_allowRemainingUndefines = allow;
|
||||
}
|
||||
void setLogInputFiles(bool log) {
|
||||
_logInputFiles = log;
|
||||
void setAllowShlibUndefines(bool allow) {
|
||||
_allowShlibUndefines = allow;
|
||||
}
|
||||
void appendInputFile(StringRef path) {
|
||||
void setLogInputFiles(bool log) {
|
||||
_logInputFiles = log;
|
||||
}
|
||||
void appendInputFile(StringRef path) {
|
||||
_inputFiles.emplace_back(LinkerInput(path));
|
||||
}
|
||||
void appendInputFile(std::unique_ptr<llvm::MemoryBuffer> buffer) {
|
||||
void appendInputFile(std::unique_ptr<llvm::MemoryBuffer> buffer) {
|
||||
_inputFiles.emplace_back(LinkerInput(std::move(buffer)));
|
||||
}
|
||||
void appendLLVMOption(const char *opt) {
|
||||
void appendLLVMOption(const char *opt) {
|
||||
_llvmOptions.push_back(opt);
|
||||
}
|
||||
|
||||
|
||||
/// After all set* methods are called, the Driver calls this method
|
||||
/// to validate that there are no missing options or invalid combinations
|
||||
/// of options. If there is a problem, a description of the problem
|
||||
/// is written to the supplied stream.
|
||||
///
|
||||
/// \returns true if there is an error with the current settings.
|
||||
/// \returns true if there is an error with the current settings.
|
||||
virtual bool validate(raw_ostream &diagnostics) = 0;
|
||||
|
||||
|
||||
/// @}
|
||||
/// \name Methods used by Driver::link()
|
||||
/// @{
|
||||
|
||||
|
||||
/// Returns the file system path to which the linked output should be written.
|
||||
///
|
||||
/// \todo To support in-memory linking, we need an abstraction that allows
|
||||
/// the linker to write to an in-memory buffer.
|
||||
StringRef outputPath() const {
|
||||
return _outputPath;
|
||||
StringRef outputPath() const {
|
||||
return _outputPath;
|
||||
}
|
||||
|
||||
|
||||
/// Abstract method to parse a supplied input file buffer into one or
|
||||
/// more lld::File objects. Subclasses of TargetInfo must implement this
|
||||
/// method.
|
||||
/// more lld::File objects. Subclasses of TargetInfo must implement this
|
||||
/// method.
|
||||
///
|
||||
/// \param inputBuff This is an in-memory read-only copy of the input file.
|
||||
/// If the resulting lld::File object will contain pointers into
|
||||
/// this memory buffer, the lld::File object should take ownership
|
||||
/// of the buffer. Otherwise core linking will maintain ownership of the
|
||||
/// buffer and delete it at some point.
|
||||
/// of the buffer. Otherwise core linking will maintain ownership of the
|
||||
/// buffer and delete it at some point.
|
||||
///
|
||||
/// \param [out] result The instantiated lld::File object is returned here.
|
||||
/// The \p result is a vector because some input files parse into more than
|
||||
/// one lld::File (e.g. YAML).
|
||||
/// one lld::File (e.g. YAML).
|
||||
virtual error_code parseFile(std::unique_ptr<MemoryBuffer> &inputBuff,
|
||||
std::vector<std::unique_ptr<File>> &result) const = 0;
|
||||
|
||||
|
||||
/// This is a wrapper around parseFile() where the input file is specified
|
||||
/// by file system path. The default implementation reads the input file
|
||||
/// into a memory buffer and calls parseFile().
|
||||
@@ -276,12 +287,12 @@ public:
|
||||
/// to add file format specific "files" to set of files to be linked. This is
|
||||
/// how file format specific atoms can be added to the link.
|
||||
virtual void addImplicitFiles(InputFiles&) const;
|
||||
|
||||
|
||||
/// This method is called by core linking to build the list of Passes to be
|
||||
/// run on the merged/linked graph of all input files.
|
||||
virtual void addPasses(PassManager &pm) const;
|
||||
|
||||
/// Calls through to the writeFile() method on the specified Writer.
|
||||
/// Calls through to the writeFile() method on the specified Writer.
|
||||
///
|
||||
/// \param linkedFile This is the merged/linked graph of all input file Atoms.
|
||||
virtual error_code writeFile(const File &linkedFile) const;
|
||||
@@ -289,18 +300,18 @@ public:
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Methods needed by YAML I/O and error messages to convert Kind values
|
||||
/// \name Methods needed by YAML I/O and error messages to convert Kind values
|
||||
/// to and from strings.
|
||||
/// @{
|
||||
|
||||
/// Abstract method to parse a kind name string into an integral
|
||||
|
||||
/// Abstract method to parse a kind name string into an integral
|
||||
/// Reference::Kind
|
||||
virtual ErrorOr<Reference::Kind> relocKindFromString(StringRef str) const = 0;
|
||||
|
||||
/// Abstract method to return the name for a given integral
|
||||
/// Abstract method to return the name for a given integral
|
||||
/// Reference::Kind.
|
||||
virtual ErrorOr<std::string> stringFromRelocKind(Reference::Kind k) const = 0;
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -310,7 +321,7 @@ protected:
|
||||
|
||||
/// Abstract method to lazily instantiate the Writer.
|
||||
virtual Writer &writer() const = 0;
|
||||
|
||||
|
||||
|
||||
StringRef _outputPath;
|
||||
StringRef _entrySymbolName;
|
||||
@@ -324,6 +335,7 @@ protected:
|
||||
bool _printRemainingUndefines;
|
||||
bool _allowRemainingUndefines;
|
||||
bool _logInputFiles;
|
||||
bool _allowShlibUndefines;
|
||||
std::vector<StringRef> _deadStripRoots;
|
||||
std::vector<LinkerInput> _inputFiles;
|
||||
std::vector<const char*> _llvmOptions;
|
||||
|
||||
@@ -45,6 +45,13 @@ public:
|
||||
bool mergeCommonStrings() const { return _mergeCommonStrings; }
|
||||
virtual uint64_t getBaseAddress() const { return _baseAddress; }
|
||||
|
||||
/// This controls if undefined atoms need to be created for undefines that are
|
||||
/// present in a SharedLibrary. If this option is set, undefined atoms are
|
||||
/// created for every undefined symbol that are present in the dynamic table
|
||||
/// in the shared library
|
||||
bool useShlibUndefines() const { return _useShlibUndefines; }
|
||||
/// @}
|
||||
|
||||
/// \brief Does this relocation belong in the dynamic relocation table?
|
||||
///
|
||||
/// This table is evaluated at loadtime by the dynamic loader and is
|
||||
@@ -60,7 +67,7 @@ public:
|
||||
|
||||
virtual error_code parseFile(std::unique_ptr<MemoryBuffer> &mb,
|
||||
std::vector<std::unique_ptr<File>> &result) const;
|
||||
|
||||
|
||||
static std::unique_ptr<ELFTargetInfo> create(llvm::Triple);
|
||||
|
||||
/// \brief Does this relocation belong in the dynamic plt relocation table?
|
||||
@@ -79,7 +86,7 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Does the output have dynamic sections.
|
||||
bool isDynamic() const;
|
||||
bool isDynamic() const;
|
||||
|
||||
template <typename ELFT>
|
||||
lld::elf::TargetHandler<ELFT> &getTargetHandler() const {
|
||||
@@ -95,16 +102,17 @@ public:
|
||||
void setNoInhibitExec(bool v) { _noInhibitExec = v; }
|
||||
void setIsStaticExecutable(bool v) { _isStaticExecutable = v; }
|
||||
void setMergeCommonStrings(bool v) { _mergeCommonStrings = v; }
|
||||
void appendSearchPath(StringRef dirPath) {
|
||||
void setUseShlibUndefines(bool use) { _useShlibUndefines = use; }
|
||||
void appendSearchPath(StringRef dirPath) {
|
||||
_inputSearchPaths.push_back(dirPath);
|
||||
}
|
||||
/// Searches directories then calls appendInputFile()
|
||||
bool appendLibrary(StringRef libName);
|
||||
|
||||
|
||||
private:
|
||||
ELFTargetInfo() LLVM_DELETED_FUNCTION;
|
||||
protected:
|
||||
ELFTargetInfo(llvm::Triple);
|
||||
ELFTargetInfo(llvm::Triple);
|
||||
|
||||
virtual Writer &writer() const;
|
||||
|
||||
@@ -117,6 +125,7 @@ protected:
|
||||
bool _noInhibitExec;
|
||||
bool _mergeCommonStrings;
|
||||
bool _runLayoutPass;
|
||||
bool _useShlibUndefines;
|
||||
std::vector<StringRef> _inputSearchPaths;
|
||||
llvm::BumpPtrAllocator _extraStrings;
|
||||
mutable std::unique_ptr<Reader> _elfReader;
|
||||
|
||||
@@ -181,11 +181,11 @@ void Resolver::addAtoms(const std::vector<const DefinedAtom*>& newAtoms) {
|
||||
// ask symbol table if any definitionUndefined atoms still exist
|
||||
// if so, keep searching libraries until no more atoms being added
|
||||
void Resolver::resolveUndefines() {
|
||||
const bool searchArchives =
|
||||
_targetInfo.searchArchivesToOverrideTentativeDefinitions();
|
||||
const bool searchSharedLibs =
|
||||
_targetInfo.searchSharedLibrariesToOverrideTentativeDefinitions();
|
||||
|
||||
const bool searchArchives =
|
||||
_targetInfo.searchArchivesToOverrideTentativeDefinitions();
|
||||
const bool searchSharedLibs =
|
||||
_targetInfo.searchSharedLibrariesToOverrideTentativeDefinitions();
|
||||
|
||||
// keep looping until no more undefines were added in last loop
|
||||
unsigned int undefineGenCount = 0xFFFFFFFF;
|
||||
while (undefineGenCount != _symbolTable.size()) {
|
||||
@@ -311,15 +311,23 @@ bool Resolver::checkUndefines(bool final) {
|
||||
|
||||
// error message about missing symbols
|
||||
if (!undefinedAtoms.empty()) {
|
||||
// FIXME: need diagonstics interface for writing error messages
|
||||
// FIXME: need diagnostics interface for writing error messages
|
||||
bool foundUndefines = false;
|
||||
for (const UndefinedAtom *undefAtom : undefinedAtoms) {
|
||||
if (undefAtom->canBeNull() == UndefinedAtom::canBeNullNever) {
|
||||
foundUndefines = true;
|
||||
if (_targetInfo.printRemainingUndefines()) {
|
||||
llvm::errs() << "Undefined Symbol: " << undefAtom->file().path()
|
||||
<< " : " << undefAtom->name() << "\n";
|
||||
const File &f = undefAtom->file();
|
||||
bool isAtomUndefined = false;
|
||||
if (isa<SharedLibraryFile>(f)) {
|
||||
if (!_targetInfo.allowShlibUndefines()) {
|
||||
foundUndefines = true;
|
||||
isAtomUndefined = true;
|
||||
}
|
||||
} else if (undefAtom->canBeNull() == UndefinedAtom::canBeNullNever) {
|
||||
foundUndefines = true;
|
||||
isAtomUndefined = true;
|
||||
}
|
||||
if (isAtomUndefined && _targetInfo.printRemainingUndefines()) {
|
||||
llvm::errs() << "Undefined Symbol: " << undefAtom->file().path()
|
||||
<< " : " << undefAtom->name() << "\n";
|
||||
}
|
||||
}
|
||||
if (foundUndefines) {
|
||||
|
||||
@@ -15,23 +15,17 @@
|
||||
namespace lld {
|
||||
|
||||
TargetInfo::TargetInfo()
|
||||
: Reader(*this)
|
||||
, _deadStrip(false)
|
||||
, _globalsAreDeadStripRoots(false)
|
||||
, _searchArchivesToOverrideTentativeDefinitions(false)
|
||||
, _searchSharedLibrariesToOverrideTentativeDefinitions(false)
|
||||
, _warnIfCoalesableAtomsHaveDifferentCanBeNull(false)
|
||||
, _warnIfCoalesableAtomsHaveDifferentLoadName(false)
|
||||
, _forceLoadAllArchives(false)
|
||||
, _printRemainingUndefines(true)
|
||||
, _allowRemainingUndefines(false)
|
||||
, _logInputFiles(false)
|
||||
{
|
||||
}
|
||||
: Reader(*this), _deadStrip(false), _globalsAreDeadStripRoots(false),
|
||||
_searchArchivesToOverrideTentativeDefinitions(false),
|
||||
_searchSharedLibrariesToOverrideTentativeDefinitions(false),
|
||||
_warnIfCoalesableAtomsHaveDifferentCanBeNull(false),
|
||||
_warnIfCoalesableAtomsHaveDifferentLoadName(false),
|
||||
_forceLoadAllArchives(false), _printRemainingUndefines(true),
|
||||
_allowRemainingUndefines(false), _logInputFiles(false),
|
||||
_allowShlibUndefines(false) {}
|
||||
|
||||
TargetInfo::~TargetInfo() {}
|
||||
|
||||
|
||||
error_code TargetInfo::readFile(StringRef path,
|
||||
std::vector<std::unique_ptr<File>> &result) const {
|
||||
OwningPtr<llvm::MemoryBuffer> opmb;
|
||||
|
||||
@@ -77,33 +77,33 @@ bool GnuLdDriver::linkELF(int argc, const char *argv[],
|
||||
std::unique_ptr<ELFTargetInfo> options(parse(argc, argv, diagnostics));
|
||||
if (!options)
|
||||
return true;
|
||||
|
||||
|
||||
return link(*options, diagnostics);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[],
|
||||
raw_ostream &diagnostics) {
|
||||
std::unique_ptr<ELFTargetInfo>
|
||||
GnuLdDriver::parse(int argc, const char *argv[], raw_ostream &diagnostics) {
|
||||
// Parse command line options using LDOptions.td
|
||||
std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
|
||||
GnuLdOptTable table;
|
||||
unsigned missingIndex;
|
||||
unsigned missingCount;
|
||||
parsedArgs.reset(table.ParseArgs(&argv[1], &argv[argc],
|
||||
missingIndex, missingCount));
|
||||
parsedArgs.reset(
|
||||
table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
|
||||
if (missingCount) {
|
||||
diagnostics << "error: missing arg value for '"
|
||||
<< parsedArgs->getArgString(missingIndex)
|
||||
<< "' expected " << missingCount << " argument(s).\n";
|
||||
diagnostics << "error: missing arg value for '"
|
||||
<< parsedArgs->getArgString(missingIndex) << "' expected "
|
||||
<< missingCount << " argument(s).\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto it = parsedArgs->filtered_begin(OPT_UNKNOWN),
|
||||
ie = parsedArgs->filtered_end(); it != ie; ++it) {
|
||||
diagnostics << "warning: ignoring unknown argument: "
|
||||
<< (*it)->getAsString(*parsedArgs) << "\n";
|
||||
diagnostics << "warning: ignoring unknown argument: " << (*it)->getAsString(
|
||||
*parsedArgs)
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
||||
// Handle --help
|
||||
if (parsedArgs->getLastArg(OPT_help)) {
|
||||
table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
|
||||
@@ -112,37 +112,36 @@ std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[],
|
||||
|
||||
// Use -target or use default target triple to instantiate TargetInfo
|
||||
llvm::Triple triple;
|
||||
if (llvm::opt::Arg *trip = parsedArgs->getLastArg(OPT_target))
|
||||
if (llvm::opt::Arg *trip = parsedArgs->getLastArg(OPT_target))
|
||||
triple = llvm::Triple(trip->getValue());
|
||||
else
|
||||
triple = getDefaultTarget(argv[0]);
|
||||
std::unique_ptr<ELFTargetInfo> options(ELFTargetInfo::create(triple));
|
||||
|
||||
|
||||
if (!options) {
|
||||
diagnostics << "unknown target triple\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Handle -e xxx
|
||||
if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry))
|
||||
options->setEntrySymbolName(entry->getValue());
|
||||
|
||||
|
||||
// Handle -emit-yaml
|
||||
if (parsedArgs->getLastArg(OPT_emit_yaml))
|
||||
options->setOutputYAML(true);
|
||||
|
||||
|
||||
// Handle -o xxx
|
||||
if (llvm::opt::Arg *output = parsedArgs->getLastArg(OPT_output))
|
||||
if (llvm::opt::Arg *output = parsedArgs->getLastArg(OPT_output))
|
||||
options->setOutputPath(output->getValue());
|
||||
else if (options->outputYAML())
|
||||
options->setOutputPath("-"); // yaml writes to stdout by default
|
||||
options->setOutputPath("-"); // yaml writes to stdout by default
|
||||
else
|
||||
options->setOutputPath("a.out");
|
||||
|
||||
|
||||
// Handle -r, -shared, or -static
|
||||
if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_relocatable,
|
||||
OPT_shared,
|
||||
OPT_static)) {
|
||||
if (llvm::opt::Arg *kind =
|
||||
parsedArgs->getLastArg(OPT_relocatable, OPT_shared, OPT_static)) {
|
||||
switch (kind->getOption().getID()) {
|
||||
case OPT_relocatable:
|
||||
options->setOutputFileType(llvm::ELF::ET_REL);
|
||||
@@ -151,60 +150,76 @@ std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[],
|
||||
break;
|
||||
case OPT_shared:
|
||||
options->setOutputFileType(llvm::ELF::ET_DYN);
|
||||
options->setAllowShlibUndefines(true);
|
||||
options->setUseShlibUndefines(false);
|
||||
break;
|
||||
case OPT_static:
|
||||
options->setOutputFileType(llvm::ELF::ET_EXEC);
|
||||
options->setIsStaticExecutable(true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
options->setOutputFileType(llvm::ELF::ET_EXEC);
|
||||
options->setIsStaticExecutable(false);
|
||||
options->setAllowShlibUndefines(false);
|
||||
options->setUseShlibUndefines(true);
|
||||
}
|
||||
else {
|
||||
options->setOutputFileType(llvm::ELF::ET_EXEC);
|
||||
options->setIsStaticExecutable(false);
|
||||
}
|
||||
|
||||
|
||||
// Handle --noinhibit-exec
|
||||
if (parsedArgs->getLastArg(OPT_noinhibit_exec))
|
||||
if (parsedArgs->getLastArg(OPT_noinhibit_exec))
|
||||
options->setAllowRemainingUndefines(true);
|
||||
|
||||
|
||||
// Handle --force-load
|
||||
if (parsedArgs->getLastArg(OPT_force_load))
|
||||
options->setForceLoadAllArchives(true);
|
||||
|
||||
|
||||
// Handle --merge-strings
|
||||
if (parsedArgs->getLastArg(OPT_merge_strings))
|
||||
options->setMergeCommonStrings(true);
|
||||
|
||||
|
||||
// Handle -t
|
||||
if (parsedArgs->getLastArg(OPT_t))
|
||||
options->setLogInputFiles(true);
|
||||
|
||||
|
||||
// Handle --no-allow-shlib-undefined
|
||||
if (parsedArgs->getLastArg(OPT_no_allow_shlib_undefs))
|
||||
options->setAllowShlibUndefines(false);
|
||||
|
||||
// Handle --allow-shlib-undefined
|
||||
if (parsedArgs->getLastArg(OPT_allow_shlib_undefs))
|
||||
options->setAllowShlibUndefines(true);
|
||||
|
||||
// Handle --use-shlib-undefs
|
||||
if (parsedArgs->getLastArg(OPT_use_shlib_undefs))
|
||||
options->setUseShlibUndefines(true);
|
||||
|
||||
// Handle -Lxxx
|
||||
for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_L),
|
||||
ie = parsedArgs->filtered_end();
|
||||
it != ie; ++it) {
|
||||
it != ie; ++it) {
|
||||
options->appendSearchPath((*it)->getValue());
|
||||
}
|
||||
|
||||
|
||||
// Copy mllvm
|
||||
for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_mllvm),
|
||||
ie = parsedArgs->filtered_end();
|
||||
it != ie; ++it) {
|
||||
options->appendLLVMOption((*it)->getValue());
|
||||
it != ie; ++it) {
|
||||
options->appendLLVMOption((*it)->getValue());
|
||||
}
|
||||
|
||||
|
||||
// Handle input files (full paths and -lxxx)
|
||||
for (llvm::opt::arg_iterator it = parsedArgs->filtered_begin(OPT_INPUT,OPT_l),
|
||||
ie = parsedArgs->filtered_end();
|
||||
it != ie; ++it) {
|
||||
for (llvm::opt::arg_iterator
|
||||
it = parsedArgs->filtered_begin(OPT_INPUT, OPT_l),
|
||||
ie = parsedArgs->filtered_end();
|
||||
it != ie; ++it) {
|
||||
switch ((*it)->getOption().getID()) {
|
||||
case OPT_INPUT:
|
||||
options->appendInputFile((*it)->getValue());
|
||||
break;
|
||||
case OPT_l:
|
||||
if (options->appendLibrary((*it)->getValue())) {
|
||||
diagnostics << "Failed to find library for "
|
||||
<< (*it)->getValue() << "\n";
|
||||
diagnostics << "Failed to find library for " << (*it)->getValue()
|
||||
<< "\n";
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
@@ -212,7 +227,7 @@ std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[],
|
||||
llvm_unreachable("input option type not handled");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate the combination of options used.
|
||||
if (options->validate(diagnostics))
|
||||
return nullptr;
|
||||
@@ -220,22 +235,20 @@ std::unique_ptr<ELFTargetInfo> GnuLdDriver::parse(int argc, const char *argv[],
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
/// Get the default target triple based on either the program name
|
||||
/// Get the default target triple based on either the program name
|
||||
/// (e.g. "x86-ibm-linux-lld") or the primary target llvm was configured for.
|
||||
llvm::Triple GnuLdDriver::getDefaultTarget(const char *progName) {
|
||||
SmallVector<StringRef, 4> components;
|
||||
llvm::SplitString(llvm::sys::path::stem(progName), components, "-");
|
||||
// If has enough parts to be start with a triple.
|
||||
if (components.size() >= 4) {
|
||||
llvm::Triple triple(components[0], components[1], components[2],
|
||||
components[3]);
|
||||
llvm::Triple triple(components[0], components[1], components[2],
|
||||
components[3]);
|
||||
// If first component looks like an arch.
|
||||
if (triple.getArch() != llvm::Triple::UnknownArch)
|
||||
return triple;
|
||||
}
|
||||
|
||||
|
||||
// Fallback to use whatever default triple llvm was configured for.
|
||||
return llvm::Triple(llvm::sys::getDefaultTargetTriple());
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,15 @@ def merge_strings : Flag<["--"], "merge-strings">,
|
||||
HelpText<"Merge common strings across mergeable sections">;
|
||||
|
||||
|
||||
def no_allow_shlib_undefs : Flag<["--"], "no-allow-shlib-undefined">,
|
||||
HelpText<"Donot allow undefined symbols from dynamic library when creating executables">;
|
||||
|
||||
def allow_shlib_undefs : Flag<["--"], "allow-shlib-undefined">,
|
||||
HelpText<"Allow undefined symbols from dynamic library when creating executables">;
|
||||
|
||||
def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">,
|
||||
HelpText<"Resolve undefined symbols from dynamic libraries">;
|
||||
|
||||
// extensions
|
||||
def emit_yaml : Flag<["-"], "emit-yaml">,
|
||||
HelpText<"Write YAML instead of ELF">;
|
||||
|
||||
@@ -126,17 +126,12 @@ class ELFUndefinedAtom LLVM_FINAL : public lld::UndefinedAtom {
|
||||
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
|
||||
|
||||
public:
|
||||
ELFUndefinedAtom(const ELFFile<ELFT> &file, StringRef name,
|
||||
const Elf_Sym *symbol)
|
||||
ELFUndefinedAtom(const File &file, StringRef name, const Elf_Sym *symbol)
|
||||
: _owningFile(file), _name(name), _symbol(symbol) {}
|
||||
|
||||
virtual const ELFFile<ELFT> &file() const {
|
||||
return _owningFile;
|
||||
}
|
||||
virtual const File &file() const { return _owningFile; }
|
||||
|
||||
virtual StringRef name() const {
|
||||
return _name;
|
||||
}
|
||||
virtual StringRef name() const { return _name; }
|
||||
|
||||
// FIXME: What distinguishes a symbol in ELF that can help decide if the
|
||||
// symbol is undefined only during build and not runtime? This will make us
|
||||
@@ -149,7 +144,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const ELFFile<ELFT> &_owningFile;
|
||||
const File &_owningFile;
|
||||
StringRef _name;
|
||||
const Elf_Sym *_symbol;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#ifndef LLD_READER_WRITER_ELF_DYNAMIC_FILE_H
|
||||
#define LLD_READER_WRITER_ELF_DYNAMIC_FILE_H
|
||||
|
||||
#include "Atoms.h"
|
||||
|
||||
#include "lld/Core/SharedLibraryFile.h"
|
||||
#include "lld/ReaderWriter/ELFTargetInfo.h"
|
||||
|
||||
@@ -27,6 +29,8 @@ public:
|
||||
std::unique_ptr<DynamicFile> file(
|
||||
new DynamicFile(ti, mb->getBufferIdentifier()));
|
||||
llvm::OwningPtr<llvm::object::Binary> binaryFile;
|
||||
bool useShlibUndefines = ti.useShlibUndefines();
|
||||
|
||||
if (error_code ec = createBinary(mb.release(), binaryFile))
|
||||
return ec;
|
||||
|
||||
@@ -51,18 +55,25 @@ public:
|
||||
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))
|
||||
if (error_code ec = obj.getSymbolName(
|
||||
obj.getDynamicSymbolTableSectionHeader(), &*i, name))
|
||||
return ec;
|
||||
file->_nameToSym[name]._symbol = &*i;
|
||||
|
||||
// TODO: Read undefined dynamic symbols into _undefinedAtoms.
|
||||
// TODO: Add absolute symbols
|
||||
if (i->st_shndx == llvm::ELF::SHN_ABS)
|
||||
continue;
|
||||
|
||||
if (useShlibUndefines && (i->st_shndx == llvm::ELF::SHN_UNDEF)) {
|
||||
// Create an undefined atom.
|
||||
if (!name.empty()) {
|
||||
auto *newAtom =
|
||||
new (file->_alloc) ELFUndefinedAtom<ELFT>(*file.get(), name, &*i);
|
||||
file->_undefinedAtoms._atoms.push_back(newAtom);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
file->_nameToSym[name]._symbol = &*i;
|
||||
}
|
||||
|
||||
return std::move(file);
|
||||
|
||||
@@ -29,7 +29,8 @@ ELFTargetInfo::ELFTargetInfo(llvm::Triple triple)
|
||||
, _outputYAML(false)
|
||||
, _noInhibitExec(false)
|
||||
, _mergeCommonStrings(false)
|
||||
, _runLayoutPass(true) {
|
||||
, _runLayoutPass(true)
|
||||
, _useShlibUndefines(false) {
|
||||
}
|
||||
|
||||
bool ELFTargetInfo::is64Bits() const {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined
|
||||
RUN: llvm-objdump -p %t | FileCheck %s
|
||||
|
||||
CHECK: PHDR
|
||||
|
||||
27
lld/test/elf/dynamic-undef.test
Normal file
27
lld/test/elf/dynamic-undef.test
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# This test creates a executable and tests the options that are used to
|
||||
# to create an executable and a shared library
|
||||
#
|
||||
# This test will fail because there are unresolved symbols from the shared
|
||||
# library and we are not passing --allow-shlib-undefined
|
||||
RUN: not lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main 2> %t1
|
||||
RUN: FileCheck -check-prefix=EXEC %s < %t1
|
||||
# This test will pass because of --allow-shlib-undefined
|
||||
RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined
|
||||
# Test creation of shared library, this should pass because we are using
|
||||
# shared option and by default, dynamic library wouldnot create undefined atoms
|
||||
# from the input shared library
|
||||
RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main -shared
|
||||
# Test creation of shared library, this should fail because we are using
|
||||
# shared option setting the options to use the shared library undefines to
|
||||
# create undefined atoms from the input shared library
|
||||
RUN: not lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main -shared \
|
||||
RUN: --use-shlib-undefines --no-allow-shlib-undefined 2> %t2
|
||||
RUN: FileCheck -check-prefix=SHLIB %s < %t2
|
||||
|
||||
EXEC: Undefined Symbol: {{[Ia-z.\/]+}}shared.so-x86-64 : puts
|
||||
SHLIB: Undefined Symbol: {{[Ia-z.\/]+}}shared.so-x86-64 : puts
|
||||
@@ -1,7 +1,8 @@
|
||||
RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main
|
||||
RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined
|
||||
RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \
|
||||
RUN: %p/Inputs/shared.so-x86-64 -emit-yaml -o %t2 --noinhibit-exec
|
||||
RUN: %p/Inputs/shared.so-x86-64 -emit-yaml -o %t2 --allow-shlib-undefined \
|
||||
RUN: --noinhibit-exec
|
||||
RUN: llvm-objdump -p %t >> %t2
|
||||
RUN: llvm-readobj -s -dyn-symbols -dynamic-table %t >> %t2
|
||||
RUN: FileCheck %s < %t2
|
||||
|
||||
Reference in New Issue
Block a user