gold: Add -Bsymbolic-non-weak-functions

Message ID 20210520060503.2163022-1-maskray@google.com
State New
Headers show
Series
  • gold: Add -Bsymbolic-non-weak-functions
Related show

Commit Message

Libor Bukata via Binutils May 20, 2021, 6:05 a.m.
This option is a subset of -Bsymbolic-functions: only STB_GLOBAL are
considered. Vague linkage functions are STB_WEAK. A vague linkage
function may have different addresses in a -Bsymbolic-functions linked
shared object and outside the shared object.
-Bsymbolic-non-weak-functions can keep pointer equality while providing
most benefits: (a) fewer JUMP_SLOT (symbol lookups) (b) avoid PLT
entries for default visibility defined functions.

[Hope a maintainer can implement this for GNU ld.
The code is too difficult for me to implement:)]

gold/
    PR 27871
    * options.h (General_options): Add -Bsymbolic-non-weak-functions.
    * options.cc (General_options): Define
    parse_Bsymbols_non_weak_functions.
    * symtab.h (Symbol::is_preemptible): Check -Bsymbolic-non-weak-functions.
    * testsuite/Makefile.am (check_SCRIPTS): Add bsymbolic_non_weak_functions.sh.
    (check_DATA): Add bsymbolic_non_weak_functions.stdout.
    (MOSTLYCLEANFILES): Add bsymbolic_non_weak_functions.
    * testsuite/Makefile.in: Regenerate.
    * testsuite/bsymbolic_non_weak_functions.sh: New file.
    * testsuite/bsymbolic_non_weak_functions.c: New file.
---
 gold/options.cc                               |  7 +++
 gold/options.h                                | 10 ++++
 gold/symtab.h                                 |  4 +-
 gold/testsuite/Makefile.am                    | 10 ++++
 gold/testsuite/Makefile.in                    | 22 +++++++--
 gold/testsuite/bsymbolic_non_weak_functions.c | 46 +++++++++++++++++++
 .../testsuite/bsymbolic_non_weak_functions.sh | 46 +++++++++++++++++++
 7 files changed, 141 insertions(+), 4 deletions(-)
 create mode 100644 gold/testsuite/bsymbolic_non_weak_functions.c
 create mode 100755 gold/testsuite/bsymbolic_non_weak_functions.sh

-- 
2.31.1.751.gd2f1c929bd-goog

Comments

Fangrui Song May 26, 2021, 11:22 p.m. | #1
On Wed, May 19, 2021 at 11:05 PM Fangrui Song via Binutils
<binutils@sourceware.org> wrote:
>

> This option is a subset of -Bsymbolic-functions: only STB_GLOBAL are

> considered. Vague linkage functions are STB_WEAK. A vague linkage

> function may have different addresses in a -Bsymbolic-functions linked

> shared object and outside the shared object.

> -Bsymbolic-non-weak-functions can keep pointer equality while providing

> most benefits: (a) fewer JUMP_SLOT (symbol lookups) (b) avoid PLT

> entries for default visibility defined functions.

>

> [Hope a maintainer can implement this for GNU ld.

> The code is too difficult for me to implement:)]

>

> gold/

>     PR 27871

>     * options.h (General_options): Add -Bsymbolic-non-weak-functions.

>     * options.cc (General_options): Define

>     parse_Bsymbols_non_weak_functions.

>     * symtab.h (Symbol::is_preemptible): Check -Bsymbolic-non-weak-functions.

>     * testsuite/Makefile.am (check_SCRIPTS): Add bsymbolic_non_weak_functions.sh.

>     (check_DATA): Add bsymbolic_non_weak_functions.stdout.

>     (MOSTLYCLEANFILES): Add bsymbolic_non_weak_functions.

>     * testsuite/Makefile.in: Regenerate.

>     * testsuite/bsymbolic_non_weak_functions.sh: New file.

>     * testsuite/bsymbolic_non_weak_functions.c: New file.

> ---

>  gold/options.cc                               |  7 +++

>  gold/options.h                                | 10 ++++

>  gold/symtab.h                                 |  4 +-

>  gold/testsuite/Makefile.am                    | 10 ++++

>  gold/testsuite/Makefile.in                    | 22 +++++++--

>  gold/testsuite/bsymbolic_non_weak_functions.c | 46 +++++++++++++++++++

>  .../testsuite/bsymbolic_non_weak_functions.sh | 46 +++++++++++++++++++

>  7 files changed, 141 insertions(+), 4 deletions(-)

>  create mode 100644 gold/testsuite/bsymbolic_non_weak_functions.c

>  create mode 100755 gold/testsuite/bsymbolic_non_weak_functions.sh

>

> diff --git a/gold/options.cc b/gold/options.cc

> index 5a55bd8ba6d..2335dbccc03 100644

> --- a/gold/options.cc

> +++ b/gold/options.cc

> @@ -347,6 +347,13 @@ General_options::parse_Bno_symbolic(const char*, const char*,

>    this->bsymbolic_ = BSYMBOLIC_NONE;

>  }

>

> +void

> +General_options::parse_Bsymbolic_non_weak_functions(const char*, const char*,

> +                                          Command_line*)

> +{

> +  this->bsymbolic_ = BSYMBOLIC_NON_WEAK_FUNCTIONS;

> +}

> +

>  void

>  General_options::parse_Bsymbolic_functions(const char*, const char*,

>                                            Command_line*)

> diff --git a/gold/options.h b/gold/options.h

> index 757ebf18fec..b14503ce23f 100644

> --- a/gold/options.h

> +++ b/gold/options.h

> @@ -752,6 +752,11 @@ class General_options

>                       "for -shared (default)"),

>                   NULL);

>

> +  DEFINE_special (Bsymbolic_non_weak_functions, options::ONE_DASH, '\0',

> +                 N_ ("Bind default visibility defined STB_GLOBAL function symbols "

> +                     "locally for -shared"),

> +                 NULL);

> +

>    DEFINE_special (Bsymbolic_functions, options::ONE_DASH, '\0',

>                   N_ ("Bind default visibility defined function symbols "

>                       "locally for -shared"),

> @@ -1752,6 +1757,7 @@ class General_options

>    enum Bsymbolic_kind

>    {

>      BSYMBOLIC_NONE,

> +    BSYMBOLIC_NON_WEAK_FUNCTIONS,

>      BSYMBOLIC_FUNCTIONS,

>      BSYMBOLIC_ALL,

>    };

> @@ -1760,6 +1766,10 @@ class General_options

>    Bsymbolic() const

>    { return this->bsymbolic_ == BSYMBOLIC_ALL; }

>

> +  bool

> +  Bsymbolic_non_weak_functions() const

> +  { return this->bsymbolic_ == BSYMBOLIC_NON_WEAK_FUNCTIONS; }

> +

>    bool

>    Bsymbolic_functions() const

>    { return this->bsymbolic_ == BSYMBOLIC_FUNCTIONS; }

> diff --git a/gold/symtab.h b/gold/symtab.h

> index 104429a5af1..b1dce1961bb 100644

> --- a/gold/symtab.h

> +++ b/gold/symtab.h

> @@ -628,7 +628,9 @@ class Symbol

>      // rather than for being STT_FUNC, because that is what the GNU

>      // linker does.

>      if (this->type() != elfcpp::STT_OBJECT

> -       && parameters->options().Bsymbolic_functions())

> +       && (parameters->options().Bsymbolic_functions()

> +           || (parameters->options().Bsymbolic_non_weak_functions()

> +               && this->binding() != elfcpp::STB_WEAK)))

>        return false;

>

>      // Otherwise the symbol is preemptible.

> diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am

> index 38e54818f48..bea11a0675e 100644

> --- a/gold/testsuite/Makefile.am

> +++ b/gold/testsuite/Makefile.am

> @@ -4434,4 +4434,14 @@ retain_2: retain_2.o ../ld-new

>  retain_2.o: retain_2.s

>         $(TEST_AS) -o $@ $<

>

> +check_SCRIPTS += bsymbolic_non_weak_functions.sh

> +check_DATA += bsymbolic_non_weak_functions.stdout

> +MOSTLYCLEANFILES += bsymbolic_non_weak_functions

> +bsymbolic_non_weak_functions: bsymbolic_non_weak_functions.o gcctestdir/ld

> +       $(CXXLINK) $< -shared -Wl,-Bsymbolic-non-weak-functions

> +bsymbolic_non_weak_functions.o: bsymbolic_non_weak_functions.c

> +       $(COMPILE) -c -fpic -o $@ $<

> +bsymbolic_non_weak_functions.stdout: bsymbolic_non_weak_functions

> +       $(TEST_READELF) -rW $< > $@

> +

>  endif DEFAULT_TARGET_X86_64

> diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in

> index 7b4b7832d38..792e4cd63e7 100644

> --- a/gold/testsuite/Makefile.in

> +++ b/gold/testsuite/Makefile.in

> @@ -1130,13 +1130,16 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \

>  @DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@        split_s390x_z4_ns split_s390x_n1_ns split_s390x_n2_ns split_s390x_r

>

>  @DEFAULT_TARGET_X86_64_TRUE@am__append_114 = *.dwo *.dwp pr26936a \

> -@DEFAULT_TARGET_X86_64_TRUE@   pr26936b retain_1 retain_2

> +@DEFAULT_TARGET_X86_64_TRUE@   pr26936b retain_1 retain_2 \

> +@DEFAULT_TARGET_X86_64_TRUE@   bsymbolic_non_weak_functions

>  @DEFAULT_TARGET_X86_64_TRUE@am__append_115 = dwp_test_1.sh \

> -@DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.sh pr26936.sh retain.sh

> +@DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.sh pr26936.sh retain.sh \

> +@DEFAULT_TARGET_X86_64_TRUE@   bsymbolic_non_weak_functions.sh

>  @DEFAULT_TARGET_X86_64_TRUE@am__append_116 = dwp_test_1.stdout \

>  @DEFAULT_TARGET_X86_64_TRUE@   dwp_test_2.stdout pr26936a.stdout \

>  @DEFAULT_TARGET_X86_64_TRUE@   pr26936b.stdout retain_1.out \

> -@DEFAULT_TARGET_X86_64_TRUE@   retain_2.out

> +@DEFAULT_TARGET_X86_64_TRUE@   retain_2.out \

> +@DEFAULT_TARGET_X86_64_TRUE@   bsymbolic_non_weak_functions.stdout

>  subdir = testsuite

>  ACLOCAL_M4 = $(top_srcdir)/aclocal.m4

>  am__aclocal_m4_deps = $(top_srcdir)/../config/ax_pthread.m4 \

> @@ -6459,6 +6462,13 @@ retain.sh.log: retain.sh

>         --log-file $$b.log --trs-file $$b.trs \

>         $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \

>         "$$tst" $(AM_TESTS_FD_REDIRECT)

> +bsymbolic_non_weak_functions.sh.log: bsymbolic_non_weak_functions.sh

> +       @p='bsymbolic_non_weak_functions.sh'; \

> +       b='bsymbolic_non_weak_functions.sh'; \

> +       $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \

> +       --log-file $$b.log --trs-file $$b.trs \

> +       $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \

> +       "$$tst" $(AM_TESTS_FD_REDIRECT)

>  object_unittest.log: object_unittest$(EXEEXT)

>         @p='object_unittest$(EXEEXT)'; \

>         b='object_unittest'; \

> @@ -10414,6 +10424,12 @@ uninstall-am:

>  @DEFAULT_TARGET_X86_64_TRUE@   ../ld-new -pie -e _start --gc-sections -o $@  retain_2.o

>  @DEFAULT_TARGET_X86_64_TRUE@retain_2.o: retain_2.s

>  @DEFAULT_TARGET_X86_64_TRUE@   $(TEST_AS) -o $@ $<

> +@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions: bsymbolic_non_weak_functions.o gcctestdir/ld

> +@DEFAULT_TARGET_X86_64_TRUE@   $(CXXLINK) $< -shared -Wl,-Bsymbolic-non-weak-functions

> +@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions.o: bsymbolic_non_weak_functions.c

> +@DEFAULT_TARGET_X86_64_TRUE@   $(COMPILE) -c -fpic -o $@ $<

> +@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions.stdout: bsymbolic_non_weak_functions

> +@DEFAULT_TARGET_X86_64_TRUE@   $(TEST_READELF) -rW $< > $@

>

>  # Tell versions [3.59,3.63) of GNU make to not export all variables.

>  # Otherwise a system limit (for SysV at least) may be exceeded.

> diff --git a/gold/testsuite/bsymbolic_non_weak_functions.c b/gold/testsuite/bsymbolic_non_weak_functions.c

> new file mode 100644

> index 00000000000..d2002696658

> --- /dev/null

> +++ b/gold/testsuite/bsymbolic_non_weak_functions.c

> @@ -0,0 +1,46 @@

> +// bsymbolic_non_weak_functions.cc -- Test -Bsymbolic-non-weak-functions

> +

> +// Copyright (C) 2021 Free Software Foundation, Inc.

> +

> +// This file is part of gold.

> +

> +// This program is free software; you can redistribute it and/or modify

> +// it under the terms of the GNU General Public License as published by

> +// the Free Software Foundation; either version 3 of the License, or

> +// (at your option) any later version.

> +

> +// This program is distributed in the hope that it will be useful,

> +// but WITHOUT ANY WARRANTY; without even the implied warranty of

> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

> +// GNU General Public License for more details.

> +

> +// You should have received a copy of the GNU General Public License

> +// along with this program; if not, write to the Free Software

> +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,

> +// MA 02110-1301, USA.

> +

> +// The goal of this program is to verify that the --dynamic-list option

> +// allows overrides for symbols listed in the file, and does symbolic

> +// binding for symbols not listed.

> +

> +#include <stdint.h>

> +

> +int global_data = 0;

> +

> +__attribute__ ((noinline)) void global_fun (void);

> +void global_fun (void) { }

> +

> +__attribute__ ((weak, noinline)) void weak_fun (void);

> +void weak_fun (void) { }

> +

> +uintptr_t test (void);

> +

> +uintptr_t test (void)

> +{

> +  uintptr_t x = global_data;

> +  x += (uintptr_t)&global_fun;

> +  x += (uintptr_t)&weak_fun;

> +  global_fun ();

> +  weak_fun ();

> +  return x;

> +}

> diff --git a/gold/testsuite/bsymbolic_non_weak_functions.sh b/gold/testsuite/bsymbolic_non_weak_functions.sh

> new file mode 100755

> index 00000000000..b64d478a8d6

> --- /dev/null

> +++ b/gold/testsuite/bsymbolic_non_weak_functions.sh

> @@ -0,0 +1,46 @@

> +#!/bin/sh

> +

> +# bsymbolic_non_weak_functions.sh -- Test -Bsymbolic-non-weak-functions

> +

> +# Copyright (C) 2021 Free Software Foundation, Inc.

> +

> +# This file is part of gold.

> +

> +# This program is free software; you can redistribute it and/or modify

> +# it under the terms of the GNU General Public License as published by

> +# the Free Software Foundation; either version 3 of the License, or

> +# (at your option) any later version.

> +

> +# This program is distributed in the hope that it will be useful,

> +# but WITHOUT ANY WARRANTY; without even the implied warranty of

> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

> +# GNU General Public License for more details.

> +

> +# You should have received a copy of the GNU General Public License

> +# along with this program; if not, write to the Free Software

> +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,

> +# MA 02110-1301, USA.

> +

> +check_present()

> +{

> +    if ! grep -q "$1" bsymbolic_non_weak_functions.stdout

> +    then

> +        echo "Did not find $1"

> +        exit 1

> +    fi

> +}

> +

> +check_absent()

> +{

> +    if grep -q "$1" bsymbolic_non_weak_functions.stdout

> +    then

> +        echo "Found unexpected $1"

> +        exit 1

> +    fi

> +}

> +

> +check_present 'GLOB_DAT.*global_data'

> +check_present 'GLOB_DAT.*weak_fun'

> +

> +check_present 'JUMP_SLOT.*weak_fun'

> +check_absent  'JUMP_SLOT.*global_fun'

> --

> 2.31.1.751.gd2f1c929bd-goog

>


Ping this and the GNU ld patch
https://sourceware.org/pipermail/binutils/2021-May/116703.html

This will be a great addition to the ELF world. (My libLLVM.so
libclang-cpp.so linked clang is more than 15% faster due to saved
dynamic relocations)
Libor Bukata via Binutils May 27, 2021, 2:38 a.m. | #2
On Wed, May 26, 2021 at 04:22:46PM -0700, Fangrui Song wrote:
> This will be a great addition to the ELF world. (My libLLVM.so

> libclang-cpp.so linked clang is more than 15% faster due to saved

> dynamic relocations)


That is quite an impressive speedup, but couldn't you get exactly the
same effect or better by giving functions protected visibility?

-- 
Alan Modra
Australia Development Lab, IBM
Fangrui Song May 27, 2021, 4:21 a.m. | #3
On 2021-05-27, Alan Modra wrote:
>On Wed, May 26, 2021 at 04:22:46PM -0700, Fangrui Song wrote:

>> This will be a great addition to the ELF world. (My libLLVM.so

>> libclang-cpp.so linked clang is more than 15% faster due to saved

>> dynamic relocations)

>

>That is quite an impressive speedup, but couldn't you get exactly the

>same effect or better by giving functions protected visibility?


The existing -fvisibility=protected is coarse-grained. There isn't an
option to exclude variables.

Even if we introduce a -fvisibility-functions=protected, there is pointer
equality issue for C++.
https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic#pointer-equality-for-functions
The address of an inline function may be different from the address seen from
outside the shared object.

(On x86, it is more unfortunate:
https://maskray.me/blog/2021-01-09-copy-relocations-canonical-plt-entries-and-protected#protected-function-symbols-and-canonical-plt-entries )


-Bsymbolic-non-weak-functions is safer.
We only one thing from compilers https://gcc.gnu.org/PR100593
[ELF] -fno-pic: Use GOT to take address of an external default visibility function

For many distributions, -fno-pic is very uncommon. The canonical PLT entry problem can mostly be ignored.
Fangrui Song June 3, 2021, 4:28 a.m. | #4
:)

On Wed, May 26, 2021 at 9:21 PM Fangrui Song <i@maskray.me> wrote:
>

>

> On 2021-05-27, Alan Modra wrote:

> >On Wed, May 26, 2021 at 04:22:46PM -0700, Fangrui Song wrote:

> >> This will be a great addition to the ELF world. (My libLLVM.so

> >> libclang-cpp.so linked clang is more than 15% faster due to saved

> >> dynamic relocations)

> >

> >That is quite an impressive speedup, but couldn't you get exactly the

> >same effect or better by giving functions protected visibility?

>

> The existing -fvisibility=protected is coarse-grained. There isn't an

> option to exclude variables.

>

> Even if we introduce a -fvisibility-functions=protected, there is pointer

> equality issue for C++.

> https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic#pointer-equality-for-functions

> The address of an inline function may be different from the address seen from

> outside the shared object.

>

> (On x86, it is more unfortunate:

> https://maskray.me/blog/2021-01-09-copy-relocations-canonical-plt-entries-and-protected#protected-function-symbols-and-canonical-plt-entries )

>

>

> -Bsymbolic-non-weak-functions is safer.

> We only one thing from compilers https://gcc.gnu.org/PR100593

> [ELF] -fno-pic: Use GOT to take address of an external default visibility function

>

> For many distributions, -fno-pic is very uncommon. The canonical PLT entry problem can mostly be ignored.
Fangrui Song June 8, 2021, 7:37 a.m. | #5
On Wed, Jun 2, 2021 at 9:28 PM Fangrui Song <i@maskray.me> wrote:
>

> :)


We can look at this from a different angle: security hardening.

@CarlosODonell: LD_PRELOAD/AUDIT give you concealment with no other
privileges. You have several options. Disable semantic interposition
in the way you build your binaries (see MaskRay's work). Disable
PRELOAD/AUDIT, which is what I'm going to pursue e.g. system-wide
glibc hardening tunable.

If LD_PRELOAD is disabled, it feels very natural that we should claim
the -Bsymbolic-non-weak-functions benefit.
Or look it differently: -Bsymbolic-non-weak-functions reduces the
attack surface of arbitrarily binding default visibility functions
elsewhere.

> On Wed, May 26, 2021 at 9:21 PM Fangrui Song <i@maskray.me> wrote:

> >

> >

> > On 2021-05-27, Alan Modra wrote:

> > >On Wed, May 26, 2021 at 04:22:46PM -0700, Fangrui Song wrote:

> > >> This will be a great addition to the ELF world. (My libLLVM.so

> > >> libclang-cpp.so linked clang is more than 15% faster due to saved

> > >> dynamic relocations)

> > >

> > >That is quite an impressive speedup, but couldn't you get exactly the

> > >same effect or better by giving functions protected visibility?

> >

> > The existing -fvisibility=protected is coarse-grained. There isn't an

> > option to exclude variables.

> >

> > Even if we introduce a -fvisibility-functions=protected, there is pointer

> > equality issue for C++.

> > https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic#pointer-equality-for-functions

> > The address of an inline function may be different from the address seen from

> > outside the shared object.

> >

> > (On x86, it is more unfortunate:

> > https://maskray.me/blog/2021-01-09-copy-relocations-canonical-plt-entries-and-protected#protected-function-symbols-and-canonical-plt-entries )

> >

> >

> > -Bsymbolic-non-weak-functions is safer.

> > We only one thing from compilers https://gcc.gnu.org/PR100593

> > [ELF] -fno-pic: Use GOT to take address of an external default visibility function

> >

> > For many distributions, -fno-pic is very uncommon. The canonical PLT entry problem can mostly be ignored.
Libor Bukata via Binutils June 8, 2021, 12:32 p.m. | #6
* Fangrui Song:

> On Wed, Jun 2, 2021 at 9:28 PM Fangrui Song <i@maskray.me> wrote:

>>

>> :)

>

> We can look at this from a different angle: security hardening.

>

> @CarlosODonell: LD_PRELOAD/AUDIT give you concealment with no other

> privileges. You have several options. Disable semantic interposition

> in the way you build your binaries (see MaskRay's work). Disable

> PRELOAD/AUDIT, which is what I'm going to pursue e.g. system-wide

> glibc hardening tunable.

>

> If LD_PRELOAD is disabled, it feels very natural that we should claim

> the -Bsymbolic-non-weak-functions benefit.

> Or look it differently: -Bsymbolic-non-weak-functions reduces the

> attack surface of arbitrarily binding default visibility functions

> elsewhere.


LD_PRELOAD does not cross a trust boundary (as long as it is correctly
implemented by the dynamic loader).

LD_PRELOAD can be used for hot-patching away security vulnerabilities,
by interposing a fixed function.

This argument goes both ways, I think.

Thanks,
Florian

Patch

diff --git a/gold/options.cc b/gold/options.cc
index 5a55bd8ba6d..2335dbccc03 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -347,6 +347,13 @@  General_options::parse_Bno_symbolic(const char*, const char*,
   this->bsymbolic_ = BSYMBOLIC_NONE;
 }
 
+void
+General_options::parse_Bsymbolic_non_weak_functions(const char*, const char*,
+					   Command_line*)
+{
+  this->bsymbolic_ = BSYMBOLIC_NON_WEAK_FUNCTIONS;
+}
+
 void
 General_options::parse_Bsymbolic_functions(const char*, const char*,
 					   Command_line*)
diff --git a/gold/options.h b/gold/options.h
index 757ebf18fec..b14503ce23f 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -752,6 +752,11 @@  class General_options
 		      "for -shared (default)"),
 		  NULL);
 
+  DEFINE_special (Bsymbolic_non_weak_functions, options::ONE_DASH, '\0',
+		  N_ ("Bind default visibility defined STB_GLOBAL function symbols "
+		      "locally for -shared"),
+		  NULL);
+
   DEFINE_special (Bsymbolic_functions, options::ONE_DASH, '\0',
 		  N_ ("Bind default visibility defined function symbols "
 		      "locally for -shared"),
@@ -1752,6 +1757,7 @@  class General_options
   enum Bsymbolic_kind
   {
     BSYMBOLIC_NONE,
+    BSYMBOLIC_NON_WEAK_FUNCTIONS,
     BSYMBOLIC_FUNCTIONS,
     BSYMBOLIC_ALL,
   };
@@ -1760,6 +1766,10 @@  class General_options
   Bsymbolic() const
   { return this->bsymbolic_ == BSYMBOLIC_ALL; }
 
+  bool
+  Bsymbolic_non_weak_functions() const
+  { return this->bsymbolic_ == BSYMBOLIC_NON_WEAK_FUNCTIONS; }
+
   bool
   Bsymbolic_functions() const
   { return this->bsymbolic_ == BSYMBOLIC_FUNCTIONS; }
diff --git a/gold/symtab.h b/gold/symtab.h
index 104429a5af1..b1dce1961bb 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -628,7 +628,9 @@  class Symbol
     // rather than for being STT_FUNC, because that is what the GNU
     // linker does.
     if (this->type() != elfcpp::STT_OBJECT
-	&& parameters->options().Bsymbolic_functions())
+	&& (parameters->options().Bsymbolic_functions()
+	    || (parameters->options().Bsymbolic_non_weak_functions()
+		&& this->binding() != elfcpp::STB_WEAK)))
       return false;
 
     // Otherwise the symbol is preemptible.
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index 38e54818f48..bea11a0675e 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -4434,4 +4434,14 @@  retain_2: retain_2.o ../ld-new
 retain_2.o: retain_2.s
 	$(TEST_AS) -o $@ $<
 
+check_SCRIPTS += bsymbolic_non_weak_functions.sh
+check_DATA += bsymbolic_non_weak_functions.stdout
+MOSTLYCLEANFILES += bsymbolic_non_weak_functions
+bsymbolic_non_weak_functions: bsymbolic_non_weak_functions.o gcctestdir/ld
+	$(CXXLINK) $< -shared -Wl,-Bsymbolic-non-weak-functions
+bsymbolic_non_weak_functions.o: bsymbolic_non_weak_functions.c
+	$(COMPILE) -c -fpic -o $@ $<
+bsymbolic_non_weak_functions.stdout: bsymbolic_non_weak_functions
+	$(TEST_READELF) -rW $< > $@
+
 endif DEFAULT_TARGET_X86_64
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index 7b4b7832d38..792e4cd63e7 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -1130,13 +1130,16 @@  check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_z4_ns split_s390x_n1_ns split_s390x_n2_ns split_s390x_r
 
 @DEFAULT_TARGET_X86_64_TRUE@am__append_114 = *.dwo *.dwp pr26936a \
-@DEFAULT_TARGET_X86_64_TRUE@	pr26936b retain_1 retain_2
+@DEFAULT_TARGET_X86_64_TRUE@	pr26936b retain_1 retain_2 \
+@DEFAULT_TARGET_X86_64_TRUE@	bsymbolic_non_weak_functions
 @DEFAULT_TARGET_X86_64_TRUE@am__append_115 = dwp_test_1.sh \
-@DEFAULT_TARGET_X86_64_TRUE@	dwp_test_2.sh pr26936.sh retain.sh
+@DEFAULT_TARGET_X86_64_TRUE@	dwp_test_2.sh pr26936.sh retain.sh \
+@DEFAULT_TARGET_X86_64_TRUE@	bsymbolic_non_weak_functions.sh
 @DEFAULT_TARGET_X86_64_TRUE@am__append_116 = dwp_test_1.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@	dwp_test_2.stdout pr26936a.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@	pr26936b.stdout retain_1.out \
-@DEFAULT_TARGET_X86_64_TRUE@	retain_2.out
+@DEFAULT_TARGET_X86_64_TRUE@	retain_2.out \
+@DEFAULT_TARGET_X86_64_TRUE@	bsymbolic_non_weak_functions.stdout
 subdir = testsuite
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/ax_pthread.m4 \
@@ -6459,6 +6462,13 @@  retain.sh.log: retain.sh
 	--log-file $$b.log --trs-file $$b.trs \
 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
 	"$$tst" $(AM_TESTS_FD_REDIRECT)
+bsymbolic_non_weak_functions.sh.log: bsymbolic_non_weak_functions.sh
+	@p='bsymbolic_non_weak_functions.sh'; \
+	b='bsymbolic_non_weak_functions.sh'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
 object_unittest.log: object_unittest$(EXEEXT)
 	@p='object_unittest$(EXEEXT)'; \
 	b='object_unittest'; \
@@ -10414,6 +10424,12 @@  uninstall-am:
 @DEFAULT_TARGET_X86_64_TRUE@	../ld-new -pie -e _start --gc-sections -o $@  retain_2.o
 @DEFAULT_TARGET_X86_64_TRUE@retain_2.o: retain_2.s
 @DEFAULT_TARGET_X86_64_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions: bsymbolic_non_weak_functions.o gcctestdir/ld
+@DEFAULT_TARGET_X86_64_TRUE@	$(CXXLINK) $< -shared -Wl,-Bsymbolic-non-weak-functions
+@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions.o: bsymbolic_non_weak_functions.c
+@DEFAULT_TARGET_X86_64_TRUE@	$(COMPILE) -c -fpic -o $@ $<
+@DEFAULT_TARGET_X86_64_TRUE@bsymbolic_non_weak_functions.stdout: bsymbolic_non_weak_functions
+@DEFAULT_TARGET_X86_64_TRUE@	$(TEST_READELF) -rW $< > $@
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/gold/testsuite/bsymbolic_non_weak_functions.c b/gold/testsuite/bsymbolic_non_weak_functions.c
new file mode 100644
index 00000000000..d2002696658
--- /dev/null
+++ b/gold/testsuite/bsymbolic_non_weak_functions.c
@@ -0,0 +1,46 @@ 
+// bsymbolic_non_weak_functions.cc -- Test -Bsymbolic-non-weak-functions
+
+// Copyright (C) 2021 Free Software Foundation, Inc.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// The goal of this program is to verify that the --dynamic-list option
+// allows overrides for symbols listed in the file, and does symbolic
+// binding for symbols not listed.
+
+#include <stdint.h>
+
+int global_data = 0;
+
+__attribute__ ((noinline)) void global_fun (void);
+void global_fun (void) { }
+
+__attribute__ ((weak, noinline)) void weak_fun (void);
+void weak_fun (void) { }
+
+uintptr_t test (void);
+
+uintptr_t test (void)
+{
+  uintptr_t x = global_data;
+  x += (uintptr_t)&global_fun;
+  x += (uintptr_t)&weak_fun;
+  global_fun ();
+  weak_fun ();
+  return x;
+}
diff --git a/gold/testsuite/bsymbolic_non_weak_functions.sh b/gold/testsuite/bsymbolic_non_weak_functions.sh
new file mode 100755
index 00000000000..b64d478a8d6
--- /dev/null
+++ b/gold/testsuite/bsymbolic_non_weak_functions.sh
@@ -0,0 +1,46 @@ 
+#!/bin/sh
+
+# bsymbolic_non_weak_functions.sh -- Test -Bsymbolic-non-weak-functions
+
+# Copyright (C) 2021 Free Software Foundation, Inc.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+check_present()
+{
+    if ! grep -q "$1" bsymbolic_non_weak_functions.stdout
+    then
+        echo "Did not find $1"
+        exit 1
+    fi
+}
+
+check_absent()
+{
+    if grep -q "$1" bsymbolic_non_weak_functions.stdout
+    then
+        echo "Found unexpected $1"
+        exit 1
+    fi
+}
+
+check_present 'GLOB_DAT.*global_data'
+check_present 'GLOB_DAT.*weak_fun'
+
+check_present 'JUMP_SLOT.*weak_fun'
+check_absent  'JUMP_SLOT.*global_fun'