ld: Add --export-dynamic-symbol

Message ID 20200503061142.161649-1-maskray@google.com
State New
Headers show
Series
  • ld: Add --export-dynamic-symbol
Related show

Commit Message

PR ld/25910
	* ldlang.c (export_dynamic_symbol_list): New.
	(ldlang_add_export_dynamic_symbol): New.
	(lang_place_export_dynamic_symbols): New.
	(lang_process): Handle export_dynamic_symbol_list.
	* ldlang.h (ldlang_add_export_dynamic_symbol): New.
	* ldlex.h (option_values): Add OPTION_EXPORT_DYNAMIC_SYMBOL.
	* lexsup.c (ld_options): Add entry for OPTION_EXPORT_DYNAMIC_SYMBOL.
	(parse_args): Handle it.
	* ld.texi: Add --export-dynamic-symbol documentation.
	* testsuite/ld-undefined/export-dynamic-symbol.exp: Run new test.
	* testsuite/ld-undefined/export-dynamic-symbol-1.d: New.
---
 ld/ld.texi                                    |  8 +++++
 ld/ldlang.c                                   | 26 ++++++++++++++
 ld/ldlang.h                                   |  2 ++
 ld/ldlex.h                                    |  1 +
 ld/lexsup.c                                   |  7 ++++
 .../ld-undefined/export-dynamic-symbol-1.d    |  8 +++++
 .../ld-undefined/export-dynamic-symbol.exp    | 35 +++++++++++++++++++
 7 files changed, 87 insertions(+)
 create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol-1.d
 create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol.exp

-- 
2.26.2.526.g744177e7f7-goog

Comments

On Sat, May 2, 2020 at 11:15 PM Fangrui Song via Binutils
<binutils@sourceware.org> wrote:
>

>         PR ld/25910

>         * ldlang.c (export_dynamic_symbol_list): New.

>         (ldlang_add_export_dynamic_symbol): New.

>         (lang_place_export_dynamic_symbols): New.

>         (lang_process): Handle export_dynamic_symbol_list.

>         * ldlang.h (ldlang_add_export_dynamic_symbol): New.

>         * ldlex.h (option_values): Add OPTION_EXPORT_DYNAMIC_SYMBOL.

>         * lexsup.c (ld_options): Add entry for OPTION_EXPORT_DYNAMIC_SYMBOL.

>         (parse_args): Handle it.

>         * ld.texi: Add --export-dynamic-symbol documentation.

>         * testsuite/ld-undefined/export-dynamic-symbol.exp: Run new test.

>         * testsuite/ld-undefined/export-dynamic-symbol-1.d: New.

> ---

>  ld/ld.texi                                    |  8 +++++

>  ld/ldlang.c                                   | 26 ++++++++++++++

>  ld/ldlang.h                                   |  2 ++

>  ld/ldlex.h                                    |  1 +

>  ld/lexsup.c                                   |  7 ++++

>  .../ld-undefined/export-dynamic-symbol-1.d    |  8 +++++

>  .../ld-undefined/export-dynamic-symbol.exp    | 35 +++++++++++++++++++

>  7 files changed, 87 insertions(+)

>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol.exp

>

> diff --git a/ld/ld.texi b/ld/ld.texi

> index 4dc78e65fa..b3dc95f314 100644

> --- a/ld/ld.texi

> +++ b/ld/ld.texi

> @@ -569,6 +569,14 @@ Note that this option is specific to ELF targeted ports.  PE 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{symbol}

> +@cindex export dynamic symbol

> +@item --export-dynamic-symbol=@var{symbol}

> +Specify a symbol that should be added to the dynamic symbol table.

> +Additionally, force @var{symbol} to be entered in the output file as an

> +undefined symbol.  Doing this may, for example, trigger linking of additional

> +modules from archives.

> +

>  @ifclear SingleFormat

>  @cindex big-endian objects

>  @cindex endianness

