From 320fdefef139160c93089a19ba41a2fcc4121203 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 2 Feb 2022 14:40:03 -0800 Subject: ld: Add a before_plugin_all_symbols_read hook Add a before_plugin_all_symbols_read hook to load symbol references from DT_NEEDED entries, included from --copy-dt-needed-entries, before reading plugin symbols to properly resolve plugin symbol references. bfd/ PR ld/28849 * elf-bfd.h (elf_link_hash_table): Add handling_dt_needed. * elflink.c (_bfd_elf_merge_symbol): Don't set non_ir_ref_dynamic before plugin 'all symbols read' hook is called. ld/ PR ld/28849 * ldelf.c (ldelf_handle_dt_needed): New function. (ldelf_before_plugin_all_symbols_read): Likewise. (ldelf_after_open): Call ldelf_handle_dt_needed. * ldelf.h (ldelf_before_plugin_all_symbols_read): New. * ldemul.c (ldemul_before_plugin_all_symbols_read): Likewise. * ldemul.h (ldemul_before_plugin_all_symbols_read): Likewise. (ld_emulation_xfer_struct): Add before_plugin_all_symbols_read. * ldlang.c (lang_process): Call ldemul_before_plugin_all_symbols_read before calling plugin_call_all_symbols_read. * emultempl/elf.em (gld${EMULATION_NAME}_before_plugin_all_symbols_read): New. (LDEMUL_BEFORE_PLUGIN_ALL_SYMBOLS_READ): New. * emultempl/emulation.em (ld_${EMULATION_NAME}_emulation): Initialize the before_plugin_all_symbols_read field. * testsuite/ld-plugin/lto.exp: Run PR ld/28849 tests. * testsuite/ld-plugin/pr28849.d: New file. * testsuite/ld-plugin/pr28849a.c: Likewise. * testsuite/ld-plugin/pr28849b.c: Likewise. --- bfd/elf-bfd.h | 3 + bfd/elflink.c | 7 +- ld/emultempl/elf.em | 14 ++ ld/emultempl/emulation.em | 1 + ld/ldelf.c | 385 +++++++++++++++++++++----------------- ld/ldelf.h | 2 + ld/ldemul.c | 7 + ld/ldemul.h | 5 + ld/ldlang.c | 2 + ld/testsuite/ld-plugin/lto.exp | 25 +++ ld/testsuite/ld-plugin/pr28849.d | 3 + ld/testsuite/ld-plugin/pr28849a.c | 2 + ld/testsuite/ld-plugin/pr28849b.c | 3 + 13 files changed, 283 insertions(+), 176 deletions(-) create mode 100644 ld/testsuite/ld-plugin/pr28849.d create mode 100644 ld/testsuite/ld-plugin/pr28849a.c create mode 100644 ld/testsuite/ld-plugin/pr28849b.c diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 889a474..4904e1e 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -599,6 +599,9 @@ struct elf_link_hash_table /* TRUE if DT_JMPREL is a required dynamic tag. */ bool dt_jmprel_required; + /* TRUE when we are handling DT_NEEDED entries. */ + bool handling_dt_needed; + /* The BFD used to hold special sections created by the linker. This will be the first BFD found which requires these sections to be created. */ diff --git a/bfd/elflink.c b/bfd/elflink.c index f852142..5a070a2 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -1090,6 +1090,7 @@ _bfd_elf_merge_symbol (bfd *abfd, const struct elf_backend_data *bed; char *new_version; bool default_sym = *matched; + struct elf_link_hash_table *htab; *skip = false; *override = NULL; @@ -1220,6 +1221,8 @@ _bfd_elf_merge_symbol (bfd *abfd, symbols. */ bfd_elf_link_mark_dynamic_symbol (info, h, sym); + htab = elf_hash_table (info); + /* NEWDYN and OLDDYN indicate whether the new or old symbol, respectively, is from a dynamic object. */ @@ -1283,7 +1286,9 @@ _bfd_elf_merge_symbol (bfd *abfd, olddyn = (oldsec->symbol->flags & BSF_DYNAMIC) != 0; } - if (oldbfd != NULL + /* Set non_ir_ref_dynamic only when not handling DT_NEEDED entries. */ + if (!htab->handling_dt_needed + && oldbfd != NULL && (oldbfd->flags & BFD_PLUGIN) != (abfd->flags & BFD_PLUGIN)) { if (newdyn != olddyn) diff --git a/ld/emultempl/elf.em b/ld/emultempl/elf.em index 55dd20f..7ae6f6d 100644 --- a/ld/emultempl/elf.em +++ b/ld/emultempl/elf.em @@ -53,6 +53,8 @@ fragment <root)) - return; - - if (command_line.out_implib_filename) - { - unlink_if_ordinary (command_line.out_implib_filename); - link_info.out_implib_bfd - = bfd_openw (command_line.out_implib_filename, - bfd_get_target (link_info.output_bfd)); - - if (link_info.out_implib_bfd == NULL) - { - einfo (_("%F%P: %s: can't open for writing: %E\n"), - command_line.out_implib_filename); - } - } - - if (ldelf_emit_note_gnu_build_id != NULL) - { - /* Find an ELF input. */ - for (abfd = link_info.input_bfds; - abfd != (bfd *) NULL; abfd = abfd->link.next) - if (bfd_get_flavour (abfd) == bfd_target_elf_flavour - && bfd_count_sections (abfd) != 0 - && !bfd_input_just_syms (abfd)) - break; - - /* PR 10555: If there are no ELF input files do not try to - create a .note.gnu-build-id section. */ - if (abfd == NULL - || !ldelf_setup_build_id (abfd)) - { - free ((char *) ldelf_emit_note_gnu_build_id); - ldelf_emit_note_gnu_build_id = NULL; - } - } - - get_elf_backend_data (link_info.output_bfd)->setup_gnu_properties (&link_info); - - /* Do not allow executable files to be used as inputs to the link. */ - for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) - { - /* Discard input .note.gnu.build-id sections. */ - s = bfd_get_section_by_name (abfd, ".note.gnu.build-id"); - while (s != NULL) - { - if (s != elf_tdata (link_info.output_bfd)->o->build_id.sec) - s->flags |= SEC_EXCLUDE; - s = bfd_get_next_section_by_name (NULL, s); - } - - if (abfd->xvec->flavour == bfd_target_elf_flavour - && !bfd_input_just_syms (abfd) - && elf_tdata (abfd) != NULL - /* FIXME: Maybe check for other non-supportable types as well ? */ - && (elf_tdata (abfd)->elf_header->e_type == ET_EXEC - || (elf_tdata (abfd)->elf_header->e_type == ET_DYN - && elf_tdata (abfd)->is_pie))) - einfo (_("%F%P: cannot use executable file '%pB' as input to a link\n"), - abfd); - } - - if (bfd_link_relocatable (&link_info)) - { - if (link_info.execstack == !link_info.noexecstack) - { - /* PR ld/16744: If "-z [no]execstack" has been specified on the - command line and we are perfoming a relocatable link then no - PT_GNU_STACK segment will be created and so the - linkinfo.[no]execstack values set in _handle_option() will have no - effect. Instead we create a .note.GNU-stack section in much the - same way as the assembler does with its --[no]execstack option. */ - flagword flags = SEC_READONLY | (link_info.execstack ? SEC_CODE : 0); - (void) bfd_make_section_with_flags (link_info.input_bfds, - ".note.GNU-stack", flags); - } - return; - } - - if (!link_info.traditional_format) - { - bfd *elfbfd = NULL; - bool warn_eh_frame = false; - int seen_type = 0; - - for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) - { - int type = 0; - - if (bfd_input_just_syms (abfd)) - continue; - - for (s = abfd->sections; s && type < COMPACT_EH_HDR; s = s->next) - { - const char *name = bfd_section_name (s); - - if (bfd_is_abs_section (s->output_section)) - continue; - if (startswith (name, ".eh_frame_entry")) - type = COMPACT_EH_HDR; - else if (strcmp (name, ".eh_frame") == 0 && s->size > 8) - type = DWARF2_EH_HDR; - } - - if (type != 0) - { - if (seen_type == 0) - { - seen_type = type; - } - else if (seen_type != type) - { - einfo (_("%F%P: compact frame descriptions incompatible with" - " DWARF2 .eh_frame from %pB\n"), - type == DWARF2_EH_HDR ? abfd : elfbfd); - break; - } - - if (!elfbfd - && (type == COMPACT_EH_HDR - || link_info.eh_frame_hdr_type != 0)) - { - if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) - elfbfd = abfd; - - warn_eh_frame = true; - } - } - - if (seen_type == COMPACT_EH_HDR) - link_info.eh_frame_hdr_type = COMPACT_EH_HDR; - } - if (elfbfd) - { - const struct elf_backend_data *bed; - - bed = get_elf_backend_data (elfbfd); - s = bfd_make_section_with_flags (elfbfd, ".eh_frame_hdr", - bed->dynamic_sec_flags - | SEC_READONLY); - if (s != NULL - && bfd_set_section_alignment (s, 2)) - { - htab->eh_info.hdr_sec = s; - warn_eh_frame = false; - } - } - if (warn_eh_frame) - einfo (_("%P: warning: cannot create .eh_frame_hdr section," - " --eh-frame-hdr ignored\n")); - } - /* Get the list of files which appear in DT_NEEDED entries in dynamic objects included in the link (often there will be none). For each such file, we want to track down the corresponding @@ -1195,10 +1037,12 @@ ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd, && (bfd_elf_get_dyn_lib_class (l->by) & DYN_AS_NEEDED) != 0) continue; - /* Skip the lib if --no-copy-dt-needed-entries and - --allow-shlib-undefined is in effect. */ + /* Skip the lib if --no-copy-dt-needed-entries and when we are + handling DT_NEEDED entries or --allow-shlib-undefined is in + effect. */ if (l->by != NULL - && link_info.unresolved_syms_in_shared_libs == RM_IGNORE + && (htab->handling_dt_needed + || link_info.unresolved_syms_in_shared_libs == RM_IGNORE) && (bfd_elf_get_dyn_lib_class (l->by) & DYN_NO_ADD_NEEDED) != 0) continue; @@ -1338,23 +1182,214 @@ ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd, l->name, l->by); } - for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) - if (bfd_get_format (abfd) == bfd_object - && ((abfd->flags) & DYNAMIC) != 0 - && bfd_get_flavour (abfd) == bfd_target_elf_flavour - && (elf_dyn_lib_class (abfd) & (DYN_AS_NEEDED | DYN_NO_NEEDED)) == 0 - && elf_dt_name (abfd) != NULL) - { - if (bfd_elf_add_dt_needed_tag (abfd, &link_info) < 0) - einfo (_("%F%P: failed to add DT_NEEDED dynamic tag\n")); - } + /* Don't add DT_NEEDED when loading shared objects from DT_NEEDED for + plugin symbol resolution while handling DT_NEEDED entries. */ + if (!htab->handling_dt_needed) + for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) + if (bfd_get_format (abfd) == bfd_object + && ((abfd->flags) & DYNAMIC) != 0 + && bfd_get_flavour (abfd) == bfd_target_elf_flavour + && (elf_dyn_lib_class (abfd) & (DYN_AS_NEEDED | DYN_NO_NEEDED)) == 0 + && elf_dt_name (abfd) != NULL) + { + if (bfd_elf_add_dt_needed_tag (abfd, &link_info) < 0) + einfo (_("%F%P: failed to add DT_NEEDED dynamic tag\n")); + } link_info.input_bfds_tail = save_input_bfd_tail; *save_input_bfd_tail = NULL; +} + +/* This is called before calling plugin 'all symbols read' hook. */ + +void +ldelf_before_plugin_all_symbols_read (int use_libpath, int native, + int is_linux, int is_freebsd, + int elfsize, const char *prefix) +{ + struct elf_link_hash_table *htab = elf_hash_table (&link_info); + + if (!is_elf_hash_table (&htab->root)) + return; + + htab->handling_dt_needed = true; + ldelf_handle_dt_needed (htab, use_libpath, native, is_linux, + is_freebsd, elfsize, prefix); + htab->handling_dt_needed = false; +} + +/* This is called after all the input files have been opened and all + symbols have been loaded. */ + +void +ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd, + int elfsize, const char *prefix) +{ + struct elf_link_hash_table *htab; + asection *s; + bfd *abfd; + + after_open_default (); + + htab = elf_hash_table (&link_info); + if (!is_elf_hash_table (&htab->root)) + return; + + if (command_line.out_implib_filename) + { + unlink_if_ordinary (command_line.out_implib_filename); + link_info.out_implib_bfd + = bfd_openw (command_line.out_implib_filename, + bfd_get_target (link_info.output_bfd)); + + if (link_info.out_implib_bfd == NULL) + { + einfo (_("%F%P: %s: can't open for writing: %E\n"), + command_line.out_implib_filename); + } + } + + if (ldelf_emit_note_gnu_build_id != NULL) + { + /* Find an ELF input. */ + for (abfd = link_info.input_bfds; + abfd != (bfd *) NULL; abfd = abfd->link.next) + if (bfd_get_flavour (abfd) == bfd_target_elf_flavour + && bfd_count_sections (abfd) != 0 + && !bfd_input_just_syms (abfd)) + break; + + /* PR 10555: If there are no ELF input files do not try to + create a .note.gnu-build-id section. */ + if (abfd == NULL + || !ldelf_setup_build_id (abfd)) + { + free ((char *) ldelf_emit_note_gnu_build_id); + ldelf_emit_note_gnu_build_id = NULL; + } + } + + get_elf_backend_data (link_info.output_bfd)->setup_gnu_properties (&link_info); + + /* Do not allow executable files to be used as inputs to the link. */ + for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) + { + /* Discard input .note.gnu.build-id sections. */ + s = bfd_get_section_by_name (abfd, ".note.gnu.build-id"); + while (s != NULL) + { + if (s != elf_tdata (link_info.output_bfd)->o->build_id.sec) + s->flags |= SEC_EXCLUDE; + s = bfd_get_next_section_by_name (NULL, s); + } + + if (abfd->xvec->flavour == bfd_target_elf_flavour + && !bfd_input_just_syms (abfd) + && elf_tdata (abfd) != NULL + /* FIXME: Maybe check for other non-supportable types as well ? */ + && (elf_tdata (abfd)->elf_header->e_type == ET_EXEC + || (elf_tdata (abfd)->elf_header->e_type == ET_DYN + && elf_tdata (abfd)->is_pie))) + einfo (_("%F%P: cannot use executable file '%pB' as input to a link\n"), + abfd); + } + + if (bfd_link_relocatable (&link_info)) + { + if (link_info.execstack == !link_info.noexecstack) + { + /* PR ld/16744: If "-z [no]execstack" has been specified on the + command line and we are perfoming a relocatable link then no + PT_GNU_STACK segment will be created and so the + linkinfo.[no]execstack values set in _handle_option() will have no + effect. Instead we create a .note.GNU-stack section in much the + same way as the assembler does with its --[no]execstack option. */ + flagword flags = SEC_READONLY | (link_info.execstack ? SEC_CODE : 0); + (void) bfd_make_section_with_flags (link_info.input_bfds, + ".note.GNU-stack", flags); + } + return; + } + + if (!link_info.traditional_format) + { + bfd *elfbfd = NULL; + bool warn_eh_frame = false; + int seen_type = 0; + + for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next) + { + int type = 0; + + if (bfd_input_just_syms (abfd)) + continue; + + for (s = abfd->sections; s && type < COMPACT_EH_HDR; s = s->next) + { + const char *name = bfd_section_name (s); + + if (bfd_is_abs_section (s->output_section)) + continue; + if (startswith (name, ".eh_frame_entry")) + type = COMPACT_EH_HDR; + else if (strcmp (name, ".eh_frame") == 0 && s->size > 8) + type = DWARF2_EH_HDR; + } + + if (type != 0) + { + if (seen_type == 0) + { + seen_type = type; + } + else if (seen_type != type) + { + einfo (_("%F%P: compact frame descriptions incompatible with" + " DWARF2 .eh_frame from %pB\n"), + type == DWARF2_EH_HDR ? abfd : elfbfd); + break; + } + + if (!elfbfd + && (type == COMPACT_EH_HDR + || link_info.eh_frame_hdr_type != 0)) + { + if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) + elfbfd = abfd; + + warn_eh_frame = true; + } + } + + if (seen_type == COMPACT_EH_HDR) + link_info.eh_frame_hdr_type = COMPACT_EH_HDR; + } + if (elfbfd) + { + const struct elf_backend_data *bed; + + bed = get_elf_backend_data (elfbfd); + s = bfd_make_section_with_flags (elfbfd, ".eh_frame_hdr", + bed->dynamic_sec_flags + | SEC_READONLY); + if (s != NULL + && bfd_set_section_alignment (s, 2)) + { + htab->eh_info.hdr_sec = s; + warn_eh_frame = false; + } + } + if (warn_eh_frame) + einfo (_("%P: warning: cannot create .eh_frame_hdr section," + " --eh-frame-hdr ignored\n")); + } if (link_info.eh_frame_hdr_type == COMPACT_EH_HDR) if (!bfd_elf_parse_eh_frame_entries (NULL, &link_info)) einfo (_("%F%P: failed to parse EH frame entries\n")); + + ldelf_handle_dt_needed (htab, use_libpath, native, is_linux, + is_freebsd, elfsize, prefix); } static bfd_size_type diff --git a/ld/ldelf.h b/ld/ldelf.h index 0da26c9..efa8b45 100644 --- a/ld/ldelf.h +++ b/ld/ldelf.h @@ -22,6 +22,8 @@ extern const char *ldelf_emit_note_gnu_build_id; extern void ldelf_after_parse (void); extern bool ldelf_load_symbols (lang_input_statement_type *); +extern void ldelf_before_plugin_all_symbols_read (int, int, int, int, + int, const char *); extern void ldelf_after_open (int, int, int, int, int, const char *); extern bool ldelf_setup_build_id (bfd *); extern void ldelf_append_to_separated_string (char **, char *); diff --git a/ld/ldemul.c b/ld/ldemul.c index 85c00de..4c8cad8 100644 --- a/ld/ldemul.c +++ b/ld/ldemul.c @@ -60,6 +60,13 @@ ldemul_before_parse (void) } void +ldemul_before_plugin_all_symbols_read (void) +{ + if (ld_emulation->before_plugin_all_symbols_read) + ld_emulation->before_plugin_all_symbols_read (); +} + +void ldemul_after_open (void) { ld_emulation->after_open (); diff --git a/ld/ldemul.h b/ld/ldemul.h index 33e690d..16a3b69 100644 --- a/ld/ldemul.h +++ b/ld/ldemul.h @@ -32,6 +32,8 @@ extern void ldemul_after_parse (void); extern void ldemul_before_parse (void); +extern void ldemul_before_plugin_all_symbols_read + (void); extern void ldemul_after_open (void); extern void ldemul_after_check_relocs @@ -131,6 +133,9 @@ typedef struct ld_emulation_xfer_struct { /* Run after parsing the command line and script file. */ void (*after_parse) (void); + /* Run before calling plugin 'all symbols read' hook. */ + void (*before_plugin_all_symbols_read) (void); + /* Run after opening all input files, and loading the symbols. */ void (*after_open) (void); diff --git a/ld/ldlang.c b/ld/ldlang.c index 1733f8e..1c8d229 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -8080,6 +8080,8 @@ lang_process (void) lang_statement_list_type added; lang_statement_list_type files, inputfiles; + ldemul_before_plugin_all_symbols_read (); + /* Now all files are read, let the plugin(s) decide if there are any more to be added to the link before we call the emulation's after_open hook. We create a private list of diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp index 64b8802..f4ea1d4 100644 --- a/ld/testsuite/ld-plugin/lto.exp +++ b/ld/testsuite/ld-plugin/lto.exp @@ -497,6 +497,31 @@ set lto_link_elf_tests [list \ "pr28879" \ "c++" \ ] \ + [list \ + "Build libpr28849a.so" \ + "-shared" \ + "-fPIC" \ + {pr28849a.c} \ + "" \ + "libpr28849a.so" \ + ] \ + [list \ + "Build libpr28849b.so" \ + "-shared -Wl,--no-as-needed,tmpdir/libpr28849a.so" \ + "" \ + {dummy.c} \ + "" \ + "libpr28849b.so" \ + ] \ + [list \ + "Build pr28849" \ + "-Wl,--no-as-needed,--copy-dt-needed-entries,-rpath-link,. \ + tmpdir/libpr28849b.so" \ + "-O2 -flto" \ + {pr28849b.c} \ + {{"nm" {-D} "pr28849.d"}} \ + "pr28849" \ + ] \ ] # PR 14918 checks that libgcc is not spuriously included in a shared link of diff --git a/ld/testsuite/ld-plugin/pr28849.d b/ld/testsuite/ld-plugin/pr28849.d new file mode 100644 index 0000000..a1f90c1 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr28849.d @@ -0,0 +1,3 @@ +#... +[0-9a-f]+ T _?should_be_dynamic_in_exec +#pass diff --git a/ld/testsuite/ld-plugin/pr28849a.c b/ld/testsuite/ld-plugin/pr28849a.c new file mode 100644 index 0000000..2fc5c7b --- /dev/null +++ b/ld/testsuite/ld-plugin/pr28849a.c @@ -0,0 +1,2 @@ +extern void should_be_dynamic_in_exec (void); +void y (void) { should_be_dynamic_in_exec (); } diff --git a/ld/testsuite/ld-plugin/pr28849b.c b/ld/testsuite/ld-plugin/pr28849b.c new file mode 100644 index 0000000..616d9a5 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr28849b.c @@ -0,0 +1,3 @@ +extern void y (void); +void should_be_dynamic_in_exec (void) {} +int main (void) { y (); return 0; } -- cgit v1.1