From aad84e2ee2431e5e4ef4e30d642c3a95146b4e1c Mon Sep 17 00:00:00 2001 From: George Rimar Date: Fri, 30 Jun 2017 10:19:54 +0000 Subject: [PATCH] [ELF] - Resolve references properly when using .symver directive This is PR28414. Previously LLD was unable to link following: (failed with undefined symbol bar) ``` Version script: SOME_VERSION { global: *; }; .global _start .global bar .symver _start, bar@@SOME_VERSION _start: jmp bar ``` Manual has next description: // .symver name, name2@@nodename In this case, the symbol name must exist and be defined within the file being assembled. It is similar to name2@nodename. **The difference is name2@@nodename will also be used to resolve references to name2 by the linker** https://sourceware.org/binutils/docs/as/Symver.html // Patch implements that. If we have name@@ver symbol and name is undefined, name@@ver is used to resolve references to name. Differential revision: https://reviews.llvm.org/D33680 llvm-svn: 306813 --- lld/ELF/SymbolTable.cpp | 24 ++++++++++++++++++++---- lld/test/ELF/version-script-symver.s | 11 +++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 lld/test/ELF/version-script-symver.s diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 0b5450b071f2..d75b89f17527 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -712,15 +712,31 @@ void SymbolTable::assignWildcardVersion(SymbolVersion Ver, B->symbol()->VersionId = VersionId; } +static bool isDefaultVersion(SymbolBody *B) { + return B->isInCurrentDSO() && B->getName().find("@@") != StringRef::npos; +} + // This function processes version scripts by updating VersionId // member of symbols. template void SymbolTable::scanVersionScript() { // Symbol themselves might know their versions because symbols // can contain versions in the form of @. - // Let them parse their names. - if (!Config->VersionDefinitions.empty()) - for (Symbol *Sym : SymVector) - Sym->body()->parseSymbolVersion(); + // Let them parse and update their names to exclude version suffix. + for (Symbol *Sym : SymVector) { + SymbolBody *Body = Sym->body(); + bool IsDefault = isDefaultVersion(Body); + Body->parseSymbolVersion(); + + if (!IsDefault) + continue; + + // @@ means the symbol is the default version. If that's the + // case, the symbol is not used only to resolve of version + // but also undefined unversioned symbols with name . + SymbolBody *S = find(Body->getName()); + if (S && S->isUndefined()) + S->copy(Body); + } // Handle edge cases first. handleAnonymousVersion(); diff --git a/lld/test/ELF/version-script-symver.s b/lld/test/ELF/version-script-symver.s new file mode 100644 index 000000000000..7798330b053d --- /dev/null +++ b/lld/test/ELF/version-script-symver.s @@ -0,0 +1,11 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "VERSION { global: *; };" > %t.map +# RUN: ld.lld %t.o --version-script %t.map -o %t + +.global _start +.global bar +.symver _start, bar@@VERSION +_start: + jmp bar