This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
stateful handling of some ld flags
- From: Ulrich Drepper <drepper at gmail dot com>
- To: binutils at sourceware dot org
- Date: Sat, 28 Jun 2014 15:48:55 -0400
- Subject: stateful handling of some ld flags
- Authentication-results: sourceware.org; auth=none
This patch is a possible solution for a problem that bugged me for a
very long time. If you provide rules to link with a set of libraries
(for instance, when using pkg-config) one cannot use --as-needed,
--copy-dt-needed-entries, --whole-archive (and the respective inverse
versions) because it is not known what the current state is. One cannot
switch back.
Other programs, for instance, gcc have similar problems. A solution is
to use push/pop pairs.
This patch implements this for this three sets of command line options.
No need to do this for the linker script equivalent, it already does the
right thing.
I don't think it is possible to have a general push state/pop state
pair, there are simply too many settings which can or cannot be assumed
to be affected.
What do you think?
2014-06-28 Ulrich Drepper <drepper@gmail.com>
* ldang.h (struct lang_input_statements_flags): Change type of
add_DT_NEEDED_for_dynamic, add_DT_NEEDED_for_regular, and whole_archive
to use unsigned.
* ldlex.h (enum option_values): Add OPTION_NO_WHOLE_ARCHIVE_PUSH,
OPTION_WHOLE_ARCHIVE_PUSH, OPTION_WHOLE_ARCHIVE_POP,
OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH,
OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH,
OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_POP,
OPTION_ADD_DT_NEEDED_FOR_REGULAR_PUSH,
OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR_PUSH, and
OPTION_ADD_DT_NEEDED_POP.
* lexsup.c (ld_optins): Add --as-needed-push, --no-as-needed-push,
-- as-needed-pop, --copy-dt-needed-entries-push,
--no-copy-dt-needed-entries-push, --copy-dt-needed-entries-pop,
--no-whole-archive-push, --whole-archive-push, --whole-archive-pop.
(parse_args): Handle the new options.
* ldlang.c (load_symbols): Use only least bit of
entry->flags.whole_archive.
(open_input_bfds): Likewise of s->input_statement.flags.whole_archive
and s->input_statement.flags.add_DT_NEEDED_for_regular.
* ldgram.y: Use 1 instead of TRUE in AS_NEEDED rules.
* emultempl/elf32.em (gld${EMULATION_NAME}_load_symbols): Use only
least bit of entry->flags.add_DT_NEEDED_for_regular and
entry->flags.add_DT_NEEDED_for_dynamic.
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index 39b4ccd..da949e7 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -119,13 +119,13 @@ gld${EMULATION_NAME}_load_symbols (lang_input_statement_type *entry)
/* Tell the ELF linker that we don't want the output file to have a
DT_NEEDED entry for this file, unless it is used to resolve
references in a regular object. */
- if (entry->flags.add_DT_NEEDED_for_regular)
+ if (entry->flags.add_DT_NEEDED_for_regular & 1)
link_class = DYN_AS_NEEDED;
/* Tell the ELF linker that we don't want the output file to have a
DT_NEEDED entry for any dynamic library in DT_NEEDED tags from
this file at all. */
- if (!entry->flags.add_DT_NEEDED_for_dynamic)
+ if (!(entry->flags.add_DT_NEEDED_for_dynamic & 1))
link_class |= DYN_NO_ADD_NEEDED;
if (entry->flags.just_syms
diff --git a/ld/ldgram.y b/ld/ldgram.y
index 4875fa7..ff1e561 100644
--- a/ld/ldgram.y
+++ b/ld/ldgram.y
@@ -385,17 +385,17 @@ input_list:
(char *)NULL); }
| AS_NEEDED '('
{ $<integer>$ = input_flags.add_DT_NEEDED_for_regular;
- input_flags.add_DT_NEEDED_for_regular = TRUE; }
+ input_flags.add_DT_NEEDED_for_regular = 1; }
input_list ')'
{ input_flags.add_DT_NEEDED_for_regular = $<integer>3; }
| input_list ',' AS_NEEDED '('
{ $<integer>$ = input_flags.add_DT_NEEDED_for_regular;
- input_flags.add_DT_NEEDED_for_regular = TRUE; }
+ input_flags.add_DT_NEEDED_for_regular = 1; }
input_list ')'
{ input_flags.add_DT_NEEDED_for_regular = $<integer>5; }
| input_list AS_NEEDED '('
{ $<integer>$ = input_flags.add_DT_NEEDED_for_regular;
- input_flags.add_DT_NEEDED_for_regular = TRUE; }
+ input_flags.add_DT_NEEDED_for_regular = 1; }
input_list ')'
{ input_flags.add_DT_NEEDED_for_regular = $<integer>4; }
;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index e7c9669..69be3c1 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -2801,7 +2801,7 @@ load_symbols (lang_input_statement_type *entry,
case bfd_archive:
check_excluded_libs (entry->the_bfd);
- if (entry->flags.whole_archive)
+ if (entry->flags.whole_archive & 1)
{
bfd *member = NULL;
bfd_boolean loaded = TRUE;
@@ -3280,7 +3280,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
&& ((mode & OPEN_BFD_RESCAN) == 0
|| plugin_insert == NULL)
#endif
- && !s->input_statement.flags.whole_archive
+ && !(s->input_statement.flags.whole_archive & 1)
&& s->input_statement.flags.loaded
&& s->input_statement.the_bfd != NULL
&& bfd_check_format (s->input_statement.the_bfd,
@@ -3291,7 +3291,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
else if ((mode & OPEN_BFD_RESCAN) != 0
&& plugin_insert == NULL
&& s->input_statement.flags.loaded
- && s->input_statement.flags.add_DT_NEEDED_for_regular
+ && (s->input_statement.flags.add_DT_NEEDED_for_regular & 1)
&& s->input_statement.the_bfd != NULL
&& ((s->input_statement.the_bfd->flags) & DYNAMIC) != 0
&& plugin_should_reload (s->input_statement.the_bfd))
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 7d69c56..c031830 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -252,18 +252,6 @@ struct lang_input_statement_flags
/* Whether to search for this entry as a dynamic archive. */
unsigned int dynamic : 1;
- /* Set if a DT_NEEDED tag should be added not just for the dynamic library
- explicitly given by this entry but also for any dynamic libraries in
- this entry's needed list. */
- unsigned int add_DT_NEEDED_for_dynamic : 1;
-
- /* Set if this entry should cause a DT_NEEDED tag only when some
- regular file references its symbols (ie. --as-needed is in effect). */
- unsigned int add_DT_NEEDED_for_regular : 1;
-
- /* Whether to include the entire contents of an archive. */
- unsigned int whole_archive : 1;
-
/* Set when bfd opening is successful. */
unsigned int loaded : 1;
@@ -282,6 +270,27 @@ struct lang_input_statement_flags
/* Set if reloading an --as-needed lib. */
unsigned int reload : 1;
#endif /* ENABLE_PLUGINS */
+
+ /* The lowest bit is set if a DT_NEEDED tag should be added not just for
+ the dynamic library explicitly given by this entry but also for any
+ dynamic libraries in this entry's needed list. The higher bits are
+ previously set values which can be restored with an appropriate pop
+ operation. The even bits is the value, the odd bits indicate whether
+ the pair of bits is actually valid. */
+ unsigned int add_DT_NEEDED_for_dynamic;
+
+ /* The lowest bit is set if this entry should cause a DT_NEEDED tag only
+ when some regular file references its symbols (ie. --as-needed is in
+ effect). The higher bits are previously set values which can be
+ restored with an appropriate pop operation. The even bits is the value,
+ the odd bits indicate whether the pair of bits is actually valid. */
+ unsigned int add_DT_NEEDED_for_regular;
+
+ /* The lowest bit indicated whether to include the entire contents of
+ an archive. The higher bits are previously set values which can be
+ restored with an appropriate pop operation. The even bits is the value,
+ the odd bits indicate whether the pair of bits is actually valid. */
+ unsigned int whole_archive;
};
typedef struct lang_input_statement_struct
diff --git a/ld/ldlex.h b/ld/ldlex.h
index b2753c3..8b50184 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -49,6 +49,7 @@ enum option_values
OPTION_NOINHIBIT_EXEC,
OPTION_NON_SHARED,
OPTION_NO_WHOLE_ARCHIVE,
+ OPTION_NO_WHOLE_ARCHIVE_PUSH,
OPTION_OFORMAT,
OPTION_RELAX,
OPTION_NO_RELAX,
@@ -89,10 +90,18 @@ enum option_values
OPTION_SPLIT_BY_RELOC,
OPTION_SPLIT_BY_FILE ,
OPTION_WHOLE_ARCHIVE,
+ OPTION_WHOLE_ARCHIVE_PUSH,
+ OPTION_WHOLE_ARCHIVE_POP,
OPTION_ADD_DT_NEEDED_FOR_DYNAMIC,
OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC,
+ OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH,
+ OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH,
+ OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_POP,
OPTION_ADD_DT_NEEDED_FOR_REGULAR,
OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR,
+ OPTION_ADD_DT_NEEDED_FOR_REGULAR_PUSH,
+ OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR_PUSH,
+ OPTION_ADD_DT_NEEDED_POP,
OPTION_WRAP,
OPTION_FORCE_EXE_SUFFIX,
OPTION_GC_SECTIONS,
diff --git a/ld/lexsup.c b/ld/lexsup.c
index a8c57d2..2f54231 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -255,6 +255,18 @@ static const struct ld_option ld_options[] =
'\0', NULL, N_("Always set DT_NEEDED for dynamic libraries mentioned on\n"
" the command line"),
TWO_DASHES },
+ { {"as-needed-push", no_argument, NULL, OPTION_ADD_DT_NEEDED_FOR_REGULAR_PUSH},
+ '\0', NULL, N_("Only set DT_NEEDED for following dynamic libs if used,\n"
+ " remember previous setting"),
+ TWO_DASHES },
+ { {"no-as-needed-push", no_argument, NULL, OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR_PUSH},
+ '\0', NULL, N_("Always set DT_NEEDED for dynamic libraries mentioned on\n"
+ " the command line, remember\n"
+ " previous setting"),
+ TWO_DASHES },
+ { {"as-needed-pop", no_argument, NULL, OPTION_ADD_DT_NEEDED_POP},
+ '\0', NULL, N_("Restore previous setting of DT_NEEDED for dynamic libs"),
+ TWO_DASHES },
{ {"assert", required_argument, NULL, OPTION_ASSERT},
'\0', N_("KEYWORD"), N_("Ignored for SunOS compatibility"), ONE_DASH },
{ {"Bdynamic", no_argument, NULL, OPTION_CALL_SHARED},
@@ -289,6 +301,21 @@ static const struct ld_option ld_options[] =
OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC},
'\0', NULL, N_("Do not copy DT_NEEDED links mentioned inside DSOs that follow"),
TWO_DASHES },
+ { {"copy-dt-needed-entries-push", no_argument, NULL,
+ OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH},
+ '\0', NULL, N_("Copy DT_NEEDED links mentioned inside DSOs that follow,\n"
+ " remember previous setting"),
+ TWO_DASHES },
+ { {"no-copy-dt-needed-entries-push", no_argument, NULL,
+ OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH},
+ '\0', NULL, N_("Do not copy DT_NEEDED links mentioned inside DSOs that follow,\n"
+ " remember previous setting"),
+ TWO_DASHES },
+ { {"copy-dt-needed-entries-pop", no_argument, NULL,
+ OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_POP},
+ '\0', NULL, N_("Restore previous setting for copy DT_NEEDED links mentioned\n"
+ " inside DSOs that follow"),
+ TWO_DASHES },
{ {"cref", no_argument, NULL, OPTION_CREF},
'\0', NULL, N_("Output cross reference table"), TWO_DASHES },
@@ -365,6 +392,9 @@ static const struct ld_option ld_options[] =
TWO_DASHES},
{ {"no-whole-archive", no_argument, NULL, OPTION_NO_WHOLE_ARCHIVE},
'\0', NULL, N_("Turn off --whole-archive"), TWO_DASHES },
+ { {"no-whole-archive-push", no_argument, NULL, OPTION_NO_WHOLE_ARCHIVE_PUSH},
+ '\0', NULL, N_("Turn off --whole-archive, preserve previous setting"),
+ TWO_DASHES },
{ {"noinhibit-exec", no_argument, NULL, OPTION_NOINHIBIT_EXEC},
'\0', NULL, N_("Create an output file even if errors occur"),
TWO_DASHES },
@@ -499,6 +529,14 @@ static const struct ld_option ld_options[] =
{ {"whole-archive", no_argument, NULL, OPTION_WHOLE_ARCHIVE},
'\0', NULL, N_("Include all objects from following archives"),
TWO_DASHES },
+ { {"whole-archive-push", no_argument, NULL, OPTION_WHOLE_ARCHIVE_PUSH},
+ '\0', NULL, N_("Include all objects from following archives,\n"
+ " preserve previous setting"),
+ TWO_DASHES },
+ { {"whole-archive-pop", no_argument, NULL, OPTION_WHOLE_ARCHIVE_POP},
+ '\0', NULL, N_("Restore previous setting for include all objects from\n"
+ " following archives"),
+ TWO_DASHES },
{ {"wrap", required_argument, NULL, OPTION_WRAP},
'\0', N_("SYMBOL"), N_("Use wrapper functions for SYMBOL"), TWO_DASHES },
{ {"ignore-unresolved-symbol", required_argument, NULL,
@@ -936,7 +974,14 @@ parse_args (unsigned argc, char **argv)
config.only_cmd_line_lib_dirs = TRUE;
break;
case OPTION_NO_WHOLE_ARCHIVE:
- input_flags.whole_archive = FALSE;
+ input_flags.whole_archive &= ~1u;
+ break;
+ case OPTION_NO_WHOLE_ARCHIVE_PUSH:
+ if (input_flags.whole_archive
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of whole archive setting too deep\n"));
+ input_flags.whole_archive
+ = (input_flags.whole_archive << 2) | 8u;
break;
case 'O':
/* FIXME "-O<non-digits> <value>" used to set the address of
@@ -1337,19 +1382,69 @@ parse_args (unsigned argc, char **argv)
link_info.warn_alternate_em = TRUE;
break;
case OPTION_WHOLE_ARCHIVE:
- input_flags.whole_archive = TRUE;
+ input_flags.whole_archive |= 1u;
+ break;
+ case OPTION_WHOLE_ARCHIVE_PUSH:
+ if (input_flags.whole_archive
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of whole archive setting too deep\n"));
+ input_flags.whole_archive
+ = (input_flags.whole_archive << 2) | 9u;
+ break;
+ case OPTION_WHOLE_ARCHIVE_POP:
+ if ((input_flags.whole_archive & 8u) == 0)
+ einfo (_("%P%F: pop of whole archive setting without push\n"));
+ input_flags.whole_archive >>= 2;
break;
case OPTION_ADD_DT_NEEDED_FOR_DYNAMIC:
- input_flags.add_DT_NEEDED_for_dynamic = TRUE;
+ input_flags.add_DT_NEEDED_for_dynamic |= 1u;
break;
case OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC:
- input_flags.add_DT_NEEDED_for_dynamic = FALSE;
+ input_flags.add_DT_NEEDED_for_dynamic &= ~1u;
+ break;
+ case OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH:
+ if (input_flags.add_DT_NEEDED_for_dynamic
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of DT_NEEDED for links inside mentioned libs settings too deep\n"));
+ input_flags.add_DT_NEEDED_for_dynamic
+ = (input_flags.add_DT_NEEDED_for_dynamic << 2) | 9u;
+ break;
+ case OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC_PUSH:
+ if (input_flags.add_DT_NEEDED_for_dynamic
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of DT_NEEDED for links inside mentioned libs settings too deep\n"));
+ input_flags.add_DT_NEEDED_for_dynamic
+ = (input_flags.add_DT_NEEDED_for_dynamic << 2) | 8u;
+ break;
+ case OPTION_ADD_DT_NEEDED_FOR_DYNAMIC_POP:
+ if ((input_flags.add_DT_NEEDED_for_dynamic & 8u) == 0)
+ einfo (_("%P%F: pop of DT_NEEDED for links inside mentioned libs settings without push\n"));
+ input_flags.add_DT_NEEDED_for_dynamic >>= 2;
break;
case OPTION_ADD_DT_NEEDED_FOR_REGULAR:
- input_flags.add_DT_NEEDED_for_regular = TRUE;
+ input_flags.add_DT_NEEDED_for_regular |= 1u;
break;
case OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR:
- input_flags.add_DT_NEEDED_for_regular = FALSE;
+ input_flags.add_DT_NEEDED_for_regular &= ~1u;
+ break;
+ case OPTION_ADD_DT_NEEDED_FOR_REGULAR_PUSH:
+ if (input_flags.add_DT_NEEDED_for_regular
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of DT_NEEDED for mentioned libs settings too deep\n"));
+ input_flags.add_DT_NEEDED_for_regular
+ = (input_flags.add_DT_NEEDED_for_regular << 2) | 9u;
+ break;
+ case OPTION_NO_ADD_DT_NEEDED_FOR_REGULAR_PUSH:
+ if (input_flags.add_DT_NEEDED_for_regular
+ & (1u << ((8 * sizeof (unsigned int)) - 1)))
+ einfo (_("%P%F: nesting of DT_NEEDED for mentioned libs settings too deep\n"));
+ input_flags.add_DT_NEEDED_for_regular
+ = (input_flags.add_DT_NEEDED_for_regular << 2) | 8u;
+ break;
+ case OPTION_ADD_DT_NEEDED_POP:
+ if ((input_flags.add_DT_NEEDED_for_regular & 8u) == 0)
+ einfo (_("%P%F: pop of DT_NEEDED for mentioned libs settings without push\n"));
+ input_flags.add_DT_NEEDED_for_regular >>= 2;
break;
case OPTION_WRAP:
add_wrap (optarg);