From: H.J. Lu Date: Thu, 28 Apr 2016 12:25:17 +0000 (-0700) Subject: Add a do_assignments hook to ldemul X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=973751eab4d682a4381f3f676dc6a519c2766583;p=thirdparty%2Fbinutils-gdb.git Add a do_assignments hook to ldemul When ELF relocation check is moved after lang_gc_sections, we should call bfd_elf_record_link_assignment before ELF relocation check so that linker defined symbols will be processed earlier to stabilize symbols. We have to do it in lang_do_assignments just before the call to lang_do_assignments_1. bfd/ * elf-bfd.h (_bfd_elf_record_start_stop): New prototype. * elflink.c (start_stop_symbol): New struct. (start_stop_symbol_info): Likewise. (elf_link_collect_start_stop): New function. (_bfd_elf_record_start_stop): Likewise. ld/ * ldemul.c (ldemul_do_assignments): New function. * ldemul.h (ldemul_do_assignments): New prototype. (ld_emulation_xfer_struct): Add do_assignments. * ldlang.c (lang_do_assignments): Call ldemul_do_assignments just before lang_do_assignments_1 in lang_mark_phase_enum. * emultempl/aix.em (ld_${EMULATION_NAME}_emulation): Initialize the do_assignments field to NULL. * emultempl/armcoff.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/beos.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/generic.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/gld960.em (ld_gld960_emulation): Likewise. * emultempl/gld960c.em (ld_gld960coff_emulation): Likewise. * emultempl/linux.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/lnk960.em (ld_lnk960_emulation): Likewise. * emultempl/m68kcoff.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/msp430.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/pe.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/pep.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/sunos.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/ticoff.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/vanilla.em (ld_${EMULATION_NAME}_emulation): Likewise. * emultempl/elf32.em (ehdr_start): New variable. (ehdr_start_save): Likewise. (gld${EMULATION_NAME}_do_assignments): New function. (gld${EMULATION_NAME}_restore_ehdr_start): Likewise. (ehdr_start_empty): Removed. (gld${EMULATION_NAME}_before_allocation): Don't adjust __ehdr_start here. Call gld${EMULATION_NAME}_restore_ehdr_start at the end. (ld_${EMULATION_NAME}_emulation): Initialize the do_assignments field with gld${EMULATION_NAME}_do_assignments by default. --- diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 9067dd9aea1..2dafbcf6a7a 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -2341,6 +2341,9 @@ extern bfd_boolean bfd_elf_gc_common_final_link extern asection *_bfd_elf_is_start_stop (const struct bfd_link_info *, struct elf_link_hash_entry *); +extern void _bfd_elf_record_start_stop + (struct bfd_link_info *); + extern bfd_boolean bfd_elf_reloc_symbol_deleted_p (bfd_vma, void *); diff --git a/bfd/elflink.c b/bfd/elflink.c index b6ff6b650a9..a6e5bfb4818 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -13791,3 +13791,130 @@ elf_append_rel (bfd *abfd, asection *s, Elf_Internal_Rela *rel) BFD_ASSERT (loc + bed->s->sizeof_rel <= s->contents + s->size); bed->s->swap_reloc_out (abfd, rel, loc); } + +/* A linked list of __start_ and __stop_ symbols */ + +struct start_stop_symbol +{ + struct elf_link_hash_entry *h; + const char *sec_name; + struct start_stop_symbol *next; +}; + +struct start_stop_symbol_info +{ + struct start_stop_symbol *symbol_list; + bfd_boolean failed; +}; + +/* Collect __start_ and __stop_ symbols referenced by + regular objects. This is called via elf_link_hash_traverse. */ + +static bfd_boolean +elf_link_collect_start_stop (struct elf_link_hash_entry *h, void *data) +{ + if (!h->def_regular && h->ref_regular) + { + struct start_stop_symbol_info *info; + struct start_stop_symbol *p, **head; + const char *sec_name = h->root.root.string; + + if (sec_name[0] == '_' + && sec_name[1] == '_' + && sec_name[2] == 's' + && sec_name[3] == 't') + sec_name += 4; + else + return TRUE; + + if (sec_name[0] == 'a' + && sec_name[1] == 'r' + && sec_name[2] == 't') + sec_name += 3; + else if (sec_name[0] == 'o' + && sec_name[1] == 'p') + sec_name += 2; + else + return TRUE; + + if (sec_name[0] == '_' && sec_name[1] != '\0') + sec_name += 1; + else + return TRUE; + + info = (struct start_stop_symbol_info *) data; + + p = (struct start_stop_symbol *) bfd_malloc (sizeof (*p)); + if (p == NULL) + { + info->failed = TRUE; + return FALSE; + } + + head = &info->symbol_list; + p->next = info->symbol_list; + p->h = h; + p->sec_name = sec_name; + *head = p; + } + return TRUE; +} + +/* Record all __start_ and __stop_ symbols referenced by + regular objects. */ + +void +_bfd_elf_record_start_stop (struct bfd_link_info *info) +{ + struct start_stop_symbol_info symbols; + + /* Collect start/stop symbols. Assuming there are fewer start/stop + symbols than input files, avoid scan over all input files for each + start/stop symbol. */ + symbols.symbol_list = NULL; + symbols.failed = FALSE; + elf_link_hash_traverse (elf_hash_table (info), + elf_link_collect_start_stop, &symbols); + + if (symbols.failed) + info->callbacks->einfo (_("%P%X: collect start/stop symbols: %E\n")); + else if (symbols.symbol_list) + { + bfd *i; + + for (i = info->input_bfds; i != NULL; i = i->link.next) + if ((i->flags + & (DYNAMIC | BFD_LINKER_CREATED | BFD_PLUGIN)) == 0) + { + /* Only check regular input files. */ + struct start_stop_symbol *p, **pp; + + for (pp = &symbols.symbol_list; (p = *pp) != NULL; ) + if (bfd_get_section_by_name (i, p->sec_name) != NULL) + { + p->h->def_regular = 1; + p->h->type = STT_OBJECT; + /* If it is currently defined by a dynamic object, but + not by a regular object, then mark it as undefined + so that the generic linker will force the correct + value. */ + if (p->h->def_dynamic) + { + p->h->root.type = bfd_link_hash_undefined; + p->h->root.u.undef.abfd + = p->h->root.u.def.section->owner; + } + + /* Remove this from the list. */ + *pp = p->next; + free (p); + } + else + pp = &p->next; + + /* Stop when the list is empty. */ + if (symbols.symbol_list == NULL) + break; + } + } +} diff --git a/ld/emultempl/aix.em b/ld/emultempl/aix.em index b9cab4e691c..3c775f93f38 100644 --- a/ld/emultempl/aix.em +++ b/ld/emultempl/aix.em @@ -1554,6 +1554,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = { NULL, /* recognized_file */ NULL, /* find potential_libraries */ NULL, /* new_vers_pattern */ - NULL /* extra_map_file_text */ + NULL, /* extra_map_file_text */ + NULL /* do_assignments */ }; EOF diff --git a/ld/emultempl/armcoff.em b/ld/emultempl/armcoff.em index 387a1f71f44..98489955e2e 100644 --- a/ld/emultempl/armcoff.em +++ b/ld/emultempl/armcoff.em @@ -280,6 +280,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = NULL, /* recognized file */ NULL, /* find_potential_libraries */ NULL, /* new_vers_pattern */ - NULL /* extra_map_file_text */ + NULL, /* extra_map_file_text */ + NULL /* do_assignments */ }; EOF diff --git a/ld/emultempl/beos.em b/ld/emultempl/beos.em index 64301023394..26226887ca8 100644 --- a/ld/emultempl/beos.em +++ b/ld/emultempl/beos.em @@ -778,6 +778,7 @@ struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation = NULL, /* recognized file */ NULL, /* find_potential_libraries */ NULL, /* new_vers_pattern */ - NULL /* extra_map_file_text */ + NULL, /* extra_map_file_text */ + NULL /* do_assignments */ }; EOF diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em index 4f5d1a4d2c0..1b26e1f7c4d 100644 --- a/ld/emultempl/elf32.em +++ b/ld/emultempl/elf32.em @@ -1330,6 +1330,7 @@ fragment <assignment_statement.exp); } +static struct elf_link_hash_entry *ehdr_start; +static struct bfd_link_hash_entry ehdr_start_save; + +static void +gld${EMULATION_NAME}_do_assignments (void) +{ + if (is_elf_hash_table (link_info.hash)) + { + /* If we are going to make any variable assignments, we need to + let the ELF backend know about them in case the variables are + referred to by dynamic objects. */ + lang_for_each_statement (gld${EMULATION_NAME}_find_statement_assignment); + + /* Also let the ELF backend know about __start_ and + __stop_ symbols. */ + _bfd_elf_record_start_stop (&link_info); + + /* Make __ehdr_start hidden if it has been referenced, to + prevent the symbol from being dynamic. */ + if (!bfd_link_relocatable (&link_info)) + { + struct elf_link_hash_entry *h + = elf_link_hash_lookup (elf_hash_table (&link_info), + "__ehdr_start", FALSE, FALSE, TRUE); + + /* Only adjust the export class if the symbol was referenced + and not defined, otherwise leave it alone. def_regular is + set by the ELF backend if __ehdr_start is defined in linker + script. */ + if (h != NULL + && !h->def_regular + && (h->root.type == bfd_link_hash_new + || h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_undefweak + || h->root.type == bfd_link_hash_common)) + { + _bfd_elf_link_hash_hide_symbol (&link_info, h, TRUE); + if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL) + h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN; + /* Don't leave the symbol undefined. Undefined hidden + symbols typically won't have dynamic relocations, but + we most likely will need dynamic relocations for + __ehdr_start if we are building a PIE or shared + library. */ + ehdr_start = h; + ehdr_start_save = h->root; + h->root.type = bfd_link_hash_defined; + h->root.u.def.section = bfd_abs_section_ptr; + h->root.u.def.value = 0; + } + } + } +} + +static void +gld${EMULATION_NAME}_restore_ehdr_start (void) +{ + if (ehdr_start != NULL) + { + /* If we twiddled __ehdr_start to defined earlier, put it back + as it was. */ + ehdr_start->root.type = ehdr_start_save.type; + ehdr_start->root.u = ehdr_start_save.u; + } +} + +EOF +else +fragment <