> diff --git a/ld/ldlang.c b/ld/ldlang.c

> index b2cdb3603a..e7ecdfeaf0 100644

> --- a/ld/ldlang.c

> +++ b/ld/ldlang.c

> @@ -3910,6 +3910,26 @@ lang_place_undefineds (void)

>      insert_undefined (ptr->name);

>  }

>

> +static struct bfd_sym_chain export_dynamic_symbol_list = { NULL, NULL };

> +

> +void

> +ldlang_add_export_dynamic_symbol (const char *const name)

> +{

> +  struct bfd_sym_chain *sym;

> +  sym = stat_alloc (sizeof (*sym));

> +  sym->name = xstrdup (name);

> +  sym->next = export_dynamic_symbol_list.next;

> +  export_dynamic_symbol_list.next = sym;

> +}

> +

> +static void

> +lang_place_export_dynamic_symbols (void)

> +{

> +  struct bfd_sym_chain *sym;

> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

> +    insert_undefined (sym->name);

> +}

> +

>  /* Structure used to build the list of symbols that the user has required

>     be defined.  */

>

> @@ -7795,6 +7815,10 @@ void

>  lang_process (void)

>  {

>    /* Finalize dynamic list.  */

> +  struct bfd_sym_chain *sym;

> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

> +    lang_append_dynamic_list (

> +        lang_new_vers_pattern (NULL, sym->name, NULL, FALSE));

>    if (link_info.dynamic_list)

>      lang_finalize_version_expr_head (&link_info.dynamic_list->head);

>

> @@ -7808,6 +7832,8 @@ lang_process (void)

>

>    /* Add to the hash table all undefineds on the command line.  */

>    lang_place_undefineds ();

> +  /* Add --export-dynamic-symbol symbols to the hash table.  */

> +  lang_place_export_dynamic_symbols ();

>

>    if (!bfd_section_already_linked_table_init ())

>      einfo (_("%F%P: can not create hash table: %E\n"));

> diff --git a/ld/ldlang.h b/ld/ldlang.h

> index 2aa3930f95..8c004b173c 100644

> --- a/ld/ldlang.h

> +++ b/ld/ldlang.h

> @@ -606,6 +606,8 @@ extern lang_output_section_statement_type *next_matching_output_section_statemen

>    (lang_output_section_statement_type *, int);

>  extern void ldlang_add_undef

>    (const char *const, bfd_boolean);

> +extern void ldlang_add_export_dynamic_symbol

> +  (const char *const);

>  extern void ldlang_add_require_defined

>    (const char *const);

>  extern void lang_add_output_format

> diff --git a/ld/ldlex.h b/ld/ldlex.h

> index 22b928d2d9..70f2da5636 100644

> --- a/ld/ldlex.h

> +++ b/ld/ldlex.h

> @@ -81,6 +81,7 @@ enum option_values

>    OPTION_DYNAMIC_LIST_CPP_NEW,

>    OPTION_DYNAMIC_LIST_CPP_TYPEINFO,

>    OPTION_DYNAMIC_LIST_DATA,

> +  OPTION_EXPORT_DYNAMIC_SYMBOL,

>    OPTION_WARN_COMMON,

>    OPTION_WARN_CONSTRUCTORS,

>    OPTION_WARN_FATAL,

> diff --git a/ld/lexsup.c b/ld/lexsup.c

> index d1955b9afa..0a0c2f2873 100644

> --- a/ld/lexsup.c

> +++ b/ld/lexsup.c

> @@ -504,6 +504,8 @@ static const struct ld_option ld_options[] =

>      '\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_("Read dynamic list"), 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},

> @@ -1425,6 +1427,11 @@ parse_args (unsigned argc, char **argv)

>           if (opt_symbolic == symbolic)

>             opt_symbolic = symbolic_unset;

>           break;

> +       case OPTION_EXPORT_DYNAMIC_SYMBOL:

> +         ldlang_add_export_dynamic_symbol (optarg);

> +         if (opt_dynamic_list != dynamic_list_data)

> +           opt_dynamic_list = dynamic_list;

> +         break;

>         case OPTION_WARN_COMMON:

>           config.warn_common = TRUE;

>           break;

> diff --git a/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

> new file mode 100644

> index 0000000000..768bf0abd6

> --- /dev/null

> +++ b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

> @@ -0,0 +1,8 @@

> +#name: --export-dynamic-symbol foo archive

> +#source: require-defined.s

> +#ld: -pie --export-dynamic-symbol foo tmpdir/libentry.a


I assume that it supports

$ ld -pie --export-dynamic-symbol foo --export-dynamic-symbol bar ...

Please add another --export-dynamic-symbol to your test.

-- 
H.J.
On 2020-05-03, H.J. Lu wrote:
>On Sat, May 2, 2020 at 11:15 PM Fangrui Song via Binutils

><binutils@sourceware.org> wrote:

>>

>>         PR ld/25910

>>         * ldlang.c (export_dynamic_symbol_list): New.

>>         (ldlang_add_export_dynamic_symbol): New.

>>         (lang_place_export_dynamic_symbols): New.

>>         (lang_process): Handle export_dynamic_symbol_list.

>>         * ldlang.h (ldlang_add_export_dynamic_symbol): New.

>>         * ldlex.h (option_values): Add OPTION_EXPORT_DYNAMIC_SYMBOL.

>>         * lexsup.c (ld_options): Add entry for OPTION_EXPORT_DYNAMIC_SYMBOL.

>>         (parse_args): Handle it.

>>         * ld.texi: Add --export-dynamic-symbol documentation.

>>         * testsuite/ld-undefined/export-dynamic-symbol.exp: Run new test.

>>         * testsuite/ld-undefined/export-dynamic-symbol-1.d: New.

>> ---

>>  ld/ld.texi                                    |  8 +++++

>>  ld/ldlang.c                                   | 26 ++++++++++++++

>>  ld/ldlang.h                                   |  2 ++

>>  ld/ldlex.h                                    |  1 +

>>  ld/lexsup.c                                   |  7 ++++

>>  .../ld-undefined/export-dynamic-symbol-1.d    |  8 +++++

>>  .../ld-undefined/export-dynamic-symbol.exp    | 35 +++++++++++++++++++

>>  7 files changed, 87 insertions(+)

>>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

>>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol.exp

>>

>> diff --git a/ld/ld.texi b/ld/ld.texi

>> index 4dc78e65fa..b3dc95f314 100644

>> --- a/ld/ld.texi

>> +++ b/ld/ld.texi

>> @@ -569,6 +569,14 @@ Note that this option is specific to ELF targeted ports.  PE 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{symbol}

>> +@cindex export dynamic symbol

>> +@item --export-dynamic-symbol=@var{symbol}

>> +Specify a symbol that should be added to the dynamic symbol table.

>> +Additionally, force @var{symbol} to be entered in the output file as an

>> +undefined symbol.  Doing this may, for example, trigger linking of additional

>> +modules from archives.

>> +

>>  @ifclear SingleFormat

>>  @cindex big-endian objects

>>  @cindex endianness

>> diff --git a/ld/ldlang.c b/ld/ldlang.c

>> index b2cdb3603a..e7ecdfeaf0 100644

>> --- a/ld/ldlang.c

>> +++ b/ld/ldlang.c

>> @@ -3910,6 +3910,26 @@ lang_place_undefineds (void)

>>      insert_undefined (ptr->name);

>>  }

