Index: include/lld/Driver/GnuLdInputGraph.h =================================================================== --- include/lld/Driver/GnuLdInputGraph.h +++ include/lld/Driver/GnuLdInputGraph.h @@ -35,17 +35,20 @@ class Attributes { public: Attributes() - : _isWholeArchive(false), _asNeeded(false), _isDashlPrefix(false) {} + : _isWholeArchive(false), _asNeeded(false), _isDashlPrefix(false), + _isSysRooted(false) {} void setWholeArchive(bool isWholeArchive) { _isWholeArchive = isWholeArchive; } void setAsNeeded(bool asNeeded) { _asNeeded = asNeeded; } void setDashlPrefix(bool isDashlPrefix) { _isDashlPrefix = isDashlPrefix; } + void setSysRooted(bool isSysRooted) { _isSysRooted = isSysRooted; } public: bool _isWholeArchive; bool _asNeeded; bool _isDashlPrefix; + bool _isSysRooted; }; ELFFileNode(ELFLinkingContext &ctx, StringRef path, Attributes &attributes) Index: include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- include/lld/ReaderWriter/ELFLinkingContext.h +++ include/lld/ReaderWriter/ELFLinkingContext.h @@ -173,6 +173,13 @@ /// Searches directories for a match on the input File ErrorOr searchLibrary(StringRef libName) const; + /// \brief Searches directories for a match on the input file. + /// If \p fileName is an absolute path and \p isSysRooted is true, check + /// the file under sysroot directory. If \p fileName is a relative path + /// and is not in the current directory, search the file through library + /// search directories. + ErrorOr searchFile(StringRef fileName, bool isSysRooted) const; + /// Get the entry symbol name StringRef entrySymbolName() const override; @@ -207,6 +214,8 @@ StringRef sharedObjectName() const { return _soname; } + StringRef getSysroot() const { return _sysrootPath; } + /// \brief Set path to the system root void setSysroot(StringRef path) { _sysrootPath = path; Index: lib/Driver/GnuLdDriver.cpp =================================================================== --- lib/Driver/GnuLdDriver.cpp +++ lib/Driver/GnuLdDriver.cpp @@ -153,9 +153,9 @@ } llvm::ErrorOr ELFFileNode::getPath(const LinkingContext &) const { - if (!_attributes._isDashlPrefix) - return _path; - return _elfLinkingContext.searchLibrary(_path); + if (_attributes._isDashlPrefix) + return _elfLinkingContext.searchLibrary(_path); + return _elfLinkingContext.searchFile(_path, _attributes._isSysRooted); } std::string ELFFileNode::errStr(error_code errc) { Index: lib/Driver/GnuLdInputGraph.cpp =================================================================== --- lib/Driver/GnuLdInputGraph.cpp +++ lib/Driver/GnuLdInputGraph.cpp @@ -10,6 +10,9 @@ #include "lld/Driver/GnuLdInputGraph.h" #include "lld/ReaderWriter/LinkerScript.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + using namespace lld; /// \brief Parse the input file to lld::File. @@ -70,12 +73,27 @@ return error_code::success(); } +static bool isPathUnderSysroot(StringRef sysroot, StringRef path) { + // TODO: Handle the case when 'sysroot' and/or 'path' are symlinks. + if (sysroot.empty() || sysroot.size() >= path.size()) + return false; + if (llvm::sys::path::is_separator(sysroot.back())) + sysroot = sysroot.substr(0, sysroot.size() - 1); + if (!llvm::sys::path::is_separator(path[sysroot.size()])) + return false; + + return llvm::sys::fs::equivalent(sysroot, path.substr(0, sysroot.size())); +} + /// \brief Handle GnuLD script for ELF. error_code ELFGNULdScript::parse(const LinkingContext &ctx, raw_ostream &diagnostics) { ELFFileNode::Attributes attributes; if (error_code ec = GNULdScript::parse(ctx, diagnostics)) return ec; + StringRef sysRoot = _elfLinkingContext.getSysroot(); + if (!sysRoot.empty() && isPathUnderSysroot(sysRoot, *getPath(ctx))) + attributes.setSysRooted(true); for (const script::Command *c : _linkerScript->_commands) { auto *group = dyn_cast(c); if (!group) Index: lib/ReaderWriter/ELF/ELFLinkingContext.cpp =================================================================== --- lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -247,6 +247,29 @@ return libName; } +ErrorOr ELFLinkingContext::searchFile(StringRef fileName, + bool isSysRooted) const { + SmallString<128> path; + if (llvm::sys::path::is_absolute(fileName) && isSysRooted) { + path.assign(_sysrootPath); + path.append(fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } else if (llvm::sys::fs::exists(fileName)) + return fileName; + + if (llvm::sys::path::is_absolute(fileName)) + return llvm::make_error_code(llvm::errc::no_such_file_or_directory); + + for (StringRef dir : _inputSearchPaths) { + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + return llvm::make_error_code(llvm::errc::no_such_file_or_directory); +} + void ELFLinkingContext::createInternalFiles( std::vector> &files) const { std::unique_ptr file( Index: test/elf/Inputs/group-cmd-search-1.ls =================================================================== --- /dev/null +++ test/elf/Inputs/group-cmd-search-1.ls @@ -0,0 +1 @@ +GROUP ( shared.so-x86-64 ) Index: test/elf/Inputs/group-cmd-search-2.ls =================================================================== --- /dev/null +++ test/elf/Inputs/group-cmd-search-2.ls @@ -0,0 +1 @@ +GROUP ( /shared.so-x86-64 ) Index: test/elf/group-cmd-search.test =================================================================== --- /dev/null +++ test/elf/group-cmd-search.test @@ -0,0 +1,95 @@ +/* + In general the linker scripts's GROUP command works like a pair + of command line options --start-group/--end-group. But there is + a difference in the files look up algorithm. + + The --start-group/--end-group commands use a trivial approach: + a) If the path has '-l' prefix, add 'lib' prefix and '.a'/'.so' + suffix and search the path through library search directories. + b) Otherwise, use the path 'as-is'. + + The GROUP command implements more compicated approach: + a) If the path has '-l' prefix, add 'lib' prefix and '.a'/'.so' + suffix and search the path through library search directories. + b) If the path does not have '-l' prefix, and sysroot is configured, + and the path starts with the / character, and the script being + processed is located inside the sysroot, search the path under + the sysroot. Otherwise, try to open the path in the current + directory. If it is not found, search through library search + directories. +*/ + +/* + This link should finish successfully. The --start-group/--end-group + contains an existing absolute path to the file. + +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group %p/Inputs/shared.so-x86-64 --end-group -o %t1 +*/ + +/* + This link should fail with unknown input file format error. + There is no shared.so-x86-64 file in the current directory. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group shared.so-x86-64 --end-group -o %t2 +*/ + +/* + This link should fail with unknown input file format error. + The absolute path /shared.so-x86-64 does not exist and the linker + should not attempt to search it under the sysroot directory. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group /shared.so-x86-64 --end-group -o %t3 +*/ + +/* + This link should finish successfully. The group-cmd-search-1.ls + script contains "GROUP ( shared.so-x86-64 )" command and the linker + has to search shared.so-x86-64 through the library search paths. + +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-1.ls -o %t4 +*/ + +/* + This link should fail with unknown input file format error. + The group-cmd-search-2.ls script contains GROUP command with + a non-existing absolute path but there is no --sysroot argument. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-2.ls -o %t5 +*/ + +/* + This link should finish successfully. The group-cmd-search-2.ls + script contains GROUP command with an absolute path and the sysroot + directory is provided. The linker has to search the absolute path + under the sysroot directory. + +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-2.ls -o %t6 +*/ + +/* + This link should fail with unknown input file format error. + The linker script from this file contains GROUP with an absolute + path which can be found under provided sysroot directory. + But the linker script itself is not under the sysroot. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %s -o %t7 +*/ +GROUP ( /shared.so-x86-64 )