diff options
author | Andreas Schwab <schwab@linux-m68k.org> | 2003-01-21 12:55:11 +0000 |
---|---|---|
committer | Andreas Schwab <schwab@linux-m68k.org> | 2003-01-21 12:55:11 +0000 |
commit | 7fce784e9a2c17aa56a1ff5edd8214487154c044 (patch) | |
tree | 5be5b13b06218a654222d0afed21b5897c3a5ea7 /bfd/elf32-ppc.c | |
parent | 1bbc9cec6b49e928898668d59dda264e704ae27a (diff) | |
download | gdb-7fce784e9a2c17aa56a1ff5edd8214487154c044.zip gdb-7fce784e9a2c17aa56a1ff5edd8214487154c044.tar.gz gdb-7fce784e9a2c17aa56a1ff5edd8214487154c044.tar.bz2 |
* elf32-ppc.c (struct ppc_elf_dyn_relocs): Define.
(struct ppc_elf_link_hash_entry): Define.
(ppc_elf_hash_entry): New function.
(struct ppc_elf_link_hash_table): Define.
(ppc_elf_hash_table): New function.
(ppc_elf_link_hash_newfunc): New function.
(ppc_elf_link_hash_table_create): New function.
(ppc_elf_copy_indirect_symbol): New function.
(allocate_dynrelocs): New function.
(readonly_dynrelocs): New function.
(ppc_elf_size_dynamic_sections): Allocate space for dynamic
relocs and determine DT_TEXTREL.
(ppc_elf_check_relocs): Don't do that here, just count the
dynamic relocs.
(ppc_elf_gc_sweep_hook): Discard any dynamic relocs against the
removed section.
(bfd_elf32_bfd_link_hash_table_create): Define.
(elf_backend_copy_indirect_symbol): Define.
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r-- | bfd/elf32-ppc.c | 333 |
1 files changed, 322 insertions, 11 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index e1278c7..acd9d84 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -33,6 +33,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* RELA relocations are used here. */ +static struct bfd_hash_entry *ppc_elf_link_hash_newfunc + PARAMS ((struct bfd_hash_entry *entry, struct bfd_hash_table *table, + const char *string)); +static struct bfd_link_hash_table *ppc_elf_link_hash_table_create + PARAMS ((bfd *abfd)); +static void ppc_elf_copy_indirect_symbol + PARAMS ((struct elf_backend_data *bed, struct elf_link_hash_entry *dir, + struct elf_link_hash_entry *ind)); static reloc_howto_type *ppc_elf_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); static void ppc_elf_info_to_howto @@ -77,6 +85,10 @@ static bfd_boolean ppc_elf_gc_sweep_hook const Elf_Internal_Rela *relocs)); static bfd_boolean ppc_elf_adjust_dynamic_symbol PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static bfd_boolean allocate_dynrelocs + PARAMS ((struct elf_link_hash_entry *, PTR)); +static bfd_boolean readonly_dynrelocs + PARAMS ((struct elf_link_hash_entry *, PTR)); static bfd_boolean ppc_elf_size_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *)); static bfd_boolean ppc_elf_relocate_section @@ -134,6 +146,149 @@ static bfd_boolean ppc_elf_grok_psinfo || ELF_ST_VISIBILITY (H->other) != STV_DEFAULT) \ && (H->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0) +/* The PPC linker needs to keep track of the number of relocs that it + decides to copy as dynamic relocs in check_relocs for each symbol. + This is so that it can later discard them if they are found to be + unnecessary. We store the information in a field extending the + regular ELF linker hash table. */ + +struct ppc_elf_dyn_relocs +{ + struct ppc_elf_dyn_relocs *next; + + /* The input section of the reloc. */ + asection *sec; + + /* Total number of relocs copied for the input section. */ + bfd_size_type count; +}; + +/* PPC ELF linker hash entry. */ + +struct ppc_elf_link_hash_entry +{ + struct elf_link_hash_entry root; + + /* Track dynamic relocs copied for this symbol. */ + struct ppc_elf_dyn_relocs *dyn_relocs; +}; + +#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent)) + +/* PPC ELF linker hash table. */ + +struct ppc_elf_link_hash_table +{ + struct elf_link_hash_table root; + + /* Small local sym to section mapping cache. */ + struct sym_sec_cache sym_sec; +}; + +/* Get the PPC ELF linker hash table from a link_info structure. */ + +#define ppc_elf_hash_table(p) \ + ((struct ppc_elf_link_hash_table *) (p)->hash) + +/* Create an entry in a PPC ELF linker hash table. */ + +static struct bfd_hash_entry * +ppc_elf_link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, + sizeof (struct ppc_elf_link_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = _bfd_elf_link_hash_newfunc (entry, table, string); + if (entry != NULL) + ppc_elf_hash_entry (entry)->dyn_relocs = NULL; + + return entry; +} + +/* Create a PPC ELF linker hash table. */ + +static struct bfd_link_hash_table * +ppc_elf_link_hash_table_create (abfd) + bfd *abfd; +{ + struct ppc_elf_link_hash_table *ret; + + ret = ((struct ppc_elf_link_hash_table *) + bfd_malloc (sizeof (struct ppc_elf_link_hash_table))); + if (ret == NULL) + return NULL; + + if (! _bfd_elf_link_hash_table_init (&ret->root, abfd, + ppc_elf_link_hash_newfunc)) + { + free (ret); + return NULL; + } + + ret->sym_sec.abfd = NULL; + + return &ret->root.root; +} + +/* Copy the extra info we tack onto an elf_link_hash_entry. */ + +static void +ppc_elf_copy_indirect_symbol (bed, dir, ind) + struct elf_backend_data *bed; + struct elf_link_hash_entry *dir, *ind; +{ + struct ppc_elf_link_hash_entry *edir, *eind; + + edir = (struct ppc_elf_link_hash_entry *) dir; + eind = (struct ppc_elf_link_hash_entry *) ind; + + if (eind->dyn_relocs != NULL) + { + if (edir->dyn_relocs != NULL) + { + struct ppc_elf_dyn_relocs **pp; + struct ppc_elf_dyn_relocs *p; + + if (ind->root.type == bfd_link_hash_indirect) + abort (); + + /* Add reloc counts against the weak sym to the strong sym + list. Merge any entries against the same section. */ + for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) + { + struct ppc_elf_dyn_relocs *q; + + for (q = edir->dyn_relocs; q != NULL; q = q->next) + if (q->sec == p->sec) + { + q->count += p->count; + *pp = p->next; + break; + } + if (q == NULL) + pp = &p->next; + } + *pp = edir->dyn_relocs; + } + + edir->dyn_relocs = eind->dyn_relocs; + eind->dyn_relocs = NULL; + } + + _bfd_elf_link_hash_copy_indirect (bed, dir, ind); +} + static reloc_howto_type *ppc_elf_howto_table[(int) R_PPC_max]; static reloc_howto_type ppc_elf_howto_raw[] = { @@ -1903,6 +2058,65 @@ ppc_elf_adjust_dynamic_symbol (info, h) return TRUE; } +/* Allocate space in associated reloc sections for dynamic relocs. */ + +static bfd_boolean +allocate_dynrelocs (h, info) + struct elf_link_hash_entry *h; + PTR info ATTRIBUTE_UNUSED; +{ + struct ppc_elf_dyn_relocs *p; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + /* When warning symbols are created, they **replace** the "real" + entry in the hash table, thus we never get to see the real + symbol in a hash traversal. So look at it now. */ + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next) + { + asection *sreloc = elf_section_data (p->sec)->sreloc; + sreloc->_raw_size += p->count * sizeof (Elf32_External_Rela); + } + + return TRUE; +} + +/* Find any dynamic relocs that apply to read-only sections. */ + +static bfd_boolean +readonly_dynrelocs (h, info) + struct elf_link_hash_entry *h; + PTR info; +{ + struct ppc_elf_dyn_relocs *p; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next) + { + asection *s = p->sec->output_section; + + if (s != NULL + && ((s->flags & (SEC_READONLY | SEC_ALLOC)) + == (SEC_READONLY | SEC_ALLOC))) + { + ((struct bfd_link_info *) info)->flags |= DF_TEXTREL; + + /* Not an error, just cut short the traversal. */ + return FALSE; + } + } + return TRUE; +} + /* Set the sizes of the dynamic sections. */ static bfd_boolean @@ -1914,6 +2128,7 @@ ppc_elf_size_dynamic_sections (output_bfd, info) asection *s; bfd_boolean plt; bfd_boolean relocs; + bfd *ibfd; #ifdef DEBUG fprintf (stderr, "ppc_elf_size_dynamic_sections called\n"); @@ -1953,6 +2168,45 @@ ppc_elf_size_dynamic_sections (output_bfd, info) } } + /* Allocate space for local sym dynamic relocs. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + continue; + + for (s = ibfd->sections; s != NULL; s = s->next) + { + struct ppc_elf_dyn_relocs *p; + + for (p = ((struct ppc_elf_dyn_relocs *) + elf_section_data (s)->local_dynrel); + p != NULL; + p = p->next) + { + if (!bfd_is_abs_section (p->sec) + && bfd_is_abs_section (p->sec->output_section)) + { + /* Input section has been discarded, either because + it is a copy of a linkonce section or due to + linker script /DISCARD/, so we'll be discarding + the relocs too. */ + } + else if (p->count != 0) + { + elf_section_data (p->sec)->sreloc->_raw_size + += p->count * sizeof (Elf32_External_Rela); + if ((p->sec->output_section->flags + & (SEC_READONLY | SEC_ALLOC)) + == (SEC_READONLY | SEC_ALLOC)) + info->flags |= DF_TEXTREL; + } + } + } + } + + /* Allocate space for global sym dynamic relocs. */ + elf_link_hash_traverse (elf_hash_table (info), allocate_dynrelocs, NULL); + /* The check_relocs and adjust_dynamic_symbol entry points have determined the sizes of the various dynamic sections. Allocate memory for them. */ @@ -2064,6 +2318,12 @@ ppc_elf_size_dynamic_sections (output_bfd, info) return FALSE; } + /* If any dynamic relocs apply to a read-only section, then we + need a DT_TEXTREL entry. */ + if ((info->flags & DF_TEXTREL) == 0) + elf_link_hash_traverse (elf_hash_table (info), readonly_dynrelocs, + (PTR) info); + if ((info->flags & DF_TEXTREL) != 0) { if (!add_dynamic_entry (DT_TEXTREL, 0)) @@ -2402,6 +2662,9 @@ ppc_elf_check_relocs (abfd, info, sec, relocs) default: if (info->shared) { + struct ppc_elf_dyn_relocs *p; + struct ppc_elf_dyn_relocs **head; + #ifdef DEBUG fprintf (stderr, "ppc_elf_check_relocs need to create relocation for %s\n", (h && h->root.root.string) ? h->root.root.string : "<unknown>"); @@ -2436,19 +2699,46 @@ ppc_elf_check_relocs (abfd, info, sec, relocs) || ! bfd_set_section_alignment (dynobj, sreloc, 2)) return FALSE; } - if ((sec->flags & (SEC_READONLY | SEC_ALLOC)) - == (SEC_READONLY | SEC_ALLOC)) - info->flags |= DF_TEXTREL; + elf_section_data (sec)->sreloc = sreloc; + } + + /* If this is a global symbol, we count the number of + relocations we need for this symbol. */ + if (h != NULL) + { + head = &ppc_elf_hash_entry (h)->dyn_relocs; } + else + { + /* Track dynamic relocs needed for local syms too. + We really need local syms available to do this + easily. Oh well. */ + + asection *s; + s = (bfd_section_from_r_symndx + (abfd, &ppc_elf_hash_table (info)->sym_sec, + sec, r_symndx)); + if (s == NULL) + return FALSE; - sreloc->_raw_size += sizeof (Elf32_External_Rela); + head = ((struct ppc_elf_dyn_relocs **) + &elf_section_data (s)->local_dynrel); + } - /* FIXME: We should here do what the m68k and i386 - backends do: if the reloc is pc-relative, record it - in case it turns out that the reloc is unnecessary - because the symbol is forced local by versioning or - we are linking with -Bdynamic. Fortunately this - case is not frequent. */ + p = *head; + if (p == NULL || p->sec != sec) + { + p = ((struct ppc_elf_dyn_relocs *) + bfd_alloc (elf_hash_table (info)->dynobj, sizeof *p)); + if (p == NULL) + return FALSE; + p->next = *head; + *head = p; + p->sec = sec; + p->count = 0; + } + + p->count++; } break; @@ -2514,6 +2804,8 @@ ppc_elf_gc_sweep_hook (abfd, info, sec, relocs) unsigned long r_symndx; struct elf_link_hash_entry *h; + elf_section_data (sec)->local_dynrel = NULL; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); local_got_refcounts = elf_local_got_refcounts (abfd); @@ -2552,9 +2844,26 @@ ppc_elf_gc_sweep_hook (abfd, info, sec, relocs) if (h->plt.refcount > 0) h->plt.refcount--; } - break; + /* Fall through */ default: + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + struct ppc_elf_dyn_relocs **pp, *p; + + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + for (pp = &ppc_elf_hash_entry (h)->dyn_relocs; + (p = *pp) != NULL; + pp = &p->next) + if (p->sec == sec) + { + if (--p->count == 0) + *pp = p->next; + break; + } + } break; } @@ -4122,6 +4431,7 @@ ppc_elf_final_write_processing (abfd, linker) #define bfd_elf32_bfd_reloc_type_lookup ppc_elf_reloc_type_lookup #define bfd_elf32_bfd_set_private_flags ppc_elf_set_private_flags #define bfd_elf32_bfd_final_link _bfd_elf32_gc_common_final_link +#define bfd_elf32_bfd_link_hash_table_create ppc_elf_link_hash_table_create #define elf_backend_object_p ppc_elf_object_p #define elf_backend_gc_mark_hook ppc_elf_gc_mark_hook @@ -4130,6 +4440,7 @@ ppc_elf_final_write_processing (abfd, linker) #define elf_backend_relocate_section ppc_elf_relocate_section #define elf_backend_create_dynamic_sections ppc_elf_create_dynamic_sections #define elf_backend_check_relocs ppc_elf_check_relocs +#define elf_backend_copy_indirect_symbol ppc_elf_copy_indirect_symbol #define elf_backend_adjust_dynamic_symbol ppc_elf_adjust_dynamic_symbol #define elf_backend_add_symbol_hook ppc_elf_add_symbol_hook #define elf_backend_size_dynamic_sections ppc_elf_size_dynamic_sections |