[ELF] Implement parsing `-l` prefixed items in the `GROUP` linker script command.
authorSimon Atanasyan <simon@atanasyan.com>
Tue, 15 Jul 2014 17:17:30 +0000 (17:17 +0000)
committerSimon Atanasyan <simon@atanasyan.com>
Tue, 15 Jul 2014 17:17:30 +0000 (17:17 +0000)
There are two forms of `-l` prefixed expression:

* -l<libname>
* -l:<filename>

In the first case a linker should construct a full library name
`lib + libname + .[so|a]` and search this library as usual. In the second case
a linker should use the `<filename>` as is and search this file through library
search directories.

The patch reviewed by Shankar Easwaran.

llvm-svn: 213077

lld/include/lld/ReaderWriter/LinkerScript.h
lld/lib/Driver/GnuLdInputGraph.cpp
lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp
lld/lib/ReaderWriter/LinkerScript.cpp
lld/test/LinkerScript/libname-err-1.test [new file with mode: 0644]
lld/test/LinkerScript/libname-err-2.test [new file with mode: 0644]
lld/test/LinkerScript/linker-script.test
lld/test/elf/Inputs/group-cmd-search-3.ls [new file with mode: 0644]
lld/test/elf/group-cmd-search.test

index 7d52f59..ede0e35 100644 (file)
@@ -34,6 +34,7 @@ public:
     unknown,
     eof,
     identifier,
+    libname,
     comma,
     l_paren,
     r_paren,
