return std::min(VA, VB);
}
-// Find an existing symbol or create and insert a new one, then apply the given
-// attributes.
-std::pair<Symbol *, bool> SymbolTable::insert(const Symbol &New) {
- // Find an existing symbol or create and insert a new one.
-
+// Find an existing symbol or create and insert a new one.
+Symbol *SymbolTable::insert(const Symbol &New) {
// <name>@@<version> means the symbol is the default version. In that
// case <name>@@<version> will be used to resolve references to <name>.
//
Old = SymVector[SymIndex];
}
+ return Old;
+}
+
+// Merge symbol properties.
+//
+// When we have many symbols of the same name, we choose one of them,
+// and that's the result of symbol resolution. However, symbols that
+// were not chosen still affect some symbol properteis.
+void SymbolTable::mergeProperties(Symbol *Old, const Symbol &New) {
// Merge symbol properties.
Old->ExportDynamic = Old->ExportDynamic || New.ExportDynamic;
Old->IsUsedInRegularObj = Old->IsUsedInRegularObj || New.IsUsedInRegularObj;
// DSO symbols do not affect visibility in the output.
- if (!isa<SharedSymbol>(&New))
+ if (!isa<SharedSymbol>(New))
Old->Visibility = getMinVisibility(Old->Visibility, New.Visibility);
-
- return {Old, IsNew};
}
template <class ELFT> Symbol *SymbolTable::addUndefined(const Undefined &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
+
+ if (Old->isPlaceholder()) {
+ replaceSymbol(Old, &New);
+ return Old;
+ }
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
- if (WasInserted ||
- (isa<SharedSymbol>(Old) && New.Visibility != STV_DEFAULT)) {
+ if (Old->isShared() && New.Visibility != STV_DEFAULT) {
replaceSymbol(Old, &New);
return Old;
}
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(const Symbol *Old, const Symbol *New) {
- if (!Old->isDefined())
+// Compare two symbols. Return 1 if the new symbol should win, -1 if
+// the new symbol should lose, or 0 if there is a conflict.
+static int compare(const Symbol *Old, const Symbol *New) {
+ assert(New->isDefined() || New->isCommon());
+
+ if (!Old->isDefined() && !Old->isCommon())
return 1;
+
if (int Cmp = compareVersion(Old->getName(), New->getName()))
return Cmp;
- if (New->Binding == STB_WEAK)
+
+ if (New->isWeak())
return -1;
+
if (Old->isWeak())
return 1;
- return 0;
-}
-// We have a new non-common defined symbol with the specified binding. Return 1
-// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
-// is a conflict. If the new symbol wins, also update the binding.
-static int compareDefinedNonCommon(const Symbol *OldSym, const Defined *New) {
- if (int Cmp = compareDefined(OldSym, New))
- return Cmp;
-
- if (auto *Old = dyn_cast<Defined>(OldSym)) {
- if (Old->Section && isa<BssSection>(Old->Section)) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + Old->getName() + " is overridden");
- return 1;
- }
+ if (Old->isCommon() && New->isCommon()) {
+ if (Config->WarnCommon)
+ warn("multiple common of " + Old->getName());
+ return 0;
+ }
- if (New->File && isa<BitcodeFile>(New->File))
- return 0;
+ if (Old->isCommon()) {
+ if (Config->WarnCommon)
+ warn("common " + Old->getName() + " is overridden");
+ return 1;
+ }
- if (Old->Section == nullptr && New->Section == nullptr &&
- Old->Value == New->Value && New->Binding == STB_GLOBAL)
- return -1;
+ if (New->isCommon()) {
+ if (Config->WarnCommon)
+ warn("common " + Old->getName() + " is overridden");
+ return -1;
}
+
+ auto *OldSym = cast<Defined>(Old);
+ auto *NewSym = cast<Defined>(New);
+
+ if (New->File && isa<BitcodeFile>(New->File))
+ return 0;
+
+ if (!OldSym->Section && !NewSym->Section && OldSym->Value == NewSym->Value &&
+ NewSym->Binding == STB_GLOBAL)
+ return -1;
+
return 0;
}
-Symbol *SymbolTable::addCommon(const Defined &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
-
- auto Replace = [&] {
- auto *Bss = make<BssSection>("COMMON", New.Size, New.Value);
- Bss->File = New.File;
- Bss->Live = !Config->GcSections;
- InputSections.push_back(Bss);
-
- Defined Sym = New;
- Sym.Value = 0;
- Sym.Section = Bss;
- replaceSymbol(Old, &Sym);
- };
-
- if (WasInserted) {
- Replace();
+Symbol *SymbolTable::addCommon(const CommonSymbol &New) {
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
+
+ if (Old->isPlaceholder()) {
+ replaceSymbol(Old, &New);
return Old;
}
- int Cmp = compareDefined(Old, &New);
+ int Cmp = compare(Old, &New);
if (Cmp < 0)
return Old;
if (Cmp > 0) {
- Replace();
- return Old;
- }
-
- auto *D = cast<Defined>(Old);
- auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
- if (!Bss) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + Old->getName() + " is overridden");
+ replaceSymbol(Old, &New);
return Old;
}
- if (Config->WarnCommon)
- warn("multiple common of " + D->getName());
+ CommonSymbol *OldSym = cast<CommonSymbol>(Old);
- Bss->Alignment = std::max<uint32_t>(Bss->Alignment, New.Value);
- if (New.Size > Bss->Size) {
- D->File = Bss->File = New.File;
- D->Size = Bss->Size = New.Size;
+ OldSym->Alignment = std::max(OldSym->Alignment, New.Alignment);
+ if (OldSym->Size < New.Size) {
+ OldSym->File = New.File;
+ OldSym->Size = New.Size;
}
- return Old;
+ return OldSym;
}
static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
error(Msg);
}
-Defined *SymbolTable::addDefined(const Defined &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
+Symbol *SymbolTable::addDefined(const Defined &New) {
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
- if (WasInserted) {
+ if (Old->isPlaceholder()) {
replaceSymbol(Old, &New);
- return cast<Defined>(Old);
+ return Old;
}
- int Cmp = compareDefinedNonCommon(Old, &New);
+ int Cmp = compare(Old, &New);
if (Cmp > 0)
replaceSymbol(Old, &New);
else if (Cmp == 0)
reportDuplicate(Old, New.File,
dyn_cast_or_null<InputSectionBase>(New.Section), New.Value);
- return cast<Defined>(Old);
+ return Old;
}
void SymbolTable::addShared(const SharedSymbol &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
// Make sure we preempt DSO symbols with default visibility.
if (New.Visibility == STV_DEFAULT)
Old->ExportDynamic = true;
- if (WasInserted) {
+ if (Old->isPlaceholder()) {
replaceSymbol(Old, &New);
- } else if (Old->Visibility == STV_DEFAULT &&
- (Old->isUndefined() || Old->isLazy())) {
+ return;
+ }
+
+ if (Old->Visibility == STV_DEFAULT && (Old->isUndefined() || Old->isLazy())) {
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
uint8_t Binding = Old->Binding;
}
Symbol *SymbolTable::addBitcode(const Defined &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
- if (WasInserted) {
+ if (Old->isPlaceholder()) {
replaceSymbol(Old, &New);
return Old;
}
- int Cmp = compareDefinedNonCommon(Old, &New);
+ int Cmp = compare(Old, &New);
if (Cmp > 0)
replaceSymbol(Old, &New);
else if (Cmp == 0)
}
template <class ELFT, class LazyT> void SymbolTable::addLazy(const LazyT &New) {
- Symbol *Old;
- bool WasInserted;
- std::tie(Old, WasInserted) = insert(New);
+ Symbol *Old = insert(New);
+ mergeProperties(Old, New);
- if (WasInserted) {
+ if (Old->isPlaceholder()) {
replaceSymbol(Old, &New);
return;
}
if (!DemangledSyms) {
DemangledSyms.emplace();
for (Symbol *Sym : SymVector) {
- if (!Sym->isDefined())
+ if (!Sym->isDefined() && !Sym->isCommon())
continue;
if (Optional<std::string> S = demangleItanium(Sym->getName()))
(*DemangledSyms)[*S].push_back(Sym);
if (Ver.IsExternCpp)
return getDemangledSyms().lookup(Ver.Name);
if (Symbol *B = find(Ver.Name))
- if (B->isDefined())
+ if (B->isDefined() || B->isCommon())
return {B};
return {};
}
}
for (Symbol *Sym : SymVector)
- if (Sym->isDefined() && M.match(Sym->getName()))
+ if ((Sym->isDefined() || Sym->isCommon()) && M.match(Sym->getName()))
Res.push_back(Sym);
return Res;
}
enum Kind {
PlaceholderKind,
DefinedKind,
+ CommonKind,
SharedKind,
UndefinedKind,
LazyArchiveKind,
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
bool isUndefined() const { return SymbolKind == UndefinedKind; }
+ bool isCommon() const { return SymbolKind == CommonKind; }
bool isDefined() const { return SymbolKind == DefinedKind; }
bool isShared() const { return SymbolKind == SharedKind; }
+ bool isPlaceholder() const { return SymbolKind == PlaceholderKind; }
+
bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
bool isLazy() const {
SectionBase *Section;
};
+// Represents a common symbol.
+//
+// On Unix, it is traditionally allowed to write variable definitions
+// without initialization expressions (such as "int foo;") to header
+// files. Such definition is called "tentative definition".
+//
+// Using tentative definition is usually considered a bad practice
+// because you should write only declarations (such as "extern int
+// foo;") to header files. Nevertheless, the linker and the compiler
+// have to do something to support bad code by allowing duplicate
+// definitions for this particular case.
+//
+// Common symbols represent variable definitions without initializations.
+// The compiler creates common symbols when it sees varaible definitions
+// without initialization (you can suppress this behavior and let the
+// compiler create a regular defined symbol by -fno-common).
+//
+// The linker allows common symbols to be replaced by regular defined
+// symbols. If there are remaining common symbols after name resolution is
+// complete, they are converted to regular defined symbols in a .bss
+// section. (Therefore, the later passes don't see any CommonSymbols.)
+class CommonSymbol : public Symbol {
+public:
+ CommonSymbol(InputFile *File, StringRefZ Name, uint8_t Binding,
+ uint8_t StOther, uint8_t Type, uint64_t Alignment, uint64_t Size)
+ : Symbol(CommonKind, File, Name, Binding, StOther, Type),
+ Alignment(Alignment), Size(Size) {}
+
+ static bool classof(const Symbol *S) { return S->isCommon(); }
+
+ uint32_t Alignment;
+ uint64_t Size;
+};
+
class Undefined : public Symbol {
public:
Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
if (!S || S->isDefined())
return nullptr;
- return Symtab->addDefined(Defined{/*File=*/nullptr, Name, Binding, StOther,
- STT_NOTYPE, Val,
- /*Size=*/0, Sec});
+ return cast<Defined>(Symtab->addDefined(
+ Defined{/*File=*/nullptr, Name, Binding, StOther, STT_NOTYPE, Val,
+ /*Size=*/0, Sec}));
}
static Defined *addAbsolute(StringRef Name) {
- return Symtab->addDefined(Defined{nullptr, Name, STB_GLOBAL, STV_HIDDEN,
- STT_NOTYPE, 0, 0, nullptr});
+ Defined New(nullptr, Name, STB_GLOBAL, STV_HIDDEN, STT_NOTYPE, 0, 0, nullptr);
+ Symbol *Sym = Symtab->addDefined(New);
+ if (!Sym->isDefined())
+ error("duplicate symbol: " + toString(*Sym));
+ return cast<Defined>(Sym);
}
// The linker is expected to define some symbols depending on
if (Config->EMachine == EM_PPC || Config->EMachine == EM_PPC64)
GotOff = 0x8000;
- ElfSym::GlobalOffsetTable =
- Symtab->addDefined(Defined{/*File=*/nullptr, GotSymName, STB_GLOBAL,
- STV_HIDDEN, STT_NOTYPE, GotOff,
- /*Size=*/0, Out::ElfHeader});
+ Symtab->addDefined(Defined{/*File=*/nullptr, GotSymName, STB_GLOBAL,
+ STV_HIDDEN, STT_NOTYPE, GotOff, /*Size=*/0,
+ Out::ElfHeader});
+ ElfSym::GlobalOffsetTable = cast<Defined>(S);
}
// __ehdr_start is the location of ELF file headers. Note that we define