diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-01-08 11:10:27 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-01-08 11:10:27 +0000 |
commit | 8b46c09925a80623c289e346c12921bc09fd1678 (patch) | |
tree | 01507d0da339cc7e5e6f5d16dfa625f94910b091 /gnu/usr.bin/binutils/ld/ldwrite.c | |
parent | 5d56227f9458a53138642c1b4488b4a30f85f334 (diff) |
Initial GNU binutils 2.6 import
Diffstat (limited to 'gnu/usr.bin/binutils/ld/ldwrite.c')
-rw-r--r-- | gnu/usr.bin/binutils/ld/ldwrite.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/ld/ldwrite.c b/gnu/usr.bin/binutils/ld/ldwrite.c new file mode 100644 index 00000000000..0bb86691644 --- /dev/null +++ b/gnu/usr.bin/binutils/ld/ldwrite.c @@ -0,0 +1,588 @@ +/* ldwrite.c -- write out the linked file + Copyright (C) 1993 Free Software Foundation, Inc. + Written by Steve Chamberlain sac@cygnus.com + +This file is part of GLD, the Gnu Linker. + +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 2 of the License, or +(at your option) any later version. + +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. + +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., 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 "ldwrite.h" +#include "ldmisc.h" +#include "ldgram.h" +#include "ldmain.h" + +static void build_link_order PARAMS ((lang_statement_union_type *)); +static void print_symbol_table PARAMS ((void)); +static void print_file_stuff PARAMS ((lang_input_statement_type *)); +static boolean print_symbol PARAMS ((struct bfd_link_hash_entry *, PTR)); + +extern char *strdup(); + +/* Build link_order structures for the BFD linker. */ + +static void +build_link_order (statement) + lang_statement_union_type *statement; +{ + switch (statement->header.type) + { + case lang_data_statement_enum: + { + asection *output_section; + struct bfd_link_order *link_order; + bfd_vma value; + + output_section = statement->data_statement.output_section; + ASSERT (output_section->owner == output_bfd); + + link_order = bfd_new_link_order (output_bfd, output_section); + if (link_order == NULL) + einfo ("%P%F: bfd_new_link_order failed\n"); + + link_order->type = bfd_data_link_order; + link_order->offset = statement->data_statement.output_vma; + link_order->u.data.contents = (bfd_byte *) xmalloc (QUAD_SIZE); + + value = statement->data_statement.value; + + ASSERT (output_section->owner == output_bfd); + switch (statement->data_statement.type) + { + case QUAD: + bfd_put_64 (output_bfd, value, link_order->u.data.contents); + link_order->size = QUAD_SIZE; + break; + case LONG: + bfd_put_32 (output_bfd, value, link_order->u.data.contents); + link_order->size = LONG_SIZE; + break; + case SHORT: + bfd_put_16 (output_bfd, value, link_order->u.data.contents); + link_order->size = SHORT_SIZE; + break; + case BYTE: + bfd_put_8 (output_bfd, value, link_order->u.data.contents); + link_order->size = BYTE_SIZE; + break; + default: + abort (); + } + } + break; + + case lang_reloc_statement_enum: + { + lang_reloc_statement_type *rs; + asection *output_section; + struct bfd_link_order *link_order; + + rs = &statement->reloc_statement; + + output_section = rs->output_section; + ASSERT (output_section->owner == output_bfd); + + link_order = bfd_new_link_order (output_bfd, output_section); + if (link_order == NULL) + einfo ("%P%F: bfd_new_link_order failed\n"); + + link_order->offset = rs->output_vma; + link_order->size = bfd_get_reloc_size (rs->howto); + + link_order->u.reloc.p = + ((struct bfd_link_order_reloc *) + xmalloc (sizeof (struct bfd_link_order_reloc))); + + link_order->u.reloc.p->reloc = rs->reloc; + link_order->u.reloc.p->addend = rs->addend_value; + + if (rs->name == NULL) + { + link_order->type = bfd_section_reloc_link_order; + if (rs->section->owner == output_bfd) + link_order->u.reloc.p->u.section = rs->section; + else + { + link_order->u.reloc.p->u.section = rs->section->output_section; + link_order->u.reloc.p->addend += rs->section->output_offset; + } + } + else + { + link_order->type = bfd_symbol_reloc_link_order; + link_order->u.reloc.p->u.name = rs->name; + } + } + break; + + case lang_input_section_enum: + /* Create a new link_order in the output section with this + attached */ + if (statement->input_section.ifile->just_syms_flag == false) + { + asection *i = statement->input_section.section; + asection *output_section = i->output_section; + + ASSERT (output_section->owner == output_bfd); + + if ((output_section->flags & SEC_HAS_CONTENTS) != 0) + { + struct bfd_link_order *link_order; + + link_order = bfd_new_link_order (output_bfd, output_section); + + if (i->flags & SEC_NEVER_LOAD) + { + /* We've got a never load section inside one which + is going to be output, we'll change it into a + fill link_order */ + link_order->type = bfd_fill_link_order; + link_order->u.fill.value = 0; + } + else + { + link_order->type = bfd_indirect_link_order; + link_order->u.indirect.section = i; + ASSERT (i->output_section == output_section); + } + if (i->_cooked_size) + link_order->size = i->_cooked_size; + else + link_order->size = bfd_get_section_size_before_reloc (i); + link_order->offset = i->output_offset; + } + } + break; + + case lang_padding_statement_enum: + /* Make a new link_order with the right filler */ + { + asection *output_section; + struct bfd_link_order *link_order; + + output_section = statement->padding_statement.output_section; + ASSERT (statement->padding_statement.output_section->owner + == output_bfd); + if ((output_section->flags & SEC_HAS_CONTENTS) != 0) + { + link_order = bfd_new_link_order (output_bfd, output_section); + link_order->type = bfd_fill_link_order; + link_order->size = statement->padding_statement.size; + link_order->offset = statement->padding_statement.output_offset; + link_order->u.fill.value = statement->padding_statement.fill; + } + } + break; + + default: + /* All the other ones fall through */ + break; + } +} + +/* Call BFD to write out the linked file. */ + + +/**********************************************************************/ + + +/* Wander around the input sections, make sure that + we'll never try and create an output section with more relocs + than will fit.. Do this by always assuming the worst case, and + creating new output sections with all the right bits */ +#define TESTIT 1 +static asection * +clone_section (abfd, s, count) + bfd *abfd; + asection *s; + int *count; +{ +#define SSIZE 8 + char sname[SSIZE]; /* ?? find the name for this size */ + asection *n; + struct bfd_link_hash_entry *h; + /* Invent a section name - use first five + chars of base section name and a digit suffix */ + do + { + unsigned int i; + char b[6]; + for (i = 0; i < sizeof (b) - 1 && s->name[i]; i++) + b[i] = s->name[i]; + b[i] = 0; + sprintf (sname, "%s%d", b, (*count)++); + } + while (bfd_get_section_by_name (abfd, sname)); + + n = bfd_make_section_anyway (abfd, strdup (sname)); + + /* Create a symbol of the same name */ + + h = bfd_link_hash_lookup (link_info.hash, + sname, true, true, false); + h->type = bfd_link_hash_defined; + h->u.def.value = 0; + h->u.def.section = n ; + + + n->flags = s->flags; + n->vma = s->vma; + n->user_set_vma = s->user_set_vma; + n->lma = s->lma; + n->_cooked_size = 0; + n->_raw_size = 0; + n->output_offset = s->output_offset; + n->output_section = n; + n->orelocation = 0; + n->reloc_count = 0; + n->alignment_power = s->alignment_power; + return n; +} + +#if TESTING +static void +ds (s) + asection *s; +{ + struct bfd_link_order *l = s->link_order_head; + printf ("vma %x size %x\n", s->vma, s->_raw_size); + while (l) + { + if (l->type == bfd_indirect_link_order) + { + printf ("%8x %s\n", l->offset, l->u.indirect.section->owner->filename); + } + else + { + printf ("%8x something else\n", l->offset); + } + l = l->next; + } + printf ("\n"); +} +dump (s, a1, a2) + char *s; + asection *a1; + asection *a2; +{ + printf ("%s\n", s); + ds (a1); + ds (a2); +} + +static void +sanity_check (abfd) + bfd *abfd; +{ + asection *s; + for (s = abfd->sections; s; s = s->next) + { + struct bfd_link_order *p; + bfd_vma prev = 0; + for (p = s->link_order_head; p; p = p->next) + { + if (p->offset > 100000) + abort (); + if (p->offset < prev) + abort (); + prev = p->offset; + } + } +} +#else +#define sanity_check(a) +#define dump(a, b, c) +#endif + + +void +split_sections (abfd, info) + bfd *abfd; + struct bfd_link_info *info; +{ + asection *original_sec; + int nsecs = abfd->section_count; + sanity_check (abfd); + /* look through all the original sections */ + for (original_sec = abfd->sections; + original_sec && nsecs; + original_sec = original_sec->next, nsecs--) + { + boolean first = true; + int count = 0; + int lines = 0; + int relocs = 0; + struct bfd_link_order **pp; + bfd_vma vma = original_sec->vma; + bfd_vma shift_offset = 0; + asection *cursor = original_sec; + + /* count up the relocations and line entries to see if + anything would be too big to fit */ + for (pp = &(cursor->link_order_head); *pp; pp = &((*pp)->next)) + { + struct bfd_link_order *p = *pp; + int thislines = 0; + int thisrelocs = 0; + if (p->type == bfd_indirect_link_order) + { + asection *sec; + + sec = p->u.indirect.section; + + if (info->strip == strip_none + || info->strip == strip_some) + thislines = sec->lineno_count; + + if (info->relocateable) + thisrelocs = sec->reloc_count; + + } + else if (info->relocateable + && (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order)) + thisrelocs++; + + if (! first + && (thisrelocs + relocs > config.split_by_reloc + || thislines + lines > config.split_by_reloc + || config.split_by_file)) + { + /* create a new section and put this link order and the + following link orders into it */ + struct bfd_link_order *l = p; + asection *n = clone_section (abfd, cursor, &count); + *pp = NULL; /* Snip off link orders from old section */ + n->link_order_head = l; /* attach to new section */ + pp = &n->link_order_head; + + /* change the size of the original section and + update the vma of the new one */ + + dump ("before snip", cursor, n); + + n->_raw_size = cursor->_raw_size - l->offset; + cursor->_raw_size = l->offset; + + vma += cursor->_raw_size; + n->lma = n->vma = vma; + + shift_offset = l->offset; + + /* run down the chain and change the output section to + the right one, update the offsets too */ + + while (l) + { + l->offset -= shift_offset; + if (l->type == bfd_indirect_link_order) + { + l->u.indirect.section->output_section = n; + l->u.indirect.section->output_offset = l->offset; + } + l = l->next; + } + dump ("after snip", cursor, n); + cursor = n; + relocs = thisrelocs; + lines = thislines; + } + else + { + relocs += thisrelocs; + lines += thislines; + } + + first = false; + } + } + sanity_check (abfd); +} +/**********************************************************************/ +void +ldwrite () +{ + /* Reset error indicator, which can typically something like invalid + format from openning up the .o files */ + bfd_set_error (bfd_error_no_error); + lang_for_each_statement (build_link_order); + + if (config.split_by_reloc || config.split_by_file) + split_sections (output_bfd, &link_info); + if (!bfd_final_link (output_bfd, &link_info)) + { + /* If there was an error recorded, print it out. Otherwise assume + an appropriate error message like unknown symbol was printed + out. */ + + if (bfd_get_error () != bfd_error_no_error) + einfo ("%F%P: final link failed: %E\n", output_bfd); + else + xexit(1); + } + + if (config.map_file) + { + print_symbol_table (); + lang_map (); + } +} + +/* Print the symbol table. */ + +static void +print_symbol_table () +{ + fprintf (config.map_file, "**FILES**\n\n"); + lang_for_each_file (print_file_stuff); + + fprintf (config.map_file, "**GLOBAL SYMBOLS**\n\n"); + fprintf (config.map_file, "offset section offset symbol\n"); + bfd_link_hash_traverse (link_info.hash, print_symbol, (PTR) NULL); +} + +/* Print information about a file. */ + +static void +print_file_stuff (f) + lang_input_statement_type *f; +{ + fprintf (config.map_file, " %s\n", f->filename); + if (f->just_syms_flag) + { + fprintf (config.map_file, " symbols only\n"); + } + else + { + asection *s; + if (true) + { + for (s = f->the_bfd->sections; + s != (asection *) NULL; + s = s->next) + { +#ifdef WINDOWS_NT + /* Don't include any information that goes into the '.junk' + section. This includes the code view .debug$ data and + stuff from .drectve sections */ + if (strcmp (s->name, ".drectve") == 0 || + strncmp (s->name, ".debug$", 7) == 0) + continue; +#endif + print_address (s->output_offset); + if (s->reloc_done) + { + fprintf (config.map_file, " %08x 2**%2ud %s\n", + (unsigned) bfd_get_section_size_after_reloc (s), + s->alignment_power, s->name); + } + + else + { + fprintf (config.map_file, " %08x 2**%2ud %s\n", + (unsigned) bfd_get_section_size_before_reloc (s), + s->alignment_power, s->name); + } + } + } + else + { + for (s = f->the_bfd->sections; + s != (asection *) NULL; + s = s->next) + { + fprintf (config.map_file, "%s ", s->name); + print_address (s->output_offset); + fprintf (config.map_file, "(%x)", + (unsigned) bfd_get_section_size_after_reloc (s)); + } + fprintf (config.map_file, "hex \n"); + } + } + print_nl (); +} + +/* Print a symbol. */ + +/*ARGSUSED*/ +static boolean +print_symbol (p, ignore) + struct bfd_link_hash_entry *p; + PTR ignore; +{ + while (p->type == bfd_link_hash_indirect + || p->type == bfd_link_hash_warning) + p = p->u.i.link; + + switch (p->type) + { + case bfd_link_hash_new: + abort (); + + case bfd_link_hash_undefined: + fprintf (config.map_file, "undefined "); + fprintf (config.map_file, "%s ", p->root.string); + print_nl (); + break; + + case bfd_link_hash_undefweak: + fprintf (config.map_file, "weak "); + fprintf (config.map_file, "%s ", p->root.string); + print_nl (); + break; + + case bfd_link_hash_defined: + case bfd_link_hash_defweak: + { + asection *defsec = p->u.def.section; + + print_address (p->u.def.value); + if (defsec) + { + fprintf (config.map_file, " %-10s", + bfd_section_name (output_bfd, defsec)); + print_space (); + print_address (p->u.def.value + defsec->vma); + } + else + { + fprintf (config.map_file, " ......."); + } + fprintf (config.map_file, " %s", p->root.string); + if (p->type == bfd_link_hash_defweak) + fprintf (config.map_file, " [weak]"); + } + print_nl (); + break; + + case bfd_link_hash_common: + fprintf (config.map_file, "common "); + print_address (p->u.c.size); + fprintf (config.map_file, " %s ", p->root.string); + print_nl (); + break; + + default: + abort (); + } + + return true; +} |