-/* Copyright (C) 1991 Free Software Foundation, Inc.
-
-This file is part of GLD, the Gnu Linker.
+/* ldctor.c -- constructor support routines
+ Copyright (C) 1991-2020 Free Software Foundation, Inc.
+ By Steve Chamberlain <sac@cygnus.com>
-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 <ldgram.h>
+#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 ();
+}