X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=ld%2Fldctor.c;h=881ca258cbc2d403eeaa1da2c70ee858da5adead;hb=610cfd618e4ea43a106d2b24ae4fe52af72de1f5;hp=2a7e9b01355f7069966859338b2f1dffa57ea550;hpb=fcf276c4958f7cc906ec389f003aa2a713c98528;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/ldctor.c b/ld/ldctor.c index 2a7e9b0135..881ca258cb 100644 --- a/ld/ldctor.c +++ b/ld/ldctor.c @@ -1,155 +1,382 @@ -/* Copyright (C) 1991 Free Software Foundation, Inc. - -This file is part of GLD, the Gnu Linker. +/* ldctor.c -- constructor support routines + Copyright (C) 1991-2019 Free Software Foundation, Inc. + By Steve Chamberlain -GLD 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 1, or (at your option) -any later version. + This file is part of the GNU Binutils. -GLD 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. + 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. -You should have received a copy of the GNU General Public License -along with GLD; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + 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. -/* - * By steve chamberlain - * steve@cygnus.com - */ + 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. */ +#include "sysdep.h" #include "bfd.h" -#include "sysdep.h" +#include "bfdlink.h" +#include "safe-ctype.h" +#include "ctf-api.h" + #include "ld.h" #include "ldexp.h" #include "ldlang.h" -#include "ldsym.h" #include "ldmisc.h" -#include "ldgram.h" +#include +#include "ldmain.h" +#include "ldctor.h" -/* exported list of statements needed to handle constructors */ +/* The list of statements needed to handle constructors. These are + invoked by the command CONSTRUCTORS in the linker script. */ lang_statement_list_type constructor_list; +/* Whether the constructors should be sorted. Note that this is + global for the entire link; we assume that there is only a single + CONSTRUCTORS command in the linker script. */ +bfd_boolean constructors_sorted; +/* The sets we have seen. */ +struct set_info *sets; -typedef struct constructor_list -{ - CONST char *name; - struct constructor_list *next; -} constructor_list_type; - -static constructor_list_type *constructor_name_list; - -static void -add_constructor_name (name) - CONST char *name; +/* Add an entry to a set. H is the entry in the linker hash table. + RELOC is the relocation to use for an entry in the set. SECTION + and VALUE are the value to add. This is called during the first + phase of the link, when we are still gathering symbols together. + We just record the information now. The ldctor_build_sets + function will construct the sets. */ + +void +ldctor_add_set_entry (struct bfd_link_hash_entry *h, + bfd_reloc_code_real_type reloc, + const char *name, + asection *section, + bfd_vma value) { - register constructor_list_type *ptr = constructor_name_list; - for (; ptr != (constructor_list_type *)NULL; ptr = ptr->next) { - if (strcmp (ptr->name, name) == 0) - return; + struct set_info *p; + struct set_element *e; + struct set_element **epp; + + for (p = sets; p != NULL; p = p->next) + if (p->h == h) + break; + + if (p == NULL) + { + p = (struct set_info *) xmalloc (sizeof (struct set_info)); + p->next = sets; + sets = p; + p->h = h; + p->reloc = reloc; + p->count = 0; + p->elements = NULL; } + else + { + if (p->reloc != reloc) + { + einfo (_("%X%P: different relocs used in set %s\n"), + h->root.string); + return; + } + + /* Don't permit a set to be constructed from different object + file formats. The same reloc may have different results. We + actually could sometimes handle this, but the case is + unlikely to ever arise. Sometimes constructor symbols are in + unusual sections, such as the absolute section--this appears + to be the case in Linux a.out--and in such cases we just + assume everything is OK. */ + if (p->elements != NULL + && section->owner != NULL + && p->elements->section->owner != NULL + && strcmp (bfd_get_target (section->owner), + bfd_get_target (p->elements->section->owner)) != 0) + { + einfo (_("%X%P: different object file formats composing set %s\n"), + h->root.string); + return; + } + } + + e = (struct set_element *) xmalloc (sizeof (struct set_element)); + e->u.next = NULL; + e->name = name; + e->section = section; + e->value = value; - /* There isn't an entry, so add one */ - ptr = (constructor_list_type *) ldmalloc (sizeof(constructor_list_type)); - ptr->next = constructor_name_list; - ptr->name = name; - constructor_name_list = ptr; + for (epp = &p->elements; *epp != NULL; epp = &(*epp)->u.next) + ; + *epp = e; + + ++p->count; } -void -ldlang_add_constructor (name) - ldsym_type *name; +/* Get the priority of a g++ global constructor or destructor from the + symbol name. */ + +static int +ctor_prio (const char *name) { - if (name->flags & SYM_CONSTRUCTOR) return; - add_constructor_name (name->name); - name->flags |= SYM_CONSTRUCTOR; + /* The name will look something like _GLOBAL_$I$65535$test02__Fv. + There might be extra leading underscores, and the $ characters + might be something else. The I might be a D. */ + + while (*name == '_') + ++name; + + if (!CONST_STRNEQ (name, "GLOBAL_")) + return -1; + + name += sizeof "GLOBAL_" - 1; + + if (name[0] != name[2]) + return -1; + if (name[1] != 'I' && name[1] != 'D') + return -1; + if (!ISDIGIT (name[3])) + return -1; + + return atoi (name + 3); } +/* This function is used to sort constructor elements by priority. It + is called via qsort. */ + +static int +ctor_cmp (const void *p1, const void *p2) +{ + const struct set_element *pe1 = *(const struct set_element **) p1; + const struct set_element *pe2 = *(const struct set_element **) p2; + const char *n1; + const char *n2; + int prio1; + int prio2; + + n1 = pe1->name; + if (n1 == NULL) + n1 = ""; + n2 = pe2->name; + if (n2 == NULL) + n2 = ""; + + /* We need to sort in reverse order by priority. When two + constructors have the same priority, we should maintain their + current relative position. */ + + prio1 = ctor_prio (n1); + prio2 = ctor_prio (n2); + + /* We sort in reverse order because that is what g++ expects. */ + if (prio1 < prio2) + return 1; + if (prio1 > prio2) + return -1; + + /* Force a stable sort. */ + if (pe1->u.idx < pe2->u.idx) + return -1; + if (pe1->u.idx > pe2->u.idx) + return 1; + return 0; +} -/* this function looks through the sections attached to the supplied - bfd to see if any of them are magical constructor sections. If so - their names are remembered and added to the list of constructors */ +/* This function is called after the first phase of the link and + before the second phase. At this point all set information has + been gathered. We now put the statements to build the sets + themselves into constructor_list. */ void -ldlang_check_for_constructors (entry) - struct lang_input_statement_struct *entry; +ldctor_build_sets (void) { - asection *section; + static bfd_boolean called; + bfd_boolean header_printed; + struct set_info *p; - for (section = entry->the_bfd->sections; - section != (asection *)NULL; - section = section->next) + /* The emulation code may call us directly, but we only want to do + this once. */ + if (called) + return; + called = TRUE; + + if (constructors_sorted) { - if (section->flags & SEC_CONSTRUCTOR) - add_constructor_name (section->name); - } -} + for (p = sets; p != NULL; p = p->next) + { + int c, i; + struct set_element *e, *enext; + struct set_element **array; + if (p->elements == NULL) + continue; -/* run through the symbol table, find all the symbols which are - constructors and for each one, create statements to do something - like.. + c = 0; + for (e = p->elements; e != NULL; e = e->u.next) + ++c; - for something like "__CTOR_LIST__, foo" in the assembler + array = (struct set_element **) xmalloc (c * sizeof *array); - __CTOR_LIST__ = . ; - LONG(__CTOR_LIST_END - . / 4 - 2) - *(foo) - __CTOR_LIST_END= . + i = 0; + for (e = p->elements; e != NULL; e = enext) + { + array[i] = e; + enext = e->u.next; + e->u.idx = i; + ++i; + } - Put these statements onto a special list. + qsort (array, c, sizeof *array, ctor_cmp); -*/ + e = array[0]; + p->elements = e; + for (i = 0; i < c - 1; i++) + array[i]->u.next = array[i + 1]; + array[i]->u.next = NULL; + free (array); + } + } -void -find_constructors () -{ - lang_statement_list_type *old = stat_ptr; - constructor_list_type *p = constructor_name_list; - stat_ptr = & constructor_list; - lang_list_init(stat_ptr); - while (p != (constructor_list_type *)NULL) + lang_list_init (&constructor_list); + push_stat_ptr (&constructor_list); + + header_printed = FALSE; + for (p = sets; p != NULL; p = p->next) { - /* Have we already done this one ? */ - CONST char *name = p->name; - ldsym_type *lookup = ldsym_get_soft(name); + struct set_element *e; + reloc_howto_type *howto; + int reloc_size, size; + + /* If the symbol is defined, we may have been invoked from + collect, and the sets may already have been built, so we do + not do anything. */ + if (p->h->type == bfd_link_hash_defined + || p->h->type == bfd_link_hash_defweak) + continue; + + /* For each set we build: + set: + .long number_of_elements + .long element0 + ... + .long elementN + .long 0 + except that we use the right size instead of .long. When + generating relocatable output, we generate relocs instead of + addresses. */ + howto = bfd_reloc_type_lookup (link_info.output_bfd, p->reloc); + if (howto == NULL) + { + if (bfd_link_relocatable (&link_info)) + { + einfo (_("%X%P: %s does not support reloc %s for set %s\n"), + bfd_get_target (link_info.output_bfd), + bfd_get_reloc_code_name (p->reloc), + p->h->root.string); + continue; + } - /* If ld is invoked from collect, then the constructor list - will already have been defined, so don't do it again. */ + /* If this is not a relocatable link, all we need is the + size, which we can get from the input BFD. */ + if (p->elements->section->owner != NULL) + howto = bfd_reloc_type_lookup (p->elements->section->owner, + p->reloc); + if (howto == NULL) + { + /* See PR 20911 for a reproducer. */ + if (p->elements->section->owner == NULL) + einfo (_("%X%P: special section %s does not support reloc %s for set %s\n"), + bfd_section_name (p->elements->section), + bfd_get_reloc_code_name (p->reloc), + p->h->root.string); + else + einfo (_("%X%P: %s does not support reloc %s for set %s\n"), + bfd_get_target (p->elements->section->owner), + bfd_get_reloc_code_name (p->reloc), + p->h->root.string); + continue; + } + } + + reloc_size = bfd_get_reloc_size (howto); + switch (reloc_size) + { + case 1: size = BYTE; break; + case 2: size = SHORT; break; + case 4: size = LONG; break; + case 8: + if (howto->complain_on_overflow == complain_overflow_signed) + size = SQUAD; + else + size = QUAD; + break; + default: + einfo (_("%X%P: unsupported size %d for set %s\n"), + bfd_get_reloc_size (howto), p->h->root.string); + size = LONG; + break; + } + + lang_add_assignment (exp_assign (".", + exp_unop (ALIGN_K, + exp_intop (reloc_size)), + FALSE)); + lang_add_assignment (exp_assign (p->h->root.string, + exp_nameop (NAME, "."), + FALSE)); + lang_add_data (size, exp_intop (p->count)); - if (lookup->sdefs_chain == (asymbol **)NULL) + for (e = p->elements; e != NULL; e = e->u.next) + { + if (config.map_file != NULL) { - size_t len = strlen(name); - char *end = ldmalloc(len+3); - strcpy(end, name); - strcat(end,"$e"); - - lang_add_assignment - ( exp_assop('=',name, exp_nameop(NAME,"."))); - - lang_add_data - (LONG, exp_binop('-', - exp_binop ( '/', - exp_binop ( '-', - exp_nameop(NAME, end), - exp_nameop(NAME,".")), - exp_intop(4)), - - exp_intop(2))); - - - lang_add_wild(name, (char *)NULL); - lang_add_data(LONG, exp_intop(0)); - lang_add_assignment - (exp_assop('=', end, exp_nameop(NAME,"."))); + int len; + + if (!header_printed) + { + minfo (_("\nSet Symbol\n\n")); + header_printed = TRUE; + } + + minfo ("%s", p->h->root.string); + len = strlen (p->h->root.string); + + if (len >= 19) + { + print_nl (); + len = 0; + } + while (len < 20) + { + print_space (); + ++len; + } + + if (e->name != NULL) + minfo ("%pT\n", e->name); + else + minfo ("%G\n", e->section->owner, e->section, e->value); } - p = p->next; + + /* Need SEC_KEEP for --gc-sections. */ + if (!bfd_is_abs_section (e->section)) + e->section->flags |= SEC_KEEP; + + if (bfd_link_relocatable (&link_info)) + lang_add_reloc (p->reloc, howto, e->section, e->name, + exp_intop (e->value)); + else + lang_add_data (size, exp_relop (e->section, e->value)); + } + + lang_add_data (size, exp_intop (0)); } - stat_ptr = old; -} + pop_stat_ptr (); +}