>>

>> +static struct bfd_sym_chain export_dynamic_symbol_list = { NULL, NULL };

>> +

>> +void

>> +ldlang_add_export_dynamic_symbol (const char *const name)

>> +{

>> +  struct bfd_sym_chain *sym;

>> +  sym = stat_alloc (sizeof (*sym));

>> +  sym->name = xstrdup (name);

>> +  sym->next = export_dynamic_symbol_list.next;

>> +  export_dynamic_symbol_list.next = sym;

>> +}

>> +

>> +static void

>> +lang_place_export_dynamic_symbols (void)

>> +{

>> +  struct bfd_sym_chain *sym;

>> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

>> +    insert_undefined (sym->name);

>> +}

>> +

>>  /* Structure used to build the list of symbols that the user has required

>>     be defined.  */

>>

>> @@ -7795,6 +7815,10 @@ void

>>  lang_process (void)

>>  {

>>    /* Finalize dynamic list.  */

>> +  struct bfd_sym_chain *sym;

>> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

>> +    lang_append_dynamic_list (

>> +        lang_new_vers_pattern (NULL, sym->name, NULL, FALSE));

>>    if (link_info.dynamic_list)

>>      lang_finalize_version_expr_head (&link_info.dynamic_list->head);

>>

>> @@ -7808,6 +7832,8 @@ lang_process (void)

>>

>>    /* Add to the hash table all undefineds on the command line.  */

>>    lang_place_undefineds ();

>> +  /* Add --export-dynamic-symbol symbols to the hash table.  */

>> +  lang_place_export_dynamic_symbols ();

>>

>>    if (!bfd_section_already_linked_table_init ())

>>      einfo (_("%F%P: can not create hash table: %E\n"));

>> diff --git a/ld/ldlang.h b/ld/ldlang.h

>> index 2aa3930f95..8c004b173c 100644

>> --- a/ld/ldlang.h

>> +++ b/ld/ldlang.h

>> @@ -606,6 +606,8 @@ extern lang_output_section_statement_type *next_matching_output_section_statemen

>>    (lang_output_section_statement_type *, int);

>>  extern void ldlang_add_undef

>>    (const char *const, bfd_boolean);

>> +extern void ldlang_add_export_dynamic_symbol

>> +  (const char *const);

>>  extern void ldlang_add_require_defined

>>    (const char *const);

>>  extern void lang_add_output_format

>> diff --git a/ld/ldlex.h b/ld/ldlex.h

>> index 22b928d2d9..70f2da5636 100644

>> --- a/ld/ldlex.h

>> +++ b/ld/ldlex.h

>> @@ -81,6 +81,7 @@ enum option_values

>>    OPTION_DYNAMIC_LIST_CPP_NEW,

>>    OPTION_DYNAMIC_LIST_CPP_TYPEINFO,

>>    OPTION_DYNAMIC_LIST_DATA,

>> +  OPTION_EXPORT_DYNAMIC_SYMBOL,

>>    OPTION_WARN_COMMON,

>>    OPTION_WARN_CONSTRUCTORS,

>>    OPTION_WARN_FATAL,

>> diff --git a/ld/lexsup.c b/ld/lexsup.c

>> index d1955b9afa..0a0c2f2873 100644

>> --- a/ld/lexsup.c

>> +++ b/ld/lexsup.c

>> @@ -504,6 +504,8 @@ static const struct ld_option ld_options[] =

>>      '\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_("Read dynamic list"), 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},

>> @@ -1425,6 +1427,11 @@ parse_args (unsigned argc, char **argv)

>>           if (opt_symbolic == symbolic)

>>             opt_symbolic = symbolic_unset;

>>           break;

>> +       case OPTION_EXPORT_DYNAMIC_SYMBOL:

>> +         ldlang_add_export_dynamic_symbol (optarg);

>> +         if (opt_dynamic_list != dynamic_list_data)

>> +           opt_dynamic_list = dynamic_list;

>> +         break;

>>         case OPTION_WARN_COMMON:

>>           config.warn_common = TRUE;

>>           break;

>> diff --git a/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

>> new file mode 100644

>> index 0000000000..768bf0abd6

>> --- /dev/null

>> +++ b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

>> @@ -0,0 +1,8 @@

>> +#name: --export-dynamic-symbol foo archive

>> +#source: require-defined.s

>> +#ld: -pie --export-dynamic-symbol foo tmpdir/libentry.a

>

>I assume that it supports

>

>$ ld -pie --export-dynamic-symbol foo --export-dynamic-symbol bar ...

>

>Please add another --export-dynamic-symbol to your test.

>

>-- 

>H.J.


I face a test difficulty. If I add another `#nm:`, runtest complains:

% cd Debug/ld
% runtest --tool ld --srcdir ../../ld/testsuite/ export-dynamic-symbol.exp
...
ERROR: option nm multiply set in ../../ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

There may also be a problem of symbol table order determinism. I don't
know whether another symbol may be ordered before foo in some ports and
after foo in other ports.

Honestly, I think for a number of tests, even if they test generic
behavior, it might be fine to restrict them to run on certain hosts,
e.g. Linux x86. What I know about testing is lacking, though, I don't
know what can be tested.

Ideally `#nm:` should just be free-form shell scripts but unfortunately
it seems to support very limited features (undocumented?)
On Mon, May 4, 2020 at 10:48 AM Fangrui Song <maskray@google.com> wrote:
>

> On 2020-05-03, H.J. Lu wrote:

> >On Sat, May 2, 2020 at 11:15 PM Fangrui Song via Binutils

> ><binutils@sourceware.org> wrote:

> >>

> >>         PR ld/25910

> >>         * ldlang.c (export_dynamic_symbol_list): New.

> >>         (ldlang_add_export_dynamic_symbol): New.

> >>         (lang_place_export_dynamic_symbols): New.

> >>         (lang_process): Handle export_dynamic_symbol_list.

> >>         * ldlang.h (ldlang_add_export_dynamic_symbol): New.

> >>         * ldlex.h (option_values): Add OPTION_EXPORT_DYNAMIC_SYMBOL.

> >>         * lexsup.c (ld_options): Add entry for OPTION_EXPORT_DYNAMIC_SYMBOL.

> >>         (parse_args): Handle it.

> >>         * ld.texi: Add --export-dynamic-symbol documentation.

> >>         * testsuite/ld-undefined/export-dynamic-symbol.exp: Run new test.

> >>         * testsuite/ld-undefined/export-dynamic-symbol-1.d: New.

> >> ---

> >>  ld/ld.texi                                    |  8 +++++

> >>  ld/ldlang.c                                   | 26 ++++++++++++++

> >>  ld/ldlang.h                                   |  2 ++

> >>  ld/ldlex.h                                    |  1 +

> >>  ld/lexsup.c                                   |  7 ++++

> >>  .../ld-undefined/export-dynamic-symbol-1.d    |  8 +++++

> >>  .../ld-undefined/export-dynamic-symbol.exp    | 35 +++++++++++++++++++

> >>  7 files changed, 87 insertions(+)

> >>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

> >>  create mode 100644 ld/testsuite/ld-undefined/export-dynamic-symbol.exp

> >>

> >> diff --git a/ld/ld.texi b/ld/ld.texi

> >> index 4dc78e65fa..b3dc95f314 100644

> >> --- a/ld/ld.texi

> >> +++ b/ld/ld.texi

> >> @@ -569,6 +569,14 @@ Note that this option is specific to ELF targeted ports.  PE 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{symbol}

> >> +@cindex export dynamic symbol

> >> +@item --export-dynamic-symbol=@var{symbol}

> >> +Specify a symbol that should be added to the dynamic symbol table.

> >> +Additionally, force @var{symbol} to be entered in the output file as an

> >> +undefined symbol.  Doing this may, for example, trigger linking of additional

> >> +modules from archives.

> >> +

> >>  @ifclear SingleFormat

> >>  @cindex big-endian objects

> >>  @cindex endianness

> >> diff --git a/ld/ldlang.c b/ld/ldlang.c

> >> index b2cdb3603a..e7ecdfeaf0 100644

> >> --- a/ld/ldlang.c

> >> +++ b/ld/ldlang.c

> >> @@ -3910,6 +3910,26 @@ lang_place_undefineds (void)

> >>      insert_undefined (ptr->name);

> >>  }

> >>

> >> +static struct bfd_sym_chain export_dynamic_symbol_list = { NULL, NULL };

> >> +

> >> +void

> >> +ldlang_add_export_dynamic_symbol (const char *const name)

> >> +{

> >> +  struct bfd_sym_chain *sym;

> >> +  sym = stat_alloc (sizeof (*sym));

> >> +  sym->name = xstrdup (name);

> >> +  sym->next = export_dynamic_symbol_list.next;

> >> +  export_dynamic_symbol_list.next = sym;

> >> +}

> >> +

> >> +static void

> >> +lang_place_export_dynamic_symbols (void)

> >> +{

> >> +  struct bfd_sym_chain *sym;

> >> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

> >> +    insert_undefined (sym->name);

> >> +}

> >> +

> >>  /* Structure used to build the list of symbols that the user has required

> >>     be defined.  */

> >>

> >> @@ -7795,6 +7815,10 @@ void

> >>  lang_process (void)

> >>  {

> >>    /* Finalize dynamic list.  */

> >> +  struct bfd_sym_chain *sym;

> >> +  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)

> >> +    lang_append_dynamic_list (

> >> +        lang_new_vers_pattern (NULL, sym->name, NULL, FALSE));

> >>    if (link_info.dynamic_list)

> >>      lang_finalize_version_expr_head (&link_info.dynamic_list->head);

> >>

> >> @@ -7808,6 +7832,8 @@ lang_process (void)

> >>

> >>    /* Add to the hash table all undefineds on the command line.  */

> >>    lang_place_undefineds ();

> >> +  /* Add --export-dynamic-symbol symbols to the hash table.  */

> >> +  lang_place_export_dynamic_symbols ();

> >>

> >>    if (!bfd_section_already_linked_table_init ())

> >>      einfo (_("%F%P: can not create hash table: %E\n"));

> >> diff --git a/ld/ldlang.h b/ld/ldlang.h

> >> index 2aa3930f95..8c004b173c 100644

> >> --- a/ld/ldlang.h

> >> +++ b/ld/ldlang.h

> >> @@ -606,6 +606,8 @@ extern lang_output_section_statement_type *next_matching_output_section_statemen

> >>    (lang_output_section_statement_type *, int);

> >>  extern void ldlang_add_undef

> >>    (const char *const, bfd_boolean);

> >> +extern void ldlang_add_export_dynamic_symbol

> >> +  (const char *const);

> >>  extern void ldlang_add_require_defined

> >>    (const char *const);

> >>  extern void lang_add_output_format

> >> diff --git a/ld/ldlex.h b/ld/ldlex.h

> >> index 22b928d2d9..70f2da5636 100644

> >> --- a/ld/ldlex.h

> >> +++ b/ld/ldlex.h

> >> @@ -81,6 +81,7 @@ enum option_values

> >>    OPTION_DYNAMIC_LIST_CPP_NEW,

> >>    OPTION_DYNAMIC_LIST_CPP_TYPEINFO,

> >>    OPTION_DYNAMIC_LIST_DATA,

> >> +  OPTION_EXPORT_DYNAMIC_SYMBOL,

> >>    OPTION_WARN_COMMON,

> >>    OPTION_WARN_CONSTRUCTORS,

> >>    OPTION_WARN_FATAL,

> >> diff --git a/ld/lexsup.c b/ld/lexsup.c

> >> index d1955b9afa..0a0c2f2873 100644

> >> --- a/ld/lexsup.c

> >> +++ b/ld/lexsup.c

> >> @@ -504,6 +504,8 @@ static const struct ld_option ld_options[] =

> >>      '\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_("Read dynamic list"), 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},

> >> @@ -1425,6 +1427,11 @@ parse_args (unsigned argc, char **argv)

> >>           if (opt_symbolic == symbolic)

> >>             opt_symbolic = symbolic_unset;

> >>           break;

> >> +       case OPTION_EXPORT_DYNAMIC_SYMBOL:

> >> +         ldlang_add_export_dynamic_symbol (optarg);

> >> +         if (opt_dynamic_list != dynamic_list_data)

> >> +           opt_dynamic_list = dynamic_list;

> >> +         break;

> >>         case OPTION_WARN_COMMON:

> >>           config.warn_common = TRUE;

> >>           break;

> >> diff --git a/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

> >> new file mode 100644

> >> index 0000000000..768bf0abd6

> >> --- /dev/null

> >> +++ b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

> >> @@ -0,0 +1,8 @@

> >> +#name: --export-dynamic-symbol foo archive

> >> +#source: require-defined.s

> >> +#ld: -pie --export-dynamic-symbol foo tmpdir/libentry.a

> >

> >I assume that it supports

> >

> >$ ld -pie --export-dynamic-symbol foo --export-dynamic-symbol bar ...

> >

> >Please add another --export-dynamic-symbol to your test.

> >

> >--

> >H.J.

>

> I face a test difficulty. If I add another `#nm:`, runtest complains:

>

> % cd Debug/ld

> % runtest --tool ld --srcdir ../../ld/testsuite/ export-dynamic-symbol.exp

> ...

> ERROR: option nm multiply set in ../../ld/testsuite/ld-undefined/export-dynamic-symbol-1.d

>

> There may also be a problem of symbol table order determinism. I don't

> know whether another symbol may be ordered before foo in some ports and

> after foo in other ports.

>

> Honestly, I think for a number of tests, even if they test generic

> behavior, it might be fine to restrict them to run on certain hosts,

> e.g. Linux x86. What I know about testing is lacking, though, I don't

> know what can be tested.

>

> Ideally `#nm:` should just be free-form shell scripts but unfortunately

> it seems to support very limited features (undocumented?)


You can't add another #nm.  But you can pass additional options to nm.

-- 
H.J.

Patch

diff --git a/ld/ld.texi b/ld/ld.texi
index 4dc78e65fa..b3dc95f314 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -569,6 +569,14 @@  Note that this option is specific to ELF targeted ports.  PE 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{symbol}
+@cindex export dynamic symbol
+@item --export-dynamic-symbol=@var{symbol}
+Specify a symbol that should be added to the dynamic symbol table.
+Additionally, force @var{symbol} to be entered in the output file as an
+undefined symbol.  Doing this may, for example, trigger linking of additional
+modules from archives.
+
 @ifclear SingleFormat
 @cindex big-endian objects
 @cindex endianness
diff --git a/ld/ldlang.c b/ld/ldlang.c
index b2cdb3603a..e7ecdfeaf0 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -3910,6 +3910,26 @@  lang_place_undefineds (void)
     insert_undefined (ptr->name);
 }
 
+static struct bfd_sym_chain export_dynamic_symbol_list = { NULL, NULL };
+
+void
+ldlang_add_export_dynamic_symbol (const char *const name)
+{
+  struct bfd_sym_chain *sym;
+  sym = stat_alloc (sizeof (*sym));
+  sym->name = xstrdup (name);
+  sym->next = export_dynamic_symbol_list.next;
+  export_dynamic_symbol_list.next = sym;
+}
+
+static void
+lang_place_export_dynamic_symbols (void)
+{
+  struct bfd_sym_chain *sym;
+  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)
+    insert_undefined (sym->name);
+}
+
 /* Structure used to build the list of symbols that the user has required
    be defined.  */
 
@@ -7795,6 +7815,10 @@  void
 lang_process (void)
 {
   /* Finalize dynamic list.  */
+  struct bfd_sym_chain *sym;
+  for (sym = export_dynamic_symbol_list.next; sym != NULL; sym = sym->next)
+    lang_append_dynamic_list (
+        lang_new_vers_pattern (NULL, sym->name, NULL, FALSE));
   if (link_info.dynamic_list)
     lang_finalize_version_expr_head (&link_info.dynamic_list->head);
 
@@ -7808,6 +7832,8 @@  lang_process (void)
 
   /* Add to the hash table all undefineds on the command line.  */
   lang_place_undefineds ();
+  /* Add --export-dynamic-symbol symbols to the hash table.  */
+  lang_place_export_dynamic_symbols ();
 
   if (!bfd_section_already_linked_table_init ())
     einfo (_("%F%P: can not create hash table: %E\n"));
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 2aa3930f95..8c004b173c 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -606,6 +606,8 @@  extern lang_output_section_statement_type *next_matching_output_section_statemen
   (lang_output_section_statement_type *, int);
 extern void ldlang_add_undef
   (const char *const, bfd_boolean);
+extern void ldlang_add_export_dynamic_symbol
+  (const char *const);
 extern void ldlang_add_require_defined
   (const char *const);
 extern void lang_add_output_format
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 22b928d2d9..70f2da5636 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -81,6 +81,7 @@  enum option_values
   OPTION_DYNAMIC_LIST_CPP_NEW,
   OPTION_DYNAMIC_LIST_CPP_TYPEINFO,
   OPTION_DYNAMIC_LIST_DATA,
+  OPTION_EXPORT_DYNAMIC_SYMBOL,
   OPTION_WARN_COMMON,
   OPTION_WARN_CONSTRUCTORS,
   OPTION_WARN_FATAL,
diff --git a/ld/lexsup.c b/ld/lexsup.c
index d1955b9afa..0a0c2f2873 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -504,6 +504,8 @@  static const struct ld_option ld_options[] =
     '\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_("Read dynamic list"), 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},
@@ -1425,6 +1427,11 @@  parse_args (unsigned argc, char **argv)
 	  if (opt_symbolic == symbolic)
 	    opt_symbolic = symbolic_unset;
 	  break;
+	case OPTION_EXPORT_DYNAMIC_SYMBOL:
+	  ldlang_add_export_dynamic_symbol (optarg);
+	  if (opt_dynamic_list != dynamic_list_data)
+	    opt_dynamic_list = dynamic_list;
+	  break;
 	case OPTION_WARN_COMMON:
 	  config.warn_common = TRUE;
 	  break;
diff --git a/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d
new file mode 100644
index 0000000000..768bf0abd6
--- /dev/null
+++ b/ld/testsuite/ld-undefined/export-dynamic-symbol-1.d
@@ -0,0 +1,8 @@ 
+#name: --export-dynamic-symbol foo archive
+#source: require-defined.s
+#ld: -pie --export-dynamic-symbol foo tmpdir/libentry.a
+#nm: -D
+
+#...
+[0-9a-f]+ T +foo
+#...
diff --git a/ld/testsuite/ld-undefined/export-dynamic-symbol.exp b/ld/testsuite/ld-undefined/export-dynamic-symbol.exp
new file mode 100644
index 0000000000..247ccf2660
--- /dev/null
+++ b/ld/testsuite/ld-undefined/export-dynamic-symbol.exp
@@ -0,0 +1,35 @@ 
+# 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.
+#
+
+set build_tests {
+  {"Build libentry.a"
+   "" "" ""
+   {entry.s} {} "libentry.a"}
+}
+
+run_ld_link_tests $build_tests
+
+set test_list [lsort [glob -nocomplain $srcdir/$subdir/export-dynamic-symbol-*.d]]
+foreach t $test_list {
+    # We need to strip the ".d", but can leave the dirname.
+    verbose [file rootname $t]
+    run_dump_test [file rootname $t]
+}