Index: include/lld/Core/LinkingContext.h =================================================================== --- include/lld/Core/LinkingContext.h +++ include/lld/Core/LinkingContext.h @@ -234,9 +234,7 @@ /// but that can be changed by the LinkingContext. This is also an /// opportunity for flavor specific processing. virtual void notifySymbolTableCoalesce(const Atom *existingAtom, - const Atom *newAtom, - bool &useNew) const { - } + const Atom *newAtom, bool &useNew) {} /// This method adds undefined symbols specified by the -u option to the to /// the list of undefined symbols known to the linker. This option essentially Index: include/lld/Core/SymbolTable.h =================================================================== --- include/lld/Core/SymbolTable.h +++ include/lld/Core/SymbolTable.h @@ -36,7 +36,7 @@ /// if an atom has been coalesced away. class SymbolTable { public: - explicit SymbolTable(const LinkingContext &); + explicit SymbolTable(LinkingContext &); /// @brief add atom to symbol table bool add(const DefinedAtom &); @@ -108,7 +108,7 @@ bool addByName(const Atom &); bool addByContent(const DefinedAtom &); - const LinkingContext &_context; + LinkingContext &_context; AtomToAtom _replacedAtoms; NameToAtom _nameTable; NameToAtom _groupTable; Index: include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- include/lld/ReaderWriter/ELFLinkingContext.h +++ include/lld/ReaderWriter/ELFLinkingContext.h @@ -19,6 +19,7 @@ #include "lld/ReaderWriter/Reader.h" #include "lld/ReaderWriter/Writer.h" +#include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/ELF.h" #include "llvm/Support/ELF.h" @@ -69,6 +70,9 @@ bool mergeCommonStrings() const { return _mergeCommonStrings; } virtual uint64_t getBaseAddress() const { return _baseAddress; } + void notifySymbolTableCoalesce(const Atom *existingAtom, const Atom *newAtom, + bool &useNew) override; + /// This controls if undefined atoms need to be created for undefines that are /// present in a SharedLibrary. If this option is set, undefined atoms are /// created for every undefined symbol that are present in the dynamic table @@ -251,6 +255,10 @@ return true; } + bool hasCoalescedWeakPair(StringRef name) const { + return _weakCoalescedSymbols.count(name) != 0; + } + private: ELFLinkingContext() LLVM_DELETED_FUNCTION; @@ -284,6 +292,7 @@ StringRefVector _rpathList; StringRefVector _rpathLinkList; std::map _absoluteSymbols; + llvm::StringSet<> _weakCoalescedSymbols; }; } // end namespace lld Index: lib/Core/SymbolTable.cpp =================================================================== --- lib/Core/SymbolTable.cpp +++ lib/Core/SymbolTable.cpp @@ -30,7 +30,7 @@ #include namespace lld { -SymbolTable::SymbolTable(const LinkingContext &context) : _context(context) {} +SymbolTable::SymbolTable(LinkingContext &context) : _context(context) {} bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); } Index: lib/ReaderWriter/ELF/ELFLinkingContext.cpp =================================================================== --- lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -241,4 +241,29 @@ return std::move(undefinedSymFile); } +static bool isSharedWeakAtom(const UndefinedAtom *ua) { + return ua->canBeNull() != UndefinedAtom::canBeNullNever && + isa(ua->file()); +} + +void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom, + const Atom *newAtom, + bool &useNew) { + // First suppose that the `existingAtom` is defined + // and the `newAtom` is undefined. + auto *da = dyn_cast(existingAtom); + auto *ua = dyn_cast(newAtom); + if (!da && !ua) { + // Then try to reverse the assumption. + da = dyn_cast(newAtom); + ua = dyn_cast(existingAtom); + } + + if (da && ua && da->scope() == Atom::scopeGlobal && isSharedWeakAtom(ua)) + // If strong defined atom coalesces away weak atom declared + // in the shared object the strong atom needs to be dynamicaly exported. + // Save its name. + _weakCoalescedSymbols.insert(ua->name()); +} + } // end namespace lld Index: lib/ReaderWriter/ELF/OutputELFWriter.h =================================================================== --- lib/ReaderWriter/ELF/OutputELFWriter.h +++ lib/ReaderWriter/ELF/OutputELFWriter.h @@ -182,7 +182,8 @@ if (auto section = dyn_cast>(sec)) for (const auto &atom : section->atoms()) { const DefinedAtom *da = dyn_cast(atom->_atom); - if (da && da->dynamicExport() == DefinedAtom::dynamicExportAlways) + if (da && (da->dynamicExport() == DefinedAtom::dynamicExportAlways || + _context.hasCoalescedWeakPair(da->name()))) _dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), atom->_virtualAddr, atom); } Index: test/elf/X86_64/dynsym-weak.test =================================================================== --- /dev/null +++ test/elf/X86_64/dynsym-weak.test @@ -0,0 +1,118 @@ +# Check that a symbol declared as a week in a shared library gets a dynamic +# symbol table record in an executable file if this executabe file declares the +# symbol as strong. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.foo.o +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.so %t.foo.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.main.o +# +# Link executable file with strong symbol. Weak symbol is in the shared lib. +# RUN: lld -flavor gnu -target x86_64 -e main -o %t1.exe %t.main.o %t.so +# RUN: llvm-readobj -dyn-symbols %t1.exe | FileCheck -check-prefix=EXE %s +# +# Link executable file. Strong and weak symbol come from different object files. +# RUN: lld -flavor gnu -target x86_64 -e main -o %t2.exe %t.main.o %t.foo.o +# RUN: llvm-readobj -dyn-symbols %t2.exe | FileCheck -check-prefix=OBJ %s +# +# Link shared library. Weak symbol is in the another shared lib. +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.res.so %t.main.o %t.so +# RUN: llvm-readobj -dyn-symbols %t.res.so | FileCheck -check-prefix=SO %s + +# EXE: Symbol { +# EXE: Name: flag@ ({{[0-9]+}}) +# EXE-NEXT: Value: 0x{{[0-9A-F]+}} +# EXE-NEXT: Size: 4 +# EXE-NEXT: Binding: Global (0x1) +# EXE-NEXT: Type: Object (0x1) +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: .data (0x{{[0-9A-F]+}}) +# EXE-NEXT: } + +# OBJ-NOT: Name: flag@ ({{[0-9]+}}) + +# SO: Symbol { +# SO: Name: flag@ ({{[0-9]+}}) +# SO-NEXT: Value: 0x{{[0-9A-F]+}} +# SO-NEXT: Size: 4 +# SO-NEXT: Binding: Global (0x1) +# SO-NEXT: Type: Object (0x1) +# SO-NEXT: Other: 0 +# SO-NEXT: Section: .data (0x{{[0-9A-F]+}}) +# SO-NEXT: } + +# foo.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: flag + Type: R_X86_64_GOTPCREL + Addend: -4 + +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x08 + Weak: + - Name: flag + +# main.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: foo + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: flag + Type: STT_OBJECT + Section: .data + Size: 0x04 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: foo +...