diff options
Diffstat (limited to 'contrib/binutils/ld/ldctor.c')
-rw-r--r-- | contrib/binutils/ld/ldctor.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/contrib/binutils/ld/ldctor.c b/contrib/binutils/ld/ldctor.c new file mode 100644 index 0000000..d986f32 --- /dev/null +++ b/contrib/binutils/ld/ldctor.c @@ -0,0 +1,247 @@ +/* ldctor.c -- constructor support routines + Copyright (C) 1991, 92, 93, 94, 1995 Free Software Foundation, Inc. + By Steve Chamberlain <sac@cygnus.com> + +This file is part of GLD, the Gnu Linker. + +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 2, or (at your option) +any later version. + +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. + +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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "bfd.h" +#include "sysdep.h" +#include "bfdlink.h" + +#include "ld.h" +#include "ldexp.h" +#include "ldlang.h" +#include "ldmisc.h" +#include "ldgram.h" +#include "ldmain.h" +#include "ldctor.h" + +/* 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; + +/* The sets we have seen. */ +struct set_info *sets; + +/* 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_find_constructors + function will construct the sets. */ + +void +ldctor_add_set_entry (h, reloc, name, section, value) + struct bfd_link_hash_entry *h; + bfd_reloc_code_real_type reloc; + const char *name; + asection *section; + bfd_vma value; +{ + struct set_info *p; + struct set_element *e; + struct set_element **epp; + + for (p = sets; p != (struct set_info *) NULL; p = p->next) + if (p->h == h) + break; + + if (p == (struct set_info *) 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 ("%P%X: 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 ("%P%X: Different object file formats composing set %s\n", + h->root.string); + return; + } + } + + e = (struct set_element *) xmalloc (sizeof (struct set_element)); + e->next = NULL; + e->name = name; + e->section = section; + e->value = value; + + for (epp = &p->elements; *epp != NULL; epp = &(*epp)->next) + ; + *epp = e; + + ++p->count; +} + +/* 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 +ldctor_build_sets () +{ + static boolean called; + lang_statement_list_type *old; + boolean header_printed; + struct set_info *p; + + /* The emulation code may call us directly, but we only want to do + this once. */ + if (called) + return; + called = true; + + old = stat_ptr; + stat_ptr = &constructor_list; + + lang_list_init (stat_ptr); + + header_printed = false; + for (p = sets; p != (struct set_info *) NULL; p = p->next) + { + struct set_element *e; + reloc_howto_type *howto; + int 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 relocateable output, we generate relocs instead of + addresses. */ + howto = bfd_reloc_type_lookup (output_bfd, p->reloc); + if (howto == (reloc_howto_type *) NULL) + { + if (link_info.relocateable) + { + einfo ("%P%X: %s does not support reloc %s for set %s\n", + bfd_get_target (output_bfd), + bfd_get_reloc_code_name (p->reloc), + p->h->root.string); + continue; + } + + /* If this is not a relocateable link, all we need is the + size, which we can get from the input BFD. */ + howto = bfd_reloc_type_lookup (p->elements->section->owner, + p->reloc); + if (howto == NULL) + { + einfo ("%P%X: %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; + } + } + + switch (bfd_get_reloc_size (howto)) + { + case 1: size = BYTE; break; + case 2: size = SHORT; break; + case 4: size = LONG; break; + case 8: size = QUAD; break; + default: + einfo ("%P%X: Unsupported size %d for set %s\n", + bfd_get_reloc_size (howto), p->h->root.string); + size = LONG; + break; + } + + lang_add_assignment (exp_assop ('=', p->h->root.string, + exp_nameop (NAME, "."))); + lang_add_data (size, exp_intop ((bfd_vma) p->count)); + + for (e = p->elements; e != (struct set_element *) NULL; e = e->next) + { + if (config.map_file != NULL) + { + 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 ("%T\n", e->name); + else + minfo ("%G\n", e->section->owner, e->section, e->value); + } + + if (link_info.relocateable) + 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; +} |