| //===- lld/Core/LinkingContext.h - Linker Target Info Interface -----------===// |
| // |
| // The LLVM Linker |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLD_CORE_LINKING_CONTEXT_H |
| #define LLD_CORE_LINKING_CONTEXT_H |
| |
| #include "lld/Core/Error.h" |
| #include "lld/Core/LLVM.h" |
| #include "lld/Core/Node.h" |
| #include "lld/Core/Parallel.h" |
| #include "lld/Core/Reference.h" |
| #include "lld/Core/range.h" |
| #include "lld/Core/Reader.h" |
| #include "llvm/Support/ErrorOr.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <string> |
| #include <vector> |
| |
| namespace lld { |
| class PassManager; |
| class File; |
| class Writer; |
| class Node; |
| class SharedLibraryFile; |
| |
| /// \brief The LinkingContext class encapsulates "what and how" to link. |
| /// |
| /// The base class LinkingContext contains the options needed by core linking. |
| /// Subclasses of LinkingContext have additional options needed by specific |
| /// Writers. For example, ELFLinkingContext has methods that supplies |
| /// options to the ELF Writer and ELF Passes. |
| class LinkingContext { |
| public: |
| /// \brief The types of output file that the linker creates. |
| enum class OutputFileType : uint8_t { |
| Default, // The default output type for this target |
| YAML, // The output type is set to YAML |
| }; |
| |
| virtual ~LinkingContext(); |
| |
| /// \name Methods needed by core linking |
| /// @{ |
| |
| /// Name of symbol linker should use as "entry point" to program, |
| /// usually "main" or "start". |
| virtual StringRef entrySymbolName() const { return _entrySymbolName; } |
| |
| /// 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; } |
| |
| /// 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 { 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 |
| /// be kept live using this method. |
| const std::vector<StringRef> &deadStripRoots() const { |
| return _deadStripRoots; |
| } |
| |
| /// Add the given symbol name to the dead strip root set. Only used if |
| /// deadStrip() returns true. |
| void addDeadStripRoot(StringRef symbolName) { |
| assert(!symbolName.empty() && "Empty symbol cannot be a dead strip root"); |
| _deadStripRoots.push_back(symbolName); |
| } |
| |
| /// 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 |
| /// 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; |
| } |
| |
| /// 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; |
| } |
| |
| /// 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 |
| /// UndefinedAtoms so that different drivers can format the error message |
| /// as needed. |
| 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; } |
| |
| /// In the lld model, a SharedLibraryAtom is a proxy atom for something |
| /// that will be found in a dynamic shared library when the program runs. |
| /// A SharedLibraryAtom optionally contains the name of the shared library |
| /// in which to find the symbol name at runtime. Core linking may merge |
| /// two SharedLibraryAtom with the same name. If this method returns true, |
| /// 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 |
| /// format the warning as needed. |
| 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 |
| /// 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 |
| /// 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 |
| /// format the warning as needed. |
| 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 |
| /// format the line as needed. |
| bool logInputFiles() const { 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. |
| /// 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; } |
| |
| /// \name Methods used by Drivers to configure TargetInfo |
| /// @{ |
| void setOutputPath(StringRef str) { _outputPath = str; } |
| |
| // Set the entry symbol name. You may also need to call addDeadStripRoot() for |
| // the symbol if your platform supports dead-stripping, so that the symbol |
| // will not be removed from the output. |
| void setEntrySymbolName(StringRef name) { |
| _entrySymbolName = name; |
| } |
| |
| void setDeadStripping(bool enable) { _deadStrip = enable; } |
| void setAllowDuplicates(bool enable) { _allowDuplicates = enable; } |
| void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; } |
| void setSearchArchivesToOverrideTentativeDefinitions(bool search) { |
| _searchArchivesToOverrideTentativeDefinitions = search; |
| } |
| void setSearchSharedLibrariesToOverrideTentativeDefinitions(bool search) { |
| _searchSharedLibrariesToOverrideTentativeDefinitions = search; |
| } |
| void setWarnIfCoalesableAtomsHaveDifferentCanBeNull(bool warn) { |
| _warnIfCoalesableAtomsHaveDifferentCanBeNull = warn; |
| } |
| void setWarnIfCoalesableAtomsHaveDifferentLoadName(bool warn) { |
| _warnIfCoalesableAtomsHaveDifferentLoadName = warn; |
| } |
| void setPrintRemainingUndefines(bool print) { |
| _printRemainingUndefines = print; |
| } |
| void setAllowRemainingUndefines(bool allow) { |
| _allowRemainingUndefines = allow; |
| } |
| void setAllowShlibUndefines(bool allow) { _allowShlibUndefines = allow; } |
| void setLogInputFiles(bool log) { _logInputFiles = log; } |
| |
| // Returns true if multiple definitions should not be treated as a |
| // fatal error. |
| bool getAllowDuplicates() const { return _allowDuplicates; } |
| |
| void appendLLVMOption(const char *opt) { _llvmOptions.push_back(opt); } |
| |
| void addAlias(StringRef from, StringRef to) { _aliasSymbols[from] = to; } |
| const std::map<std::string, std::string> &getAliases() const { |
| return _aliasSymbols; |
| } |
| |
| std::vector<std::unique_ptr<Node>> &getNodes() { return _nodes; } |
| const std::vector<std::unique_ptr<Node>> &getNodes() const { return _nodes; } |
| |
| /// Notify the LinkingContext when the symbol table found a name collision. |
| /// The useNew parameter specifies which the symbol table plans to keep, |
| /// but that can be changed by the LinkingContext. This is also an |
| /// opportunity for flavor specific processing. |
| virtual void notifySymbolTableCoalesce(const Atom *existingAtom, |
| const Atom *newAtom, bool &useNew) {} |
| |
| /// This method adds undefined symbols specified by the -u option to the to |
| /// the list of undefined symbols known to the linker. This option essentially |
| /// forces an undefined symbol to be created. You may also need to call |
| /// addDeadStripRoot() for the symbol if your platform supports dead |
| /// stripping, so that the symbol will not be removed from the output. |
| void addInitialUndefinedSymbol(StringRef symbolName) { |
| _initialUndefinedSymbols.push_back(symbolName); |
| } |
| |
| /// Iterators for symbols that appear on the command line. |
| typedef std::vector<StringRef> StringRefVector; |
| typedef StringRefVector::iterator StringRefVectorIter; |
| typedef StringRefVector::const_iterator StringRefVectorConstIter; |
| |
| /// Create linker internal files containing atoms for the linker to include |
| /// during link. Flavors can override this function in their LinkingContext |
| /// to add more internal files. These internal files are positioned before |
| /// the actual input files. |
| virtual void createInternalFiles(std::vector<std::unique_ptr<File> > &) const; |
| |
| /// Return the list of undefined symbols that are specified in the |
| /// linker command line, using the -u option. |
| range<const StringRef *> initialUndefinedSymbols() const { |
| return _initialUndefinedSymbols; |
| } |
| |
| /// 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. |
| bool validate(raw_ostream &diagnostics); |
| |
| /// Formats symbol name for use in error messages. |
| virtual std::string demangle(StringRef symbolName) const { |
| return symbolName; |
| } |
| |
| /// @} |
| /// \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; } |
| |
| /// Set the various output file types that the linker would |
| /// create |
| bool setOutputFileType(StringRef outputFileType) { |
| if (outputFileType.equals_lower("yaml")) { |
| _outputFileType = OutputFileType::YAML; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Returns the output file type that that the linker needs to create. |
| OutputFileType outputFileType() const { return _outputFileType; } |
| |
| /// Accessor for Register object embedded in LinkingContext. |
| const Registry ®istry() const { return _registry; } |
| Registry ®istry() { return _registry; } |
| |
| /// This method is called by core linking to give the Writer a chance |
| /// 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 createImplicitFiles(std::vector<std::unique_ptr<File>> &); |
| |
| /// 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); |
| |
| /// Calls through to the writeFile() method on the specified Writer. |
| /// |
| /// \param linkedFile This is the merged/linked graph of all input file Atoms. |
| virtual std::error_code writeFile(const File &linkedFile) const; |
| |
| /// Return the next ordinal and Increment it. |
| virtual uint64_t getNextOrdinalAndIncrement() const { return _nextOrdinal++; } |
| |
| // This function is called just before the Resolver kicks in. |
| // Derived classes may use it to change the list of input files. |
| virtual void finalizeInputFiles() {} |
| |
| TaskGroup &getTaskGroup() { return _taskGroup; } |
| |
| /// @} |
| protected: |
| LinkingContext(); // Must be subclassed |
| |
| /// Abstract method to lazily instantiate the Writer. |
| virtual Writer &writer() const = 0; |
| |
| /// Method to create an internal file for the entry symbol |
| virtual std::unique_ptr<File> createEntrySymbolFile() const; |
| std::unique_ptr<File> createEntrySymbolFile(StringRef filename) const; |
| |
| /// Method to create an internal file for an undefined symbol |
| virtual std::unique_ptr<File> createUndefinedSymbolFile() const; |
| std::unique_ptr<File> createUndefinedSymbolFile(StringRef filename) const; |
| |
| /// Method to create an internal file for alias symbols |
| std::unique_ptr<File> createAliasSymbolFile() const; |
| |
| StringRef _outputPath; |
| StringRef _entrySymbolName; |
| bool _deadStrip; |
| bool _allowDuplicates; |
| bool _globalsAreDeadStripRoots; |
| bool _searchArchivesToOverrideTentativeDefinitions; |
| bool _searchSharedLibrariesToOverrideTentativeDefinitions; |
| bool _warnIfCoalesableAtomsHaveDifferentCanBeNull; |
| bool _warnIfCoalesableAtomsHaveDifferentLoadName; |
| bool _printRemainingUndefines; |
| bool _allowRemainingUndefines; |
| bool _logInputFiles; |
| bool _allowShlibUndefines; |
| OutputFileType _outputFileType; |
| std::vector<StringRef> _deadStripRoots; |
| std::map<std::string, std::string> _aliasSymbols; |
| std::vector<const char *> _llvmOptions; |
| StringRefVector _initialUndefinedSymbols; |
| std::vector<std::unique_ptr<Node>> _nodes; |
| mutable llvm::BumpPtrAllocator _allocator; |
| mutable uint64_t _nextOrdinal; |
| Registry _registry; |
| |
| private: |
| /// Validate the subclass bits. Only called by validate. |
| virtual bool validateImpl(raw_ostream &diagnostics) = 0; |
| TaskGroup _taskGroup; |
| }; |
| |
| } // end namespace lld |
| |
| #endif |