X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=ld%2Fldwrite.c;h=5624dfdf4c0a5c22232c9facba346af624efcb45;hb=77bf7b5317195e092a9f18fef993c412e2e05f53;hp=1dacb3de2a7a30c8abf5888739e002bce90c4a59;hpb=4fdbafb247bd7a1af8c22d6da0acc560aa16d05a;p=deliverable%2Fbinutils-gdb.git diff --git a/ld/ldwrite.c b/ld/ldwrite.c index 1dacb3de2a..5624dfdf4c 100644 --- a/ld/ldwrite.c +++ b/ld/ldwrite.c @@ -1,85 +1,151 @@ /* ldwrite.c -- write out the linked file - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1991-2020 Free Software Foundation, Inc. Written by Steve Chamberlain sac@cygnus.com -This file is part of GLD, the Gnu Linker. + This file is part of the GNU Binutils. -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 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. -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. + 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + 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 "bfd.h" #include "sysdep.h" +#include "bfd.h" #include "bfdlink.h" +#include "libiberty.h" +#include "ctf-api.h" +#include "safe-ctype.h" #include "ld.h" #include "ldexp.h" #include "ldlang.h" #include "ldwrite.h" #include "ldmisc.h" -#include "ldgram.h" +#include #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)); - /* Build link_order structures for the BFD linker. */ static void -build_link_order (statement) - lang_statement_union_type *statement; +build_link_order (lang_statement_union_type *statement) { switch (statement->header.type) { case lang_data_statement_enum: - /* FIXME: This should probably build a link_order, but instead - it just does the output directly. */ { - bfd_vma value = statement->data_statement.value; - bfd_byte play_area[QUAD_SIZE]; - unsigned int size = 0; - asection *output_section = statement->data_statement.output_section; + asection *output_section; + struct bfd_link_order *link_order; + bfd_vma value; + + output_section = statement->data_statement.output_section; + ASSERT (output_section->owner == link_info.output_bfd); + + if (!((output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((output_section->flags & SEC_LOAD) != 0 + && (output_section->flags & SEC_THREAD_LOCAL)))) + break; + + link_order = bfd_new_link_order (link_info.output_bfd, output_section); + if (link_order == NULL) + einfo (_("%F%P: bfd_new_link_order failed\n")); + + link_order->type = bfd_data_link_order; + link_order->offset = statement->data_statement.output_offset; + link_order->u.data.contents = (bfd_byte *) xmalloc (QUAD_SIZE); + + value = statement->data_statement.value; + + /* By convention, the bfd_put routines for an unknown + endianness are big endian, so we must swap here if the + input is little endian. */ + if (!bfd_big_endian (link_info.output_bfd) + && !bfd_little_endian (link_info.output_bfd) + && !link_info.big_endian) + { + bfd_byte buffer[8]; + + switch (statement->data_statement.type) + { + case QUAD: + case SQUAD: + if (sizeof (bfd_vma) >= QUAD_SIZE) + { + bfd_putl64 (value, buffer); + value = bfd_getb64 (buffer); + break; + } + /* Fall through. */ + case LONG: + bfd_putl32 (value, buffer); + value = bfd_getb32 (buffer); + break; + case SHORT: + bfd_putl16 (value, buffer); + value = bfd_getb16 (buffer); + break; + case BYTE: + break; + default: + abort (); + } + } - ASSERT (output_section->owner == output_bfd); + ASSERT (output_section->owner == link_info.output_bfd); switch (statement->data_statement.type) { case QUAD: - bfd_put_64 (output_bfd, value, play_area); - size = QUAD_SIZE; + case SQUAD: + if (sizeof (bfd_vma) >= QUAD_SIZE) + bfd_put_64 (link_info.output_bfd, value, + link_order->u.data.contents); + else + { + bfd_vma high; + + if (statement->data_statement.type == QUAD) + high = 0; + else if ((value & 0x80000000) == 0) + high = 0; + else + high = (bfd_vma) -1; + bfd_put_32 (link_info.output_bfd, high, + (link_order->u.data.contents + + (link_info.big_endian ? 0 : 4))); + bfd_put_32 (link_info.output_bfd, value, + (link_order->u.data.contents + + (link_info.big_endian ? 4 : 0))); + } + link_order->size = QUAD_SIZE; break; case LONG: - bfd_put_32 (output_bfd, value, play_area); - size = LONG_SIZE; + bfd_put_32 (link_info.output_bfd, value, + link_order->u.data.contents); + link_order->size = LONG_SIZE; break; case SHORT: - bfd_put_16 (output_bfd, value, play_area); - size = SHORT_SIZE; + bfd_put_16 (link_info.output_bfd, value, + link_order->u.data.contents); + link_order->size = SHORT_SIZE; break; case BYTE: - bfd_put_8 (output_bfd, value, play_area); - size = BYTE_SIZE; + bfd_put_8 (link_info.output_bfd, value, + link_order->u.data.contents); + link_order->size = BYTE_SIZE; break; default: abort (); } - - if (! bfd_set_section_contents (output_bfd, output_section, - play_area, - statement->data_statement.output_vma, - size)) - einfo ("%P%X: writing data failed: %E\n"); + link_order->u.data.size = link_order->size; } break; @@ -92,27 +158,30 @@ build_link_order (statement) rs = &statement->reloc_statement; output_section = rs->output_section; - ASSERT (output_section->owner == output_bfd); + ASSERT (output_section->owner == link_info.output_bfd); - link_order = bfd_new_link_order (output_bfd, output_section); + if (!((output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((output_section->flags & SEC_LOAD) != 0 + && (output_section->flags & SEC_THREAD_LOCAL)))) + break; + + link_order = bfd_new_link_order (link_info.output_bfd, output_section); if (link_order == NULL) - einfo ("%P%F: bfd_new_link_order failed"); + einfo (_("%F%P: bfd_new_link_order failed\n")); - link_order->offset = rs->output_vma; + link_order->offset = rs->output_offset; 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 = (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->section != (asection *) NULL) + if (rs->name == NULL) { - ASSERT (rs->name == (const char *) NULL); link_order->type = bfd_section_reloc_link_order; - if (rs->section->owner == output_bfd) + if (rs->section->owner == link_info.output_bfd) link_order->u.reloc.p->u.section = rs->section; else { @@ -122,7 +191,6 @@ build_link_order (statement) } else { - ASSERT (rs->name != (const char *) NULL); link_order->type = bfd_symbol_reloc_link_order; link_order->u.reloc.p->u.name = rs->name; } @@ -130,42 +198,48 @@ build_link_order (statement) 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; + { + /* Create a new link_order in the output section with this + attached */ + asection *i = statement->input_section.section; - ASSERT (output_section->owner == output_bfd); + if (i->sec_info_type != SEC_INFO_TYPE_JUST_SYMS + && (i->flags & SEC_EXCLUDE) == 0) + { + asection *output_section = i->output_section; + struct bfd_link_order *link_order; - if ((output_section->flags & SEC_HAS_CONTENTS) != 0) - { - struct bfd_link_order *link_order; + ASSERT (output_section->owner == link_info.output_bfd); - link_order = bfd_new_link_order (output_bfd, output_section); + if (!((output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((output_section->flags & SEC_LOAD) != 0 + && (output_section->flags & SEC_THREAD_LOCAL)))) + break; - 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; - } - } + link_order = bfd_new_link_order (link_info.output_bfd, + output_section); + if (link_order == NULL) + einfo (_("%F%P: bfd_new_link_order failed\n")); + + if ((i->flags & SEC_NEVER_LOAD) != 0 + && (i->flags & SEC_DEBUGGING) == 0) + { + /* We've got a never load section inside one which is + going to be output, we'll change it into a fill. */ + link_order->type = bfd_data_link_order; + link_order->u.data.contents = (unsigned char *) ""; + link_order->u.data.size = 1; + } + else + { + link_order->type = bfd_indirect_link_order; + link_order->u.indirect.section = i; + ASSERT (i->output_section == output_section); + } + link_order->size = i->size; + link_order->offset = i->output_offset; + } + } break; case lang_padding_statement_enum: @@ -176,15 +250,22 @@ build_link_order (statement) 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; - } + == link_info.output_bfd); + + if (!((output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((output_section->flags & SEC_LOAD) != 0 + && (output_section->flags & SEC_THREAD_LOCAL)))) + break; + + link_order = bfd_new_link_order (link_info.output_bfd, + output_section); + if (link_order == NULL) + einfo (_("%F%P: bfd_new_link_order failed\n")); + link_order->type = bfd_data_link_order; + link_order->size = statement->padding_statement.size; + link_order->offset = statement->padding_statement.output_offset; + link_order->u.data.contents = statement->padding_statement.fill->data; + link_order->u.data.size = statement->padding_statement.fill->size; } break; @@ -194,149 +275,282 @@ build_link_order (statement) } } -/* Call BFD to write out the linked file. */ +/* Return true if NAME is the name of an unsplittable section. These + are the stabs strings, dwarf strings. */ -void -ldwrite () +static bfd_boolean +unsplittable_name (const char *name) { - lang_for_each_statement (build_link_order); + if (CONST_STRNEQ (name, ".stab")) + { + /* There are several stab like string sections. We pattern match on + ".stab...str" */ + unsigned len = strlen (name); + if (strcmp (&name[len-3], "str") == 0) + return TRUE; + } + else if (strcmp (name, "$GDB_STRINGS$") == 0) + return TRUE; + return FALSE; +} - if (! bfd_final_link (output_bfd, &link_info)) - einfo ("%F%P: final link failed: %E\n", output_bfd); +/* 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 (bfd *abfd, asection *s, const char *name, int *count) +{ + char *tname; + char *sname; + unsigned int len; + asection *n; + struct bfd_link_hash_entry *h; + + /* Invent a section name from the section name and a dotted numeric + suffix. */ + len = strlen (name); + tname = (char *) xmalloc (len + 1); + memcpy (tname, name, len + 1); + /* Remove a dotted number suffix, from a previous split link. */ + while (len && ISDIGIT (tname[len-1])) + len--; + if (len > 1 && tname[len-1] == '.') + /* It was a dotted number. */ + tname[len-1] = 0; + + /* We want to use the whole of the original section name for the + split name, but coff can be restricted to 8 character names. */ + if (bfd_family_coff (abfd) && strlen (tname) > 5) + { + /* Some section names cannot be truncated, as the name is + used to locate some other section. */ + if (CONST_STRNEQ (name, ".stab") + || strcmp (name, "$GDB_SYMBOLS$") == 0) + { + einfo (_ ("%F%P: cannot create split section name for %s\n"), name); + /* Silence gcc warnings. einfo exits, so we never reach here. */ + return NULL; + } + tname[5] = 0; + } - if (config.map_file) + if ((sname = bfd_get_unique_section_name (abfd, tname, count)) == NULL + || (n = bfd_make_section_anyway (abfd, sname)) == NULL + || (h = bfd_link_hash_lookup (link_info.hash, + sname, TRUE, TRUE, FALSE)) == NULL) { - print_symbol_table (); - lang_map (); + einfo (_("%F%P: clone section failed: %E\n")); + /* Silence gcc warnings. einfo exits, so we never reach here. */ + return NULL; } + free (tname); + + /* Set up section symbol. */ + 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->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; + + bfd_copy_private_section_data (abfd, s, abfd, n); + + return n; } -/* Print the symbol table. */ - +#if TESTING static void -print_symbol_table () +ds (asection *s) { - 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); + struct bfd_link_order *l = s->map_head.link_order; + printf ("vma %x size %x\n", s->vma, s->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"); } -/* Print information about a file. */ +dump (char *s, asection *a1, asection *a2) +{ + printf ("%s\n", s); + ds (a1); + ds (a2); +} static void -print_file_stuff (f) - lang_input_statement_type * f; +sanity_check (bfd *abfd) { - fprintf (config.map_file, " %s\n", f->filename); - if (f->just_syms_flag) + asection *s; + for (s = abfd->sections; s; s = s->next) { - fprintf (config.map_file, " symbols only\n"); + struct bfd_link_order *p; + bfd_vma prev = 0; + for (p = s->map_head.link_order; p; p = p->next) + { + if (p->offset > 100000) + abort (); + if (p->offset < prev) + abort (); + prev = p->offset; + } } - else +} +#else +#define sanity_check(a) +#define dump(a, b, c) +#endif + +static void +split_sections (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--) { - asection *s; - if (true) + int count = 0; + unsigned int lines = 0; + unsigned int relocs = 0; + bfd_size_type sec_size = 0; + struct bfd_link_order *l; + struct bfd_link_order *p; + bfd_vma vma = original_sec->vma; + asection *cursor = original_sec; + + /* Count up the relocations and line entries to see if anything + would be too big to fit. Accumulate section size too. */ + for (l = NULL, p = cursor->map_head.link_order; p != NULL; p = l->next) { - for (s = f->the_bfd->sections; - s != (asection *) NULL; - s = s->next) + unsigned int thislines = 0; + unsigned int thisrelocs = 0; + bfd_size_type thissize = 0; + if (p->type == bfd_indirect_link_order) { - 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); - } + asection *sec; + + sec = p->u.indirect.section; + + if (info->strip == strip_none + || info->strip == strip_some) + thislines = sec->lineno_count; + + if (bfd_link_relocatable (info)) + thisrelocs = sec->reloc_count; + + thissize = sec->size; + + } + else if (bfd_link_relocatable (info) + && (p->type == bfd_section_reloc_link_order + || p->type == bfd_symbol_reloc_link_order)) + thisrelocs++; + + if (l != NULL + && (thisrelocs + relocs >= config.split_by_reloc + || thislines + lines >= config.split_by_reloc + || (thissize + sec_size >= config.split_by_file)) + && !unsplittable_name (cursor->name)) + { + /* Create a new section and put this link order and the + following link orders into it. */ + bfd_vma shift_offset; + asection *n; + + n = clone_section (abfd, cursor, original_sec->name, &count); + + /* Attach the link orders to the new section and snip + them off from the old section. */ + n->map_head.link_order = p; + n->map_tail.link_order = cursor->map_tail.link_order; + cursor->map_tail.link_order = l; + l->next = NULL; + l = p; + + /* Change the size of the original section and + update the vma of the new one. */ + + dump ("before snip", cursor, n); - else + shift_offset = p->offset; + n->size = cursor->size - shift_offset; + cursor->size = shift_offset; + + vma += shift_offset; + n->lma = n->vma = vma; + + /* Run down the chain and change the output section to + the right one, update the offsets too. */ + do { - fprintf (config.map_file, " %08x 2**%2ud %s\n", - (unsigned) bfd_get_section_size_before_reloc (s), - s->alignment_power, s->name); + p->offset -= shift_offset; + if (p->type == bfd_indirect_link_order) + { + p->u.indirect.section->output_section = n; + p->u.indirect.section->output_offset = p->offset; + } + p = p->next; } + while (p); + + dump ("after snip", cursor, n); + cursor = n; + relocs = thisrelocs; + lines = thislines; + sec_size = thissize; } - } - else - { - for (s = f->the_bfd->sections; - s != (asection *) NULL; - s = s->next) + else { - 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)); + l = p; + relocs += thisrelocs; + lines += thislines; + sec_size += thissize; } - fprintf (config.map_file, "hex \n"); } } - print_nl (); + sanity_check (abfd); } -/* Print a symbol. */ +/* Call BFD to write out the linked file. */ -/*ARGSUSED*/ -static boolean -print_symbol (p, ignore) - struct bfd_link_hash_entry *p; - PTR ignore; +void +ldwrite (void) { - while (p->type == bfd_link_hash_indirect - || p->type == bfd_link_hash_warning) - p = p->u.i.link; + /* Reset error indicator, which can typically something like invalid + format from opening up the .o files. */ + bfd_set_error (bfd_error_no_error); + lang_clear_os_map (); + lang_for_each_statement (build_link_order); - switch (p->type) + if (config.split_by_reloc != (unsigned) -1 + || config.split_by_file != (bfd_size_type) -1) + split_sections (link_info.output_bfd, &link_info); + if (!bfd_final_link (link_info.output_bfd, &link_info)) { - 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_weak: - fprintf (config.map_file, "weak "); - fprintf (config.map_file, "%s ", p->root.string); - print_nl (); - break; - - case bfd_link_hash_defined: - { - 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); - } - 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; + /* If there was an error recorded, print it out. Otherwise assume + an appropriate error message like unknown symbol was printed + out. */ - default: - abort (); + if (bfd_get_error () != bfd_error_no_error) + einfo (_("%F%P: final link failed: %E\n")); + else + xexit (1); } - - return true; }