[ELF] Fix the file look up algorithm used in the linker script GROUP command.

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.

https://www.sourceware.org/binutils/docs-2.24/ld/File-Commands.html

The patch reviewed by Shankar Easwaran, Rui Ueyama.

llvm-svn: 207769
This commit is contained in:
Simon Atanasyan 2014-05-01 16:22:08 +00:00
parent 12963b54a8
commit ffc1c6af49
8 changed files with 154 additions and 4 deletions

View File

@ -35,17 +35,20 @@ public:
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)

View File

@ -173,6 +173,13 @@ public:
/// Searches directories for a match on the input File
ErrorOr<StringRef> 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<StringRef> searchFile(StringRef fileName, bool isSysRooted) const;
/// Get the entry symbol name
StringRef entrySymbolName() const override;
@ -207,6 +214,8 @@ public:
StringRef sharedObjectName() const { return _soname; }
StringRef getSysroot() const { return _sysrootPath; }
/// \brief Set path to the system root
void setSysroot(StringRef path) {
_sysrootPath = path;

View File

@ -153,9 +153,9 @@ static bool parseDefsymAsAlias(StringRef opt, StringRef &sym,
}
llvm::ErrorOr<StringRef> 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) {

View File

@ -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 @@ error_code GNULdScript::parse(const LinkingContext &ctx,
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<script::Group>(c);
if (!group)

View File

@ -247,6 +247,29 @@ ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const {
return libName;
}
ErrorOr<StringRef> 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<std::unique_ptr<File>> &files) const {
std::unique_ptr<SimpleFile> file(

View File

@ -0,0 +1 @@
GROUP ( shared.so-x86-64 )

View File

@ -0,0 +1 @@
GROUP ( /shared.so-x86-64 )

View File

@ -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 )