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: