From 66ac1d61526268d1f81db8ed1d5caccfed2452ec Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 22 Apr 2016 20:21:26 +0000 Subject: [PATCH] ELF: Implement basic support for --version-script. This patch only implements support for version scripts of the form: { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; }; No wildcards are supported, other than for the local entry. Symbol versioning is also not supported. It works by introducing a new Symbol flag which tracks whether a symbol appears in the global section of a version script. This patch also simplifies the logic in SymbolBody::isPreemptible(), and teaches it to handle the case where symbols with default visibility in DSOs do not appear in the dynamic symbol table because of a version script. Fixes PR27482. Differential Revision: http://reviews.llvm.org/D19430 llvm-svn: 267208 --- lld/ELF/CMakeLists.txt | 2 +- lld/ELF/Config.h | 2 + lld/ELF/Driver.cpp | 18 ++-- lld/ELF/Driver.h | 1 - lld/ELF/Options.td | 4 +- lld/ELF/{DynamicList.cpp => SymbolListFile.cpp} | 43 +++++++- lld/ELF/{DynamicList.h => SymbolListFile.h} | 7 +- lld/ELF/SymbolTable.cpp | 16 ++- lld/ELF/SymbolTable.h | 1 + lld/ELF/Symbols.cpp | 31 +++--- lld/ELF/Symbols.h | 5 + lld/test/ELF/lto/internalize-version-script.ll | 22 +++++ lld/test/ELF/no-inhibit-exec.s | 2 +- lld/test/ELF/version-script.s | 126 ++++++++++++++++++++++++ 14 files changed, 245 insertions(+), 35 deletions(-) rename lld/ELF/{DynamicList.cpp => SymbolListFile.cpp} (61%) rename lld/ELF/{DynamicList.h => SymbolListFile.h} (71%) create mode 100644 lld/test/ELF/lto/internalize-version-script.ll create mode 100644 lld/test/ELF/version-script.s diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 4ebfefb..f01f78a 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -5,7 +5,6 @@ add_public_tablegen_target(ELFOptionsTableGen) add_lld_library(lldELF Driver.cpp DriverUtils.cpp - DynamicList.cpp Error.cpp ICF.cpp InputFiles.cpp @@ -15,6 +14,7 @@ add_lld_library(lldELF MarkLive.cpp OutputSections.cpp ScriptParser.cpp + SymbolListFile.cpp SymbolTable.cpp Symbols.cpp Target.cpp diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index b050d9b..d15c254 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -51,6 +51,7 @@ struct Configuration { std::vector DynamicList; std::vector SearchPaths; std::vector Undefined; + std::vector VersionScriptGlobals; bool AllowMultipleDefinition; bool AsNeeded = false; bool Bsymbolic; @@ -85,6 +86,7 @@ struct Configuration { bool Threads; bool Trace; bool Verbose; + bool VersionScript; bool WarnCommon; bool ZExecStack; bool ZNodelete; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 64ec15a..3cfd033 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -9,11 +9,11 @@ #include "Driver.h" #include "Config.h" -#include "DynamicList.h" #include "Error.h" #include "ICF.h" #include "InputFiles.h" #include "LinkerScript.h" +#include "SymbolListFile.h" #include "SymbolTable.h" #include "Target.h" #include "Writer.h" @@ -146,11 +146,6 @@ Optional LinkerDriver::readFile(StringRef Path) { return MBRef; } -void LinkerDriver::readDynamicList(StringRef Path) { - if (Optional Buffer = readFile(Path)) - parseDynamicList(*Buffer); -} - // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef Name) { std::string Path = searchLibrary(Name); @@ -372,10 +367,18 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->Undefined.push_back(Arg->getValue()); if (Args.hasArg(OPT_dynamic_list)) - readDynamicList(getString(Args, OPT_dynamic_list)); + if (Optional Buffer = + readFile(getString(Args, OPT_dynamic_list))) + parseDynamicList(*Buffer); for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) Config->DynamicList.push_back(Arg->getValue()); + + Config->VersionScript = Args.hasArg(OPT_version_script); + if (Config->VersionScript) + if (Optional Buffer = + readFile(getString(Args, OPT_version_script))) + parseVersionScript(*Buffer); } void LinkerDriver::createFiles(opt::InputArgList &Args) { @@ -457,6 +460,7 @@ template void LinkerDriver::link(opt::InputArgList &Args) { Symtab.scanShlibUndefined(); Symtab.scanDynamicList(); + Symtab.scanVersionScript(); Symtab.addCombinedLtoObject(); diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h index 6756fab..eb42e90 100644 --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -32,7 +32,6 @@ private: std::vector getArchiveMembers(MemoryBufferRef MB); llvm::Optional readFile(StringRef Path); void readConfigs(llvm::opt::InputArgList &Args); - void readDynamicList(StringRef Path); void createFiles(llvm::opt::InputArgList &Args); template void link(llvm::opt::InputArgList &Args); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index d199b96..b4cee26 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -160,6 +160,9 @@ def verbose : Flag<["--"], "verbose">; def version : Flag<["--", "-"], "version">, HelpText<"Display the version number">; +def version_script : Separate<["--", "-"], "version-script">, + HelpText<"Read a version script">; + def warn_common : Flag<["--", "-"], "warn-common">, HelpText<"Warn about duplicate common symbols">; @@ -234,7 +237,6 @@ def no_warn_common : Flag<["--", "-"], "no-warn-common">; def no_warn_mismatch : Flag<["--"], "no-warn-mismatch">; def rpath_link : Separate<["--", "-"], "rpath-link">; def rpath_link_eq : Joined<["--", "-"], "rpath-link=">; -def version_script : Separate<["--"], "version-script">; def warn_execstack : Flag<["--"], "warn-execstack">; def warn_shared_textrel : Flag<["--"], "warn-shared-textrel">; def G : Separate<["-"], "G">; diff --git a/lld/ELF/DynamicList.cpp b/lld/ELF/SymbolListFile.cpp similarity index 61% rename from lld/ELF/DynamicList.cpp rename to lld/ELF/SymbolListFile.cpp index 0bfdd0f..8323933 100644 --- a/lld/ELF/DynamicList.cpp +++ b/lld/ELF/SymbolListFile.cpp @@ -1,4 +1,4 @@ -//===- LinkerScript.cpp ---------------------------------------------------===// +//===- SymbolListFile.cpp -------------------------------------------------===// // // The LLVM Linker // @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// -#include "DynamicList.h" +#include "SymbolListFile.h" #include "Config.h" #include "ScriptParser.h" #include "llvm/Support/MemoryBuffer.h" @@ -62,3 +62,42 @@ void DynamicListParser::run() { void elf::parseDynamicList(MemoryBufferRef MB) { DynamicListParser(MB.getBuffer()).run(); } + +// Parse the --version-script argument. We currently only accept the following +// version script syntax: +// +// { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; }; +// +// No wildcards are supported, other than for the local entry. Symbol versioning +// is also not supported. + +class VersionScriptParser final : public ScriptParserBase { +public: + VersionScriptParser(StringRef S) : ScriptParserBase(S) {} + + void run() override; +}; + +void VersionScriptParser::run() { + expect("{"); + if (peek() == "global:") { + next(); + while (!Error) { + Config->VersionScriptGlobals.push_back(next()); + expect(";"); + if (peek() == "local:") + break; + } + } + expect("local:"); + expect("*"); + expect(";"); + expect("}"); + expect(";"); + if (!atEOF()) + setError("expected EOF"); +} + +void elf::parseVersionScript(MemoryBufferRef MB) { + VersionScriptParser(MB.getBuffer()).run(); +} diff --git a/lld/ELF/DynamicList.h b/lld/ELF/SymbolListFile.h similarity index 71% rename from lld/ELF/DynamicList.h rename to lld/ELF/SymbolListFile.h index 35449f6..60362a8 100644 --- a/lld/ELF/DynamicList.h +++ b/lld/ELF/SymbolListFile.h @@ -1,4 +1,4 @@ -//===- DynamicList.h --------------------------------------------*- C++ -*-===// +//===- SymbolListFile.h -----------------------------------------*- C++ -*-===// // // The LLVM Linker // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLD_ELF_DYNAMIC_LIST_H -#define LLD_ELF_DYNAMIC_LIST_H +#ifndef LLD_ELF_SYMBOL_LIST_FILE_H +#define LLD_ELF_SYMBOL_LIST_FILE_H #include "lld/Core/LLVM.h" #include "llvm/Support/MemoryBuffer.h" @@ -17,6 +17,7 @@ namespace lld { namespace elf { void parseDynamicList(MemoryBufferRef MB); +void parseVersionScript(MemoryBufferRef MB); } // namespace elf } // namespace lld diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 9c73a6a..a7337aa 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -290,7 +290,12 @@ template Symbol *SymbolTable::insert(SymbolBody *New) { auto P = Symtab.insert(std::make_pair(Name, NumSyms)); Symbol *Sym; if (P.second) { - Sym = new (Alloc) Symbol{New, STV_DEFAULT, false, false}; + Sym = new (Alloc) Symbol; + Sym->Body = New; + Sym->Visibility = STV_DEFAULT; + Sym->IsUsedInRegularObj = false; + Sym->ExportDynamic = false; + Sym->VersionScriptGlobal = !Config->VersionScript; SymVector.push_back(Sym); } else { Sym = SymVector[P.first->second]; @@ -374,6 +379,15 @@ template void SymbolTable::scanDynamicList() { B->Backref->ExportDynamic = true; } +// This function processes the --version-script option by marking all global +// symbols with the VersionScriptGlobal flag, which acts as a filter on the +// dynamic symbol table. +template void SymbolTable::scanVersionScript() { + for (StringRef S : Config->VersionScriptGlobals) + if (SymbolBody *B = find(S)) + B->Backref->VersionScriptGlobal = true; +} + template class elf::SymbolTable; template class elf::SymbolTable; template class elf::SymbolTable; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index 3d7d5c1..e67bd32 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -61,6 +61,7 @@ public: void scanShlibUndefined(); void scanDynamicList(); + void scanVersionScript(); SymbolBody *find(StringRef Name); void wrap(StringRef Name); InputFile *findFile(SymbolBody *B); diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 206a523..1b82348 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -115,31 +115,26 @@ bool SymbolBody::isPreemptible() const { if (isLocal()) return false; + // Shared symbols resolve to the definition in the DSO. if (isShared()) return true; - if (Backref->Visibility != STV_DEFAULT) + // That's all that can be preempted in a non-DSO. + if (!Config->Shared) return false; - if (isUndefined()) { - if (!isWeak()) - return true; - - // Ideally the static linker should see a definition for every symbol, but - // shared object are normally allowed to have undefined references that the - // static linker never sees a definition for. - if (Config->Shared) - return true; - - // Otherwise, just resolve to 0. - return false; - } + // Undefined symbols in DSOs can only be preempted if they are strong. + // Weak symbols just resolve to zero. + if (isUndefined()) + return !isWeak(); - if (!Config->Shared) - return false; + // -Bsymbolic means that not even default visibility symbols can be preempted. if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc())) return false; - return true; + + // Only default visibility symbols that appear in the dynamic symbol table can + // be preempted. + return Backref->Visibility == STV_DEFAULT && Backref->includeInDynsym(); } template @@ -328,7 +323,7 @@ std::string elf::demangle(StringRef Name) { bool Symbol::includeInDynsym() const { if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED) return false; - return ExportDynamic || Body->isShared(); + return (ExportDynamic && VersionScriptGlobal) || Body->isShared(); } template uint32_t SymbolBody::template getVA(uint32_t) const; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 3b1ceea..7aed12d 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -60,6 +60,11 @@ struct Symbol { // --export-dynamic, and by dynamic lists. unsigned ExportDynamic : 1; + // This flag acts as an additional filter on the dynamic symbol list. It is + // set if there is no version script, or if the symbol appears in the global + // section of the version script. + unsigned VersionScriptGlobal : 1; + bool includeInDynsym() const; }; diff --git a/lld/test/ELF/lto/internalize-version-script.ll b/lld/test/ELF/lto/internalize-version-script.ll new file mode 100644 index 0000000..c25328f --- /dev/null +++ b/lld/test/ELF/lto/internalize-version-script.ll @@ -0,0 +1,22 @@ +; REQUIRES: x86 +; RUN: llvm-as %s -o %t.o +; RUN: echo "{ global: foo; local: *; };" > %t.script +; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps +; RUN: llvm-dis < %t2.lto.bc | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +; Check that foo is not internalized. +; CHECK: define void @foo() + +; Check that bar is correctly internalized. +; CHECK: define internal void @bar() diff --git a/lld/test/ELF/no-inhibit-exec.s b/lld/test/ELF/no-inhibit-exec.s index 2f2a62b..128f44d 100644 --- a/lld/test/ELF/no-inhibit-exec.s +++ b/lld/test/ELF/no-inhibit-exec.s @@ -6,7 +6,7 @@ # CHECK: Disassembly of section .text: # CHECK-NEXT: _start -# CHECK-NEXT: 11000: e8 00 00 00 00 callq 0 +# CHECK-NEXT: 11000: e8 fb ef fe ff callq -69637 # next code will not link without noinhibit-exec flag # because of undefined symbol _bar diff --git a/lld/test/ELF/version-script.s b/lld/test/ELF/version-script.s new file mode 100644 index 0000000..2878004 --- /dev/null +++ b/lld/test/ELF/version-script.s @@ -0,0 +1,126 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o +# RUN: ld.lld -shared %t2.o -soname shared -o %t2.so + +# RUN: echo "{ global: foo1; foo3; local: *; };" > %t.script +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld --version-script %t.script -shared %t.o %t2.so -o %t.so +# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s + +# RUN: echo "{ local: *; };" > %t3.script +# RUN: ld.lld --version-script %t3.script -shared %t.o %t2.so -o %t3.so +# RUN: llvm-readobj -dyn-symbols %t3.so | FileCheck --check-prefix=DSO2 %s + +# --version-script filters --dynamic-list. +# RUN: echo "{ foo1; foo2; };" > %t.list +# RUN: ld.lld --version-script %t.script --dynamic-list %t.list %t.o %t2.so -o %t +# RUN: llvm-readobj -dyn-symbols %t | FileCheck --check-prefix=EXE %s + +# DSO: DynamicSymbols [ +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: @ (0) +# DSO-NEXT: Value: 0x0 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Local (0x0) +# DSO-NEXT: Type: None (0x0) +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: Undefined (0x0) +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: bar@ (1) +# DSO-NEXT: Value: 0x0 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global (0x1) +# DSO-NEXT: Type: Function (0x2) +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: Undefined (0x0) +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: foo1@ (5) +# DSO-NEXT: Value: 0x1000 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global (0x1) +# DSO-NEXT: Type: None (0x0) +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text (0x5) +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: foo3@ (10) +# DSO-NEXT: Value: 0x1007 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global (0x1) +# DSO-NEXT: Type: None (0x0) +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text (0x5) +# DSO-NEXT: } +# DSO-NEXT: ] + +# DSO2: DynamicSymbols [ +# DSO2-NEXT: Symbol { +# DSO2-NEXT: Name: @ (0) +# DSO2-NEXT: Value: 0x0 +# DSO2-NEXT: Size: 0 +# DSO2-NEXT: Binding: Local (0x0) +# DSO2-NEXT: Type: None (0x0) +# DSO2-NEXT: Other: 0 +# DSO2-NEXT: Section: Undefined (0x0) +# DSO2-NEXT: } +# DSO2-NEXT: Symbol { +# DSO2-NEXT: Name: bar@ (1) +# DSO2-NEXT: Value: 0x0 +# DSO2-NEXT: Size: 0 +# DSO2-NEXT: Binding: Global (0x1) +# DSO2-NEXT: Type: Function (0x2) +# DSO2-NEXT: Other: 0 +# DSO2-NEXT: Section: Undefined (0x0) +# DSO2-NEXT: } +# DSO2-NEXT: ] + +# EXE: DynamicSymbols [ +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: @ (0) +# EXE-NEXT: Value: 0x0 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Local (0x0) +# EXE-NEXT: Type: None (0x0) +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined (0x0) +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: bar@ (1) +# EXE-NEXT: Value: 0x0 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Global (0x1) +# EXE-NEXT: Type: Function (0x2) +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined (0x0) +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: foo1@ (5) +# EXE-NEXT: Value: 0x11000 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Global (0x1) +# EXE-NEXT: Type: None (0x0) +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: .text (0x5) +# EXE-NEXT: } +# EXE-NEXT: ] + +.globl foo1 +foo1: + call bar@PLT + ret + +.globl foo2 +foo2: + ret + +.globl foo3 +foo3: + call foo2@PLT + ret + +.globl _start +_start: + ret -- 2.7.4