@@ -145,10 +146,11 @@ private:
 struct Path {
   StringRef _path;
   bool _asNeeded;
+  bool _isDashlPrefix;
 
-  Path() : _asNeeded(false) {}
-  explicit Path(StringRef path, bool asNeeded = false)
-      : _path(path), _asNeeded(asNeeded) {}
+  Path() : _asNeeded(false), _isDashlPrefix(false) {}
+  explicit Path(StringRef path, bool asNeeded = false, bool isLib = false)
+      : _path(path), _asNeeded(asNeeded), _isDashlPrefix(isLib) {}
 };
 
 class Group : public Command {
@@ -169,6 +171,8 @@ public:
       first = false;
       if (path._asNeeded)
         os << "AS_NEEDED(";
+      if (path._isDashlPrefix)
+        os << "-l";
       os << path._path;
       if (path._asNeeded)
         os << ")";
index f634c2e..ac85565 100644 (file)
@@ -96,6 +96,7 @@ std::error_code ELFGNULdScript::parse(const LinkingContext &ctx,
     for (const script::Path &path : group->getPaths()) {
       // TODO : Propagate Set WholeArchive/dashlPrefix
       attributes.setAsNeeded(path._asNeeded);
+      attributes.setDashlPrefix(path._isDashlPrefix);
       auto inputNode = new ELFFileNode(
           _elfLinkingContext, _elfLinkingContext.allocateString(path._path),
           attributes);
index 23036bc..fe33d40 100644 (file)
@@ -163,18 +163,23 @@ static void buildSearchPath(SmallString<128> &path, StringRef dir,
 }
 
 ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const {
+  bool hasColonPrefix = libName[0] == ':';
+  Twine soName =
+      hasColonPrefix ? libName.drop_front() : Twine("lib", libName) + ".so";
+  Twine archiveName =
+      hasColonPrefix ? libName.drop_front() : Twine("lib", libName) + ".a";
   SmallString<128> path;
   for (StringRef dir : _inputSearchPaths) {
     // Search for dynamic library
     if (!_isStaticExecutable) {
       buildSearchPath(path, dir, _sysrootPath);
-      llvm::sys::path::append(path, Twine("lib") + libName + ".so");
+      llvm::sys::path::append(path, soName);
       if (llvm::sys::fs::exists(path.str()))
         return StringRef(*new (_allocator) std::string(path.str()));
     }
     // Search for static libraries too
     buildSearchPath(path, dir, _sysrootPath);
-    llvm::sys::path::append(path, Twine("lib") + libName + ".a");
+    llvm::sys::path::append(path, archiveName);
     if (llvm::sys::fs::exists(path.str()))
       return StringRef(*new (_allocator) std::string(path.str()));
   }
index 796fa24..8ba5f47 100644 (file)
@@ -24,6 +24,7 @@ void Token::dump(raw_ostream &os) const {
     break;
   CASE(eof)
   CASE(identifier)
+  CASE(libname)
   CASE(kw_as_needed)
   CASE(kw_entry)
   CASE(kw_group)
@@ -118,6 +119,24 @@ void Lexer::lex(Token &tok) {
       _buffer = _buffer.drop_front(quotedStringEnd + 1);
       return;
     }
+    // -l<lib name>
+    if (_buffer.startswith("-l")) {
+      _buffer = _buffer.drop_front(2);
+      StringRef::size_type start = 0;
+      if (_buffer[start] == ':')
+        ++start;
+      if (!canStartName(_buffer[start]))
+        // Create 'unknown' token.
+        break;
+      auto libNameEnd =
+          std::find_if(_buffer.begin() + start + 1, _buffer.end(),
+                       [=](char c) { return !canContinueName(c); });
+      StringRef::size_type libNameLen =
+          std::distance(_buffer.begin(), libNameEnd);
+      tok = Token(_buffer.substr(0, libNameLen), Token::libname);
+      _buffer = _buffer.drop_front(libNameLen);
+      return;
+    }
     /// keyword or identifer.
     if (!canStartName(_buffer[0]))
       break;
@@ -295,12 +314,17 @@ Group *Parser::parseGroup() {
 
   std::vector<Path> paths;
 
-  while (_tok._kind == Token::identifier || _tok._kind == Token::kw_as_needed) {
+  while (_tok._kind == Token::identifier || _tok._kind == Token::libname ||
+         _tok._kind == Token::kw_as_needed) {
     switch (_tok._kind) {
     case Token::identifier:
       paths.push_back(Path(_tok._range));
       consumeToken();
       break;
+    case Token::libname:
+      paths.push_back(Path(_tok._range, false, true));
+      consumeToken();
+      break;
     case Token::kw_as_needed:
       if (!parseAsNeeded(paths))
         return nullptr;
@@ -325,9 +349,19 @@ bool Parser::parseAsNeeded(std::vector<Path> &paths) {
   if (!expectAndConsume(Token::l_paren, "expected ("))
     return false;
 
-  while (_tok._kind == Token::identifier) {
-    paths.push_back(Path(_tok._range, true));
-    consumeToken();
+  while (_tok._kind == Token::identifier || _tok._kind == Token::libname) {
+    switch (_tok._kind) {
+    case Token::identifier:
+      paths.push_back(Path(_tok._range, true, false));
+      consumeToken();
+      break;
+    case Token::libname:
+      paths.push_back(Path(_tok._range, true, true));
+      consumeToken();
+      break;
+    default:
+      llvm_unreachable("Invalid token.");
+    }
   }
 
   if (!expectAndConsume(Token::r_paren, "expected )"))
diff --git a/lld/test/LinkerScript/libname-err-1.test b/lld/test/LinkerScript/libname-err-1.test
new file mode 100644 (file)
index 0000000..a31b35e
--- /dev/null
@@ -0,0 +1,11 @@
+/* RUN: linker-script-test %s 2>&1 | FileCheck %s
+*/
+
+OUTPUT_ARCH(i386:x86_64)
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+GROUP( -l### )
+ENTRY(init)
+
+/*
+CHECK: test/LinkerScript/libname-err-1.test:6:10: error: expected )
+*/
diff --git a/lld/test/LinkerScript/libname-err-2.test b/lld/test/LinkerScript/libname-err-2.test
new file mode 100644 (file)
index 0000000..1e30094
--- /dev/null
@@ -0,0 +1,11 @@
+/* RUN: linker-script-test %s 2>&1 | FileCheck %s
+*/
+
+OUTPUT_ARCH(i386:x86_64)
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+GROUP( -l )
+ENTRY(init)
+
+/*
+CHECK: test/LinkerScript/libname-err-2.test:6:10: error: expected )
+*/
index a050c56..4c66435 100644 (file)
@@ -3,7 +3,7 @@
 
 OUTPUT_ARCH(i386:x86_64)
 OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
-GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a  AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ) )
+GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a  AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ) -lm -l:libgcc.a )
 ENTRY(init)
 
 /*
@@ -27,6 +27,8 @@ CHECK: kw_as_needed: AS_NEEDED
 CHECK: l_paren: (
 CHECK: identifier: /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
 CHECK: r_paren: )
+CHECK: libname: m
+CHECK: libname: :libgcc.a
 CHECK: r_paren: )
 CHECK: kw_entry: ENTRY
 CHECK: l_paren: (
@@ -34,6 +36,6 @@ CHECK: identifier: init
 CHECK: r_paren: )
 CHECK: eof:
 CHECK: OUTPUT_FORMAT(elf64-x86-64,elf64-x86-64,elf64-x86-64)
-CHECK: GROUP(/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED(/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2))
+CHECK: GROUP(/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED(/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2) -lm -l:libgcc.a)
 CHECK: ENTRY(init)
 */
diff --git a/lld/test/elf/Inputs/group-cmd-search-3.ls b/lld/test/elf/Inputs/group-cmd-search-3.ls
new file mode 100644 (file)
index 0000000..83ce0ca
--- /dev/null
@@ -0,0 +1 @@
+GROUP ( -l:shared.so-x86-64 -lfnarchive )
index 01af279..2f5c0f4 100644 (file)
@@ -101,6 +101,19 @@ RUN:     %p/Inputs/group-cmd-search-2.ls -o %t6
 */
 
 /*
+  This link should finish successfully. The group-cmd-search-3.ls
+  script contains GROUP command with two elements. The first one
+  has a -l:<path> form and should be found by iterating through
+  lib dirs and searching the 'path' name exactly. The second element
+  has a -l<lib name> form and should be found by constructing a full
+  library name lib<lib name>.a and iterating through lib dirs.
+
+RUN: lld -flavor gnu -target x86_64 -shared \
+RUN:     -L%p/Inputs %p/Inputs/use-shared.x86-64 \
+RUN:     %p/Inputs/group-cmd-search-3.ls -o %t8
+*/
+
+/*
   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.