+2020-06-03  Fangrui Song  <maskray@google.com>
+           H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR ld/25910
+       * NEWS: Mention --export-dynamic-symbol[-list].
+       * ld.texi: Document --export-dynamic-symbol[-list].
+       * ldgram.y: Pass current_dynamic_list_p to
+       lang_append_dynamic_list.
+       * ldlang.c (current_dynamic_list_p): New.
+       (ang_append_dynamic_list): Updated to take a pointer to
+       struct bfd_elf_dynamic_list * argument instead of using
+       link_info.dynamic_list.
+       (lang_append_dynamic_list_cpp_typeinfo): Pass
+       &link_info.dynamic_list to ang_append_dynamic_list.
+       (lang_append_dynamic_list_cpp_new): Likewise.
+       * ldlang.h (current_dynamic_list_p): New.
+       (lang_append_dynamic_list): Add a pointer to
+       struct bfd_elf_dynamic_list * argument.
+       * ldlex.h (option_values): Add OPTION_EXPORT_DYNAMIC_SYMBOL and
+       OPTION_EXPORT_DYNAMIC_SYMBOL_LIST.
+       * lexsup.c (ld_options): Add entries for
+       OPTION_EXPORT_DYNAMIC_SYMBOL and
+       OPTION_EXPORT_DYNAMIC_SYMBOL_LIST.
+       (parse_args): Handle --export-dynamic-symbol and
+       --export-dynamic-symbol-list.
+       * testsuite/ld-dynamic/export-dynamic-symbol-1.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol-2.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol-glob.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol-list-1.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol-list-2.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol-list-glob.d: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol.exp: New.
+       * testsuite/ld-dynamic/export-dynamic-symbol.s: New.
+       * testsuite/ld-dynamic/foo-bar.list: New.
+       * testsuite/ld-dynamic/foo.list: New.
+       * testsuite/ld-dynamic/foo.s: New.
+       * testsuite/ld-dynamic/fstar.list: New.
+       * testsuite/ld-elf/dlempty.list: New.
+       * testsuite/ld-elf/shared.exp: Add tests for
+       --export-dynamic-symbol and --export-dynamic-symbol-list.
+
 2020-06-03  Jan Beulich  <jbeulich@suse.com>
 
        * testsuite/ld-elf/linux-x86.exp: Make copied source files
 
 -*- text -*-
 
+* Add ELF linker command-line options, --export-dynamic-symbol and
+  --export-dynamic-symbol-list, to make symbols dynamic.
+
 * Add a configure option, --enable-textrel-check=[no|yes|warning|error],
   to decide what ELF linker should do by default with DT_TEXTREL in an
   executable or shared library.  Default to yes for Linux/x86 targets.
 
 support a similar function to export all symbols from a DLL or EXE; see
 the description of @samp{--export-all-symbols} below.
 
+@kindex --export-dynamic-symbol=@var{glob}
+@cindex export dynamic symbol
+@item --export-dynamic-symbol=@var{glob}
+When creating a dynamically linked executable, symbols matching
+@var{glob} will be added to the dynamic symbol table. When creating a
+shared library, references to symbols matching @var{glob} will not be
+bound to the definitions within the shared library. This option is a
+no-op when creating a shared library and @samp{-Bsymbolic} or
+@samp{--dynamic-list} are not specified. This option is only meaningful
+on ELF platforms which support shared libraries.
+
+@kindex --export-dynamic-symbol-list=@var{file}
+@cindex export dynamic symbol list
+@item --export-dynamic-symbol-list=@var{file}
+Specify a @samp{--export-dynamic-symbol} for each pattern in the file.
+The format of the file is the same as the version node without
+scope and node name.  See @ref{VERSION} for more information.
+
 @ifclear SingleFormat
 @cindex big-endian objects
 @cindex endianness
 
 dynamic_list_tag:
                vers_defns ';'
                {
-                 lang_append_dynamic_list ($1);
+                 lang_append_dynamic_list (current_dynamic_list_p, $1);
                }
        ;
 
 
    lang_statement_union).  */
 lang_statement_list_type input_file_chain;
 static const char *current_input_file;
+struct bfd_elf_dynamic_list **current_dynamic_list_p;
 struct bfd_sym_chain entry_symbol = { NULL, NULL };
 const char *entry_section = ".text";
 struct lang_input_statement_flags input_flags;
 /* Append the list of dynamic symbols to the existing one.  */
 
 void
-lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
+lang_append_dynamic_list (struct bfd_elf_dynamic_list **list_p,
+                         struct bfd_elf_version_expr *dynamic)
 {
-  if (link_info.dynamic_list)
+  if (*list_p)
     {
       struct bfd_elf_version_expr *tail;
       for (tail = dynamic; tail->next != NULL; tail = tail->next)
        ;
-      tail->next = link_info.dynamic_list->head.list;
-      link_info.dynamic_list->head.list = dynamic;
+      tail->next = (*list_p)->head.list;
+      (*list_p)->head.list = dynamic;
     }
   else
     {
       d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
       d->head.list = dynamic;
       d->match = lang_vers_match;
-      link_info.dynamic_list = d;
+      *list_p = d;
     }
 }
 
     dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
                                     FALSE);
 
-  lang_append_dynamic_list (dynamic);
+  lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
 }
 
 /* Append the list of C++ operator new and delete dynamic symbols to the
     dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
                                     FALSE);
 
-  lang_append_dynamic_list (dynamic);
+  lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
 }
 
 /* Scan a space and/or comma separated string of features.  */
 
 extern lang_statement_list_type file_chain;
 extern lang_statement_list_type input_file_chain;
 
+extern struct bfd_elf_dynamic_list **current_dynamic_list_p;
+
 extern int lang_statement_iteration;
 extern struct asneeded_minfo **asneeded_list_tail;
 
   (struct bfd_elf_version_deps *, const char *);
 extern void lang_register_vers_node
   (const char *, struct bfd_elf_version_tree *, struct bfd_elf_version_deps *);
-extern void lang_append_dynamic_list (struct bfd_elf_version_expr *);
+extern void lang_append_dynamic_list (struct bfd_elf_dynamic_list **,
+                                     struct bfd_elf_version_expr *);
 extern void lang_append_dynamic_list_cpp_typeinfo (void);
 extern void lang_append_dynamic_list_cpp_new (void);
 extern void lang_add_unique
 
   OPTION_DYNAMIC_LIST_CPP_NEW,
   OPTION_DYNAMIC_LIST_CPP_TYPEINFO,
   OPTION_DYNAMIC_LIST_DATA,
+  OPTION_EXPORT_DYNAMIC_SYMBOL,
+  OPTION_EXPORT_DYNAMIC_SYMBOL_LIST,
   OPTION_WARN_COMMON,
   OPTION_WARN_CONSTRUCTORS,
   OPTION_WARN_FATAL,
 
     '\0', NULL, N_("Use C++ typeinfo dynamic list"), TWO_DASHES },
   { {"dynamic-list", required_argument, NULL, OPTION_DYNAMIC_LIST},
     '\0', N_("FILE"), N_("Read dynamic list"), TWO_DASHES },
+  { {"export-dynamic-symbol", required_argument, NULL, OPTION_EXPORT_DYNAMIC_SYMBOL},
+    '\0', N_("SYMBOL"), N_("Export the specified symbol"), EXACTLY_TWO_DASHES },
+  { {"export-dynamic-symbol-list", required_argument, NULL, OPTION_EXPORT_DYNAMIC_SYMBOL_LIST},
+    '\0', N_("FILE"), N_("Read export dynamic symbol list"), EXACTLY_TWO_DASHES },
   { {"warn-common", no_argument, NULL, OPTION_WARN_COMMON},
     '\0', NULL, N_("Warn about duplicate common symbols"), TWO_DASHES },
   { {"warn-constructors", no_argument, NULL, OPTION_WARN_CONSTRUCTORS},
     dynamic_list_data,
     dynamic_list
   } opt_dynamic_list = dynamic_list_unset;
+  struct bfd_elf_dynamic_list *export_list = NULL;
 
   shortopts = (char *) xmalloc (OPTION_COUNT * 3 + 2);
   longopts = (struct option *)
            ldfile_open_command_file (optarg);
            saved_script_handle = hold_script_handle;
            parser_input = input_dynamic_list;
+           current_dynamic_list_p = &link_info.dynamic_list;
            yyparse ();
          }
          if (opt_dynamic_list != dynamic_list_data)
            opt_dynamic_list = dynamic_list;
          break;
+       case OPTION_EXPORT_DYNAMIC_SYMBOL:
+         {
+           struct bfd_elf_version_expr *expr
+             = lang_new_vers_pattern (NULL, xstrdup (optarg), NULL,
+                                      FALSE);
+           lang_append_dynamic_list (&export_list, expr);
+          }
+         break;
+       case OPTION_EXPORT_DYNAMIC_SYMBOL_LIST:
+         /* This option indicates a small script that only specifies
+            an export list.  Read it, but don't assume that we've
+            seen a linker script.  */
+         {
+           FILE *hold_script_handle;
+
+           hold_script_handle = saved_script_handle;
+           ldfile_open_command_file (optarg);
+           saved_script_handle = hold_script_handle;
+           parser_input = input_dynamic_list;
+           current_dynamic_list_p = &export_list;
+           yyparse ();
+         }
+         break;
        case OPTION_WARN_COMMON:
          config.warn_common = TRUE;
          break;
       && command_line.check_section_addresses < 0)
     command_line.check_section_addresses = 0;
 
+  if (export_list)
+    {
+      struct bfd_elf_version_expr *head = export_list->head.list;
+      struct bfd_elf_version_expr *next;
+
+      /* For --export-dynamic-symbol[-list]:
+        1. When building executable, treat like --dynamic-list.
+        2. When building shared object:
+           a. If -Bsymbolic or --dynamic-list are used, treat like
+              --dynamic-list.
+           b. Otherwise, ignored.
+       */
+      if (!bfd_link_relocatable (&link_info)
+         && (bfd_link_executable (&link_info)
+             || opt_symbolic != symbolic_unset
+             || opt_dynamic_list != dynamic_list_unset))
+       {
+         /* Append the export list to link_info.dynamic_list.  */
+         if (link_info.dynamic_list)
+           {
+             for (next = head; next->next != NULL; next = next->next)
+               ;
+             next->next = link_info.dynamic_list->head.list;
+             link_info.dynamic_list->head.list = head;
+           }
+         else
+           link_info.dynamic_list = export_list;
+
+         if (opt_dynamic_list != dynamic_list_data)
+           opt_dynamic_list = dynamic_list;
+       }
+      else
+       {
+         /* Free the export list.  */
+         for (; head->next != NULL; head = next)
+           {
+             next = head->next;
+             free (head);
+           }
+         free (export_list);
+       }
+    }
+
   switch (opt_dynamic_list)
     {
     case dynamic_list_unset:
 
--- /dev/null
+#name: --export-dynamic-symbol foo archive
+#source: export-dynamic-symbol.s
+#ld: -pie --export-dynamic-symbol foo tmpdir/libpr25910.a
+#nm: -n
+
+#failif
+#...
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+#name: -u --export-dynamic-symbol foo archive
+#source: export-dynamic-symbol.s
+#ld: -pie -u foo --export-dynamic-symbol foo --export-dynamic-symbol=bar tmpdir/libpr25910.a
+#nm: -D
+
+#...
+[0-9a-f]+ T +bar
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+#name: -u --export-dynamic-symbol "f*" archive
+#source: export-dynamic-symbol.s
+#ld: -pie -u foo --export-dynamic-symbol "f*" tmpdir/libpr25910.a
+#nm: -D
+
+#...
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+#name: --export-dynamic-symbol-list foo archive
+#source: export-dynamic-symbol.s
+#ld: -pie --export-dynamic-symbol-list foo.list tmpdir/libpr25910.a
+#nm: -n
+
+#failif
+#...
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+#name: -u --export-dynamic-symbol-list foo bar archive
+#source: export-dynamic-symbol.s
+#ld: -pie -u foo --export-dynamic-symbol-list foo-bar.list tmpdir/libpr25910.a
+#nm: -D
+
+#...
+[0-9a-f]+ T +bar
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+#name: -u --export-dynamic-symbol-list fstar archive
+#source: export-dynamic-symbol.s
+#ld: -pie -u foo --export-dynamic-symbol-list fstar.list tmpdir/libpr25910.a
+#nm: -D
+
+#...
+[0-9a-f]+ T +foo
+#...
 
--- /dev/null
+# Expect script for ld --export-dynamic-symbol tests
+#   Copyright (C) 2020 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# 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.
+#
+
+if ![is_elf_format] {
+    return
+}
+
+# Skip targets where -shared is not supported
+
+if ![check_shared_lib_support] {
+    return
+}
+
+set old_ASFLAGS $ASFLAGS
+
+# These targets require extra GAS options when building code for shared
+# libraries.
+if [istarget "nds32*-*"] {
+    append ASFLAGS " -mpic"
+}
+if [istarget "tic6x-*-*"] {
+    append ASFLAGS " -mpic -mpid=near"
+}
+if [istarget "sparc*-*-*"] {
+    append ASFLAGS " -K PIC -Av9"
+}
+
+set old_LDFLAGS $LDFLAGS
+
+# These targets require a non-default emulation for successful shared
+# library/executable builds.
+if [istarget "tic6x-*-*"] {
+    append LDFLAGS " -melf32_tic6x_le"
+}
+
+set build_tests {
+  {"Build libpr25910.a"
+   "" "" ""
+   {foo.s} {} "libpr25910.a"}
+}
+
+run_ld_link_tests $build_tests
+
+set test_list [lsort [glob -nocomplain $srcdir/$subdir/export-dynamic-symbol-*.d]]
+foreach test_file $test_list {
+    set test_name [file rootname $test_file]
+    verbose $test_name
+    run_dump_test $test_name
+}
+
+set ASFLAGS $old_ASFLAGS
+set LDFLAGS $old_LDFLAGS
 
--- /dev/null
+       .text
+       .global start   /* Used by SH targets.  */
+start:
+       .global _start
+_start:
+       .global __start
+__start:
+       .global main    /* Used by HPPA targets.  */
+main:
+       .globl  _main   /* Used by LynxOS targets.  */
+_main:
+       .word 0
+
+       .section .text.1, "ax"
+       .global bar
+bar:
+       .word 0
 
--- /dev/null
+{ foo; bar; };
 
--- /dev/null
+       .text
+       .globl foo
+foo:
+       .byte 0
 
--- /dev/null
+{
+  empty;
+};
 
   {"Build libdl2d.so with --dynamic-list-data -Bsymbolic"
    "-shared -Wl,-Bsymbolic,--dynamic-list-data" "-fPIC"
    {dl2.c dl2xxx.c} {} "libdl2d.so"}
+  {"Build libdl2e.so with --export-dynamic-symbol=foo"
+   "-shared -Wl,--export-dynamic-symbol=foo" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2e.so"}
+  {"Build libdl2f.so with --dynamic-list=dlempty.list and --export-dynamic-symbol=foo"
+   "-shared -Wl,--dynamic-list=dlempty.list,--export-dynamic-symbol=foo" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2f.so"}
+  {"Build libdl2g.so with --export-dynamic-symbol-list=dl2.list"
+   "-shared -Wl,--export-dynamic-symbol-list=dl2.list" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2g.so"}
+  {"Build libdl2h.so with --dynamic-list=dlempty.list and --export-dynamic-symbol-list=dl2.list"
+   "-shared -Wl,--dynamic-list=dlempty.list,--export-dynamic-symbol-list=dl2.list" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2h.so"}
+  {"Build libdl2i.so with -Bsymbolic and --export-dynamic-symbol=foo"
+   "-shared -Wl,-Bsymbolic,--export-dynamic-symbol=foo" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2i.so"}
+  {"Build libdl2j.so with -Bsymbolic and --export-dynamic-symbol-list=dl2.list"
+   "-shared -Wl,-Bsymbolic,--export-dynamic-symbol-list=dl2.list" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2j.so"}
+  {"Build libdl2k.so with --export-dynamic-symbol-list=dl2.list and -Bsymbolic"
+   "-shared -Wl,--export-dynamic-symbol-list=dl2.list,-Bsymbolic" "-fPIC"
+   {dl2.c dl2xxx.c} {} "libdl2k.so"}
   {"Build libdl4a.so with --dynamic-list=dl4.list"
    "-shared -Wl,--dynamic-list=dl4.list" "-fPIC"
    {dl4.c dl4xxx.c} {} "libdl4a.so"}
     [list "Run with libdl2d.so" \
      "-Wl,--no-as-needed tmpdir/libdl2d.so" "" \
      {dl2main.c} "dl2d" "dl2a.out" ] \
+    [list "Run with libdl2e.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2e.so" "" \
+     {dl2main.c} "dl2e" "dl2b.out" ] \
+    [list "Run with libdl2f.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2f.so" "" \
+     {dl2main.c} "dl2f" "dl2a.out" ] \
+    [list "Run with libdl2g.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2g.so" "" \
+     {dl2main.c} "dl2g" "dl2b.out" ] \
+    [list "Run with libdl2h.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2h.so" "" \
+     {dl2main.c} "dl2h" "dl2a.out" ] \
+    [list "Run with libdl2i.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2i.so" "" \
+     {dl2main.c} "dl2i" "dl2a.out" ] \
+    [list "Run with libdl2j.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2j.so" "" \
+     {dl2main.c} "dl2j" "dl2a.out" ] \
+    [list "Run with libdl2k.so" \
+     "-Wl,--no-as-needed tmpdir/libdl2k.so" "" \
+     {dl2main.c} "dl2k" "dl2a.out" ] \
     [list "Run with libdl4a.so" \
      "-Wl,--no-as-needed tmpdir/libdl4a.so" "" \
      {dl4main.c} "dl4a" "dl4a.out" ] \