Merge visibility from all symbols with the same name.

The ELF spec says:

... if any reference to or definition of a name is a symbol with a
non-default visibility attribute, the visibility attribute must be
propagated to the resolving symbol in the linked object. If different
visibility attributes are specified for distinct references to or
definitions of a symbol, the most constraining visibility attribute
must be propagated to the resolving symbol in the linked object. The
attributes, ordered from least to most constraining, are:
STV_PROTECTED, STV_HIDDEN and STV_INTERNAL.

llvm-svn: 246603
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index f2b484d..37e0033 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -13,10 +13,19 @@
 #include "InputFiles.h"
 
 using namespace llvm::object;
+using namespace llvm::ELF;
 
 using namespace lld;
 using namespace lld::elf2;
 
+static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
+  if (VA == STV_DEFAULT)
+    return VB;
+  if (VB == STV_DEFAULT)
+    return VA;
+  return std::min(VA, VB);
+}
+
 // Returns 1, 0 or -1 if this symbol should take precedence
 // over the Other, tie or lose, respectively.
 template <class ELFT> int SymbolBody::compare(SymbolBody *Other) {
@@ -27,6 +36,11 @@
   if (L > R)
     return -Other->compare<ELFT>(this);
 
+  uint8_t LV = getMostConstrainingVisibility();
+  uint8_t RV = Other->getMostConstrainingVisibility();
+  MostConstrainingVisibility = getMinVisibility(LV, RV);
+  Other->MostConstrainingVisibility = MostConstrainingVisibility;
+
   if (L != R)
     return -1;
 
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 7f7578d..5051890 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -56,6 +56,10 @@
   // Returns the symbol name.
   StringRef getName() const { return Name; }
 
+  uint8_t getMostConstrainingVisibility() const {
+    return MostConstrainingVisibility;
+  }
+
   // A SymbolBody has a backreference to a Symbol. Originally they are
   // doubly-linked. A backreference will never change. But the pointer
   // in the Symbol may be mutated by the resolver. If you have a
@@ -71,12 +75,14 @@
   template <class ELFT> int compare(SymbolBody *Other);
 
 protected:
-  SymbolBody(Kind K, StringRef Name, bool IsWeak)
-      : SymbolKind(K), IsWeak(IsWeak), Name(Name) {}
+  SymbolBody(Kind K, StringRef Name, bool IsWeak, uint8_t Visibility)
+      : SymbolKind(K), IsWeak(IsWeak), MostConstrainingVisibility(Visibility),
+        Name(Name) {}
 
 protected:
   const unsigned SymbolKind : 8;
   const unsigned IsWeak : 1;
+  unsigned MostConstrainingVisibility : 2;
   StringRef Name;
   Symbol *Backref = nullptr;
 };
@@ -92,8 +98,9 @@
 protected:
   typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
   ELFSymbolBody(Kind K, StringRef Name, const Elf_Sym &Sym)
-      : SymbolBody(K, Name, Sym.getBinding() == llvm::ELF::STB_WEAK), Sym(Sym) {
-  }
+      : SymbolBody(K, Name, Sym.getBinding() == llvm::ELF::STB_WEAK,
+                   Sym.getVisibility()),
+        Sym(Sym) {}
 
 public:
   const Elf_Sym &Sym;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 9108384..3145efc 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -304,7 +304,7 @@
     SymbolBody *Body = Sym->Body;
     const Elf_Sym &InputSym = cast<ELFSymbolBody<ELFT>>(Body)->Sym;
 
-    uint8_t V = InputSym.getVisibility();
+    uint8_t V = Body->getMostConstrainingVisibility();
     if (V != STV_DEFAULT && V != STV_PROTECTED)
       continue;
 
@@ -331,7 +331,8 @@
     uint8_t Binding = InputSym.getBinding();
     ESym->setBindingAndType(Binding, Type);
     ESym->st_size = InputSym.st_size;
-    ESym->st_other = InputSym.st_other;
+    uint8_t Other = InputSym.st_other;
+    ESym->st_other = (Other & ~0x3) | Body->getMostConstrainingVisibility();
     if (InputSym.isAbsolute()) {
       ESym->st_shndx = SHN_ABS;
       ESym->st_value = InputSym.st_value;
@@ -454,8 +455,7 @@
     SymbolBody *Body = P.second->Body;
     if (auto *C = dyn_cast<DefinedCommon<ELFT>>(Body))
       CommonSymbols.push_back(C);
-    auto *E = cast<ELFSymbolBody<ELFT>>(Body);
-    uint8_t V = E->Sym.getVisibility();
+    uint8_t V = Body->getMostConstrainingVisibility();
     if (V != STV_DEFAULT && V != STV_PROTECTED)
       continue;
     NumVisible++;
diff --git a/lld/test/elf2/Inputs/visibility.s b/lld/test/elf2/Inputs/visibility.s
new file mode 100644
index 0000000..6ef027d
--- /dev/null
+++ b/lld/test/elf2/Inputs/visibility.s
@@ -0,0 +1,13 @@
+.quad default
+
+.protected protected
+.quad protected
+
+.hidden hidden
+.quad hidden
+
+.internal internal
+.quad internal
+
+.hidden protected_with_hidden
+.quad protected_with_hidden
diff --git a/lld/test/elf2/visibility.s b/lld/test/elf2/visibility.s
new file mode 100644
index 0000000..38853eb
--- /dev/null
+++ b/lld/test/elf2/visibility.s
@@ -0,0 +1,63 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/visibility.s -o %t2
+// RUN: lld -flavor gnu2 %t %t2 -o %t3
+// RUN: llvm-readobj -t %t3 | FileCheck %s
+// REQUIRES: x86
+
+// CHECK:      Symbols [
+// CHECK-NEXT:   Symbol {
+// CHECK-NEXT:     Name:
+// CHECK-NEXT:     Value: 0x0
+// CHECK-NEXT:     Size: 0
+// CHECK-NEXT:     Binding: Local
+// CHECK-NEXT:     Type: None
+// CHECK-NEXT:     Other: 0
+// CHECK-NEXT:     Section: Undefined
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Symbol {
+// CHECK-NEXT:     Name: _start
+// CHECK-NEXT:     Value: 0x1000
+// CHECK-NEXT:     Size: 0
+// CHECK-NEXT:     Binding: Global
+// CHECK-NEXT:     Type: None
+// CHECK-NEXT:     Other: 0
+// CHECK-NEXT:     Section: .text
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Symbol {
+// CHECK-NEXT:     Name: default
+// CHECK-NEXT:     Value: 0x1000
+// CHECK-NEXT:     Size: 0
+// CHECK-NEXT:     Binding: Global
+// CHECK-NEXT:     Type: None
+// CHECK-NEXT:     Other: 0
+// CHECK-NEXT:     Section: .text
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Symbol {
+// CHECK-NEXT:     Name: protected
+// CHECK-NEXT:     Value: 0x1000
+// CHECK-NEXT:     Size: 0
+// CHECK-NEXT:     Binding: Global
+// CHECK-NEXT:     Type: None
+// CHECK-NEXT:     Other: 3
+// CHECK-NEXT:     Section: .text
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+.global _start
+_start:
+
+.global default
+default:
+
+.global protected
+protected:
+
+.global hidden
+hidden:
+
+.global internal
+internal:
+
+.global protected_with_hidden
+.protected
+protected_with_hidden: