From 4d2f976208464da32cc3a72123dde048376aed01 Mon Sep 17 00:00:00 2001 From: George Rimar Date: Tue, 4 Jul 2017 13:19:13 +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. If name is defined then multiple definition error is emited, that is consistent with what bfd do. Differential revision: https://reviews.llvm.org/D33680 llvm-svn: 307077 --- lld/ELF/SymbolTable.cpp | 24 ++++++++++++++++++++---- lld/ELF/Symbols.cpp | 7 ++++++- lld/test/ELF/version-script-symver.s | 9 +++++++++ 3 files changed, 35 insertions(+), 5 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/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index e8cd662c69ac..148acfb8d1fe 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -272,7 +272,12 @@ void SymbolBody::parseSymbolVersion() { } // It is an error if the specified version is not defined. - error(toString(File) + ": symbol " + S + " has undefined version " + Verstr); + // Usually version script is not provided when linking executable, + // but we may still want to override a versioned symbol from DSO, + // so we do not report error in this case. + if (Config->Shared) + error(toString(File) + ": symbol " + S + " has undefined version " + + Verstr); } Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, diff --git a/lld/test/ELF/version-script-symver.s b/lld/test/ELF/version-script-symver.s new file mode 100644 index 000000000000..0a4eddd46cec --- /dev/null +++ b/lld/test/ELF/version-script-symver.s @@ -0,0 +1,9 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t + +.global _start +.global bar +.symver _start, bar@@VERSION +_start: + jmp bar