Commit 674d6c52 authored by Rui Ueyama's avatar Rui Ueyama
Browse files

Merging r308492:

------------------------------------------------------------------------
r308492 | rafael | 2017-07-19 09:45:05 -0700 (Wed, 19 Jul 2017) | 20 lines

Bring back r307364.

In addition this includes a change to prefer symbols with a default
version @@ over unversioned symbols.

Original commit message:

[ELF] - Handle symbols with default version early.

This fixes last testcase provided in PR28414.
In short issue is next: when we had X@@Version symbol in object A,
we did not resolve it to X early. Then when in another object B
we had reference to undefined X, symbol X from archive was fetched.
Since both archive and object A contains another symbol Z, duplicate
symbol definition was triggered as a result.

Correct behavior is to use X@@Version from object A instead and do not fetch
any symbols from archive.

Differential revision: https://reviews.llvm.org/D35059
------------------------------------------------------------------------

llvm-svn: 308788
parent d9bbfe9d
Loading
Loading
Loading
Loading
+34 −29
Original line number Diff line number Diff line
@@ -211,6 +211,12 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
// Find an existing symbol or create and insert a new one.
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
  // <name>@@<version> means the symbol is the default version. In that
  // case <name>@@<version> will be used to resolve references to <name>.
  size_t Pos = Name.find("@@");
  if (Pos != StringRef::npos)
    Name = Name.take_front(Pos);

  auto P = Symtab.insert(
      {CachedHashStringRef(Name), SymIndex((int)SymVector.size(), false)});
  SymIndex &V = P.first->second;
@@ -312,15 +318,36 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
  return S;
}

// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
// foo@@VER. We want to effectively ignore foo, so give precedence to
// foo@@VER.
// FIXME: If users can transition to using
// .symver foo,foo@@@VER
// we can delete this hack.
static int compareVersion(Symbol *S, StringRef Name) {
  if (Name.find("@@") != StringRef::npos &&
      S->body()->getName().find("@@") == StringRef::npos)
    return 1;
  if (Name.find("@@") == StringRef::npos &&
      S->body()->getName().find("@@") != StringRef::npos)
    return -1;
  return 0;
}

// We have a new defined symbol with the specified binding. Return 1 if the new
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
// strong defined symbols.
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
                          StringRef Name) {
  if (WasInserted)
    return 1;
  SymbolBody *Body = S->body();
  if (!Body->isInCurrentDSO())
    return 1;

  if (int R = compareVersion(S, Name))
    return R;

  if (Binding == STB_WEAK)
    return -1;
  if (S->isWeak())
@@ -333,8 +360,9 @@ static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
// is a conflict. If the new symbol wins, also update the binding.
template <typename ELFT>
static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
                                   bool IsAbsolute, typename ELFT::uint Value) {
  if (int Cmp = compareDefined(S, WasInserted, Binding)) {
                                   bool IsAbsolute, typename ELFT::uint Value,
                                   StringRef Name) {
  if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) {
    if (Cmp > 0)
      S->Binding = Binding;
    return Cmp;
@@ -362,7 +390,7 @@ Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
  bool WasInserted;
  std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
                                    /*CanOmitFromDynSym*/ false, File);
  int Cmp = compareDefined(S, WasInserted, Binding);
  int Cmp = compareDefined(S, WasInserted, Binding, N);
  if (Cmp > 0) {
    S->Binding = Binding;
    replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
@@ -439,7 +467,7 @@ Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
  std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
                                    /*CanOmitFromDynSym*/ false, File);
  int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
                                          Section == nullptr, Value);
                                          Section == nullptr, Value, Name);
  if (Cmp > 0)
    replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type,
                                Value, Size, Section, File);
@@ -485,7 +513,7 @@ Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
  std::tie(S, WasInserted) =
      insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, F);
  int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
                                          /*IsAbs*/ false, /*Value*/ 0);
                                          /*IsAbs*/ false, /*Value*/ 0, Name);
  if (Cmp > 0)
    replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type, 0, 0,
                                nullptr, F);
@@ -717,32 +745,9 @@ void SymbolTable<ELFT>::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 <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
  // Symbol themselves might know their versions because symbols
  // can contain versions in the form of <name>@<version>.
  // 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;

    // <name>@@<version> means the symbol is the default version. If that's the
    // case, the symbol is not used only to resolve <name> of version <version>
    // but also undefined unversioned symbols with name <name>.
    SymbolBody *S = find(Body->getName());
    if (S && S->isUndefined())
      S->copy(Body);
  }

  // Handle edge cases first.
  handleAnonymousVersion();

+6 −0
Original line number Diff line number Diff line
.text
.globl x
.type x, @function
x:

.symver x, xx@@VER
+1 −0
Original line number Diff line number Diff line
call xx@PLT
+15 −0
Original line number Diff line number Diff line
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1
# RUN: rm -f %t.a
# RUN: llvm-ar rcs %t.a %t1
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/symver-archive1.s -o %t2.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/symver-archive2.s -o %t3.o
# RUN: ld.lld -o %t.out %t2.o %t3.o %t.a

.text
.globl x
.type x, @function
x:

.globl xx
xx = x