diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -161,7 +161,6 @@ bool gdbIndex; bool gnuHash = false; bool gnuUnique; - bool hasDynamicList = false; bool hasDynSymTab; bool ignoreDataAddressEquality; bool ignoreFunctionAddressEquality; @@ -192,6 +191,7 @@ llvm::Optional shuffleSectionSeed; bool singleRoRx; bool shared; + bool symbolic; bool isStatic = false; bool sysvHash = false; bool target1Rel; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1172,25 +1172,21 @@ error(arg->getSpelling() + ": " + toString(pat.takeError())); } - // Parses -dynamic-list and -export-dynamic-symbol. They make some - // symbols private. Note that -export-dynamic takes precedence over them - // as it says all symbols should be exported. - if (!config->exportDynamic) { - for (auto *arg : args.filtered(OPT_dynamic_list)) - if (Optional buffer = readFile(arg->getValue())) - readDynamicList(*buffer); + // When producing an executable, --dynamic-list specifies non-local defined + // symbols whith are required to be exported. When producing a shared object, + // symbols not specified by --dynamic-list are non-preemptible. + config->symbolic = + args.hasArg(OPT_Bsymbolic) || args.hasArg(OPT_dynamic_list); + for (auto *arg : args.filtered(OPT_dynamic_list)) + if (Optional buffer = readFile(arg->getValue())) + readDynamicList(*buffer); - for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) - config->dynamicList.push_back( - {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false}); - } - - // If --export-dynamic-symbol=foo is given and symbol foo is defined in - // an object file in an archive file, that object file should be pulled - // out and linked. (It doesn't have to behave like that from technical - // point of view, but this is needed for compatibility with GNU.) + // --export-dynamic-symbol specifies additional --dynamic-list symbols if any + // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic, + // -Bsymbolic-functions (if STT_FUNC), --dynamic-list. for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) - config->undefined.push_back(arg->getValue()); + config->dynamicList.push_back( + {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/true}); for (auto *arg : args.filtered(OPT_version_script)) if (Optional path = searchScript(arg->getValue())) { diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -145,7 +145,12 @@ defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">; -defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">; +defm dynamic_list : Eq<"dynamic-list", + "Read a list of dynamic symbols. (executable) Put matched non-local defined" + "symbols to the dynamic symbol table. (shared object) References to matched" + "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the " + "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">, + MetaVarName<"">; defm eh_frame_hdr: B<"eh-frame-hdr", "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", @@ -181,8 +186,12 @@ "Put symbols in the dynamic symbol table", "Do not put symbols in the dynamic symbol table (default)">; -defm export_dynamic_symbol: - Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">; +defm export_dynamic_symbol : EEq<"export-dynamic-symbol", + "(executable) Put matched symbols in the dynamic symbol table. " + "(shared object) References to matched non-local STV_DEFAULT symbols " + "shouldn't be bound to definitions within the shared object. " + "Does not imply -Bsymbolic.">, + MetaVarName<"glob">; defm fatal_warnings: B<"fatal-warnings", "Treat warnings as errors", diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -175,7 +175,6 @@ } void ScriptParser::readDynamicList() { - config->hasDynamicList = true; expect("{"); std::vector locals; std::vector globals; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -365,13 +365,11 @@ if (!config->shared) return false; - // If the dynamic list is present, it specifies preemptable symbols in a DSO. - if (config->hasDynamicList) + // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is + // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is + // in the dynamic list. + if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc())) return sym.inDynamicList; - - // -Bsymbolic means that definitions are not preempted. - if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc())) - return false; return true; } diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -35,6 +35,7 @@ * One-dash form of some long option (``--thinlto-*``, ``--lto-*``, ``--shuffle-sections=``) are no longer supported. +* ``--export-dynamic-symbol`` no longer implies ``-u``. COFF Improvements ----------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -157,6 +157,10 @@ .It Fl -dynamic-list Ns = Ns Ar file Read a list of dynamic symbols from .Ar file . +(executable) Put matched non-local defined symbols to the dynamic symbol table. +(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies +.Cm -Bsymbolic +but does not set DF_SYMBOLIC .It Fl -eh-frame-hdr Request creation of .Li .eh_frame_hdr @@ -184,10 +188,14 @@ Exclude static libraries from automatic export. .It Fl -export-dynamic , Fl E Put symbols in the dynamic symbol table. -.It Fl -export-dynamic-symbol Ns = Ns Ar symbol -Include -.Ar symbol -in the dynamic symbol table. +.It Fl -export-dynamic-symbol Ns = Ns Ar glob +(executable) Put matched non-local defined symbols to the dynamic symbol table. +(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object even if they would otherwise be due to +.Cm -Bsymbolic +, +.Cm -Bsymbolic-functions +or +.Cm --dynamic-list .It Fl -fatal-warnings Treat warnings as errors. .It Fl -filter Ns = Ns Ar value , Fl F Ar value diff --git a/lld/test/ELF/export-dynamic-symbol.s b/lld/test/ELF/export-dynamic-symbol.s --- a/lld/test/ELF/export-dynamic-symbol.s +++ b/lld/test/ELF/export-dynamic-symbol.s @@ -1,18 +1,54 @@ # REQUIRES: x86 -# RUN: rm -f %t.a -# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/archive2.s -o %t1.o -# RUN: llvm-ar rcs %t.a %t1.o -# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o -# RUN: ld.lld -shared -o %t.so --export-dynamic-symbol foo %t.a %t2.o -# RUN: llvm-readelf -dyn-symbols %t.so | FileCheck %s +## For an executable, --export-dynamic-symbol exports a symbol if it is non-local and defined. +# RUN: ld.lld -pie --export-dynamic-symbol foo %t.o -o %t +# RUN: llvm-nm -D -p %t | FileCheck %s -# RUN: ld.lld -shared -o %t.so --export-dynamic --export-dynamic-symbol foo %t.a %t2.o -# RUN: llvm-readelf -dyn-symbols %t.so | FileCheck %s +## --export-dynamic exports all non-local defined symbols. +## --export-dynamic-symbol is shadowed. +# RUN: ld.lld -pie --export-dynamic --export-dynamic-symbol foo %t.o -o %t.start +# RUN: llvm-nm -D -p %t.start | FileCheck --check-prefixes=CHECK,START %s -# CHECK: foo +# CHECK-NOT: . +# START: T _start +# CHECK: T foo +# CHECK-NOT: . -.global _start +## --export-dynamic-symbol does not imply -u: %t1.a(%t1.o) is not fetched. +## This is compatible with GNU ld since binutils 2.35 onwards. +# RUN: echo '.globl foo, bar; foo: bar:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o +# RUN: rm -f %t1.a && llvm-ar rc %t1.a %t1.o +# RUN: ld.lld --export-dynamic-symbol bar %t1.a %t.o -o %t.nofetch +# RUN: llvm-nm %t.nofetch | FileCheck /dev/null --implicit-check-not=bar + +## --export-dynamic-symbol can make a symbol preemptible even if it would be otherwise +## non-preemptible (due to -Bsymbolic, -Bsymbolic-functions or --dynamic-list). +# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol nomatch %t.o -o %t.nopreempt +# RUN: llvm-objdump -d %t.nopreempt | FileCheck --check-prefix=NOPLT %s +# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol foo %t.o -o %t.preempt +# RUN: llvm-objdump -d %t.preempt | FileCheck --check-prefix=PLT %s + +## 'nomatch' does not match any symbol. Don't warn. +# RUN: ld.lld --fatal-warnings -shared -Bsymbolic-functions --export-dynamic-symbol nomatch %t.o -o %t.nopreempt2 +# RUN: llvm-objdump -d %t.nopreempt2 | FileCheck --check-prefix=NOPLT %s +# RUN: ld.lld -shared -Bsymbolic-functions --export-dynamic-symbol foo %t.o -o %t.preempt2 +# RUN: llvm-objdump -d %t.preempt2 | FileCheck --check-prefix=PLT %s + +# RUN: echo '{};' > %t.list +# RUN: ld.lld -shared --dynamic-list %t.list --export-dynamic-symbol foo %t.o -o %t.preempt3 +# RUN: llvm-objdump -d %t.preempt3 | FileCheck --check-prefix=PLT %s + +## The option value is a glob. +# RUN: ld.lld -shared -Bsymbolic --export-dynamic-symbol 'f*' %t.o -o %t.preempt4 +# RUN: llvm-objdump -d %t.preempt4 | FileCheck --check-prefix=PLT %s + +# PLT: +# NOPLT-NOT: + +.global _start, foo +.type foo, @function _start: - nop + call foo +foo: diff --git a/lld/test/ELF/warn-backrefs.s b/lld/test/ELF/warn-backrefs.s --- a/lld/test/ELF/warn-backrefs.s +++ b/lld/test/ELF/warn-backrefs.s @@ -75,8 +75,8 @@ ## In GNU linkers, -u does not make a backward reference. # RUN: ld.lld --fatal-warnings --warn-backrefs -u foo %t2.a %t1.o -o /dev/null -## In GNU gold, --export-dynamic-symbol does not make a backward reference. -# RUN: ld.lld --fatal-warnings --warn-backrefs --export-dynamic-symbol foo %t2.a %t1.o -o /dev/null +## -u does not make a backward reference. +# RUN: ld.lld --fatal-warnings --warn-backrefs -u foo %t2.a %t1.o -o /dev/null # RUN: not ld.lld --warn-backrefs-exclude='[' 2>&1 | FileCheck --check-prefix=INVALID %s # INVALID: error: --warn-backrefs-exclude: invalid glob pattern: [