X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf32-bfin.c;h=c7d11240a70f57f1fed14ba4fb4a0577ef78ea65;hb=d8045f234d8865a7a7bfce71e81fcbeaf4098a7e;hp=6ceaeb0e089b2d0c48fcae50bdc4a03e2b07f71f;hpb=0112cd268b205d8176b8b4d00988a334822956cf;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf32-bfin.c b/bfd/elf32-bfin.c index 6ceaeb0e08..c7d11240a7 100644 --- a/bfd/elf32-bfin.c +++ b/bfd/elf32-bfin.c @@ -1,11 +1,11 @@ /* ADI Blackfin BFD support for 32-bit ELF. - Copyright 2005, 2006 Free Software Foundation, Inc. + Copyright 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. 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 + 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, @@ -15,11 +15,11 @@ 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. */ + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ -#include "bfd.h" #include "sysdep.h" +#include "bfd.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf/bfin.h" @@ -716,7 +716,7 @@ static reloc_howto_type bfin_howto_table [] = 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ - "R_BFIN_GOT12", /* name */ + "R_BFIN_GOT17M4", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ @@ -1057,8 +1057,8 @@ bfin_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, else cache_ptr->howto = (reloc_howto_type *) NULL; - } + /* Given a BFD reloc type, return the howto. */ static reloc_howto_type * bfin_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, @@ -1078,8 +1078,33 @@ bfin_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, return &bfin_gnuext_howto_table [r_type - BFIN_GNUEXT_RELOC_MIN]; return (reloc_howto_type *) NULL; +} + +static reloc_howto_type * +bfin_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, + const char *r_name) +{ + unsigned int i; + + for (i = 0; + i < (sizeof (bfin_howto_table) + / sizeof (bfin_howto_table[0])); + i++) + if (bfin_howto_table[i].name != NULL + && strcasecmp (bfin_howto_table[i].name, r_name) == 0) + return &bfin_howto_table[i]; + + for (i = 0; + i < (sizeof (bfin_gnuext_howto_table) + / sizeof (bfin_gnuext_howto_table[0])); + i++) + if (bfin_gnuext_howto_table[i].name != NULL + && strcasecmp (bfin_gnuext_howto_table[i].name, r_name) == 0) + return &bfin_gnuext_howto_table[i]; + return NULL; } + /* Given a bfin relocation type, return the howto. */ static reloc_howto_type * bfin_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, @@ -1092,7 +1117,6 @@ bfin_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, return &bfin_gnuext_howto_table [r_type - BFIN_GNUEXT_RELOC_MIN]; return (reloc_howto_type *) NULL; - } /* Return TRUE if the name is a local label. @@ -1107,2122 +1131,2219 @@ bfin_is_local_label_name ( return _bfd_elf_is_local_label_name (abfd, label); } + +/* Look through the relocs for a section during the first phase, and + allocate space in the global offset table or procedure linkage + table. */ -extern const bfd_target bfd_elf32_bfinfdpic_vec; -#define IS_FDPIC(bfd) ((bfd)->xvec == &bfd_elf32_bfinfdpic_vec) - -/* An extension of the elf hash table data structure, containing some - additional Blackfin-specific data. */ -struct bfinfdpic_elf_link_hash_table +static bfd_boolean +bfin_check_relocs (bfd * abfd, + struct bfd_link_info *info, + asection *sec, + const Elf_Internal_Rela *relocs) { - struct elf_link_hash_table elf; - - /* A pointer to the .got section. */ + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; asection *sgot; - /* A pointer to the .rel.got section. */ - asection *sgotrel; - /* A pointer to the .rofixup section. */ - asection *sgotfixup; - /* A pointer to the .plt section. */ - asection *splt; - /* A pointer to the .rel.plt section. */ - asection *spltrel; - /* GOT base offset. */ - bfd_vma got0; - /* Location of the first non-lazy PLT entry, i.e., the number of - bytes taken by lazy PLT entries. */ - bfd_vma plt0; - /* A hash table holding information about which symbols were - referenced with which PIC-related relocations. */ - struct htab *relocs_info; -}; - -/* Get the Blackfin ELF linker hash table from a link_info structure. */ - -#define bfinfdpic_hash_table(info) \ - ((struct bfinfdpic_elf_link_hash_table *) ((info)->hash)) - -#define bfinfdpic_got_section(info) \ - (bfinfdpic_hash_table (info)->sgot) -#define bfinfdpic_gotrel_section(info) \ - (bfinfdpic_hash_table (info)->sgotrel) -#define bfinfdpic_gotfixup_section(info) \ - (bfinfdpic_hash_table (info)->sgotfixup) -#define bfinfdpic_plt_section(info) \ - (bfinfdpic_hash_table (info)->splt) -#define bfinfdpic_pltrel_section(info) \ - (bfinfdpic_hash_table (info)->spltrel) -#define bfinfdpic_relocs_info(info) \ - (bfinfdpic_hash_table (info)->relocs_info) -#define bfinfdpic_got_initial_offset(info) \ - (bfinfdpic_hash_table (info)->got0) -#define bfinfdpic_plt_initial_offset(info) \ - (bfinfdpic_hash_table (info)->plt0) - -/* Create a Blackfin ELF linker hash table. */ + asection *srelgot; + if (info->relocatable) + return TRUE; -static struct bfd_link_hash_table * -bfinfdpic_elf_link_hash_table_create (bfd *abfd) -{ - struct bfinfdpic_elf_link_hash_table *ret; - bfd_size_type amt = sizeof (struct bfinfdpic_elf_link_hash_table); + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); - ret = bfd_zalloc (abfd, amt); - if (ret == NULL) - return NULL; + sgot = NULL; + srelgot = NULL; - if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, - _bfd_elf_link_hash_newfunc, - sizeof (struct elf_link_hash_entry))) + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) { - free (ret); - return NULL; - } - - return &ret->elf.root; -} + unsigned long r_symndx; + struct elf_link_hash_entry *h; -/* Decide whether a reference to a symbol can be resolved locally or - not. If the symbol is protected, we want the local address, but - its function descriptor must be assigned by the dynamic linker. */ -#define BFINFDPIC_SYM_LOCAL(INFO, H) \ - (_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \ - || ! elf_hash_table (INFO)->dynamic_sections_created) -#define BFINFDPIC_FUNCDESC_LOCAL(INFO, H) \ - ((H)->dynindx == -1 || ! elf_hash_table (INFO)->dynamic_sections_created) + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; -/* This structure collects information on what kind of GOT, PLT or - function descriptors are required by relocations that reference a - certain symbol. */ -struct bfinfdpic_relocs_info -{ - /* The index of the symbol, as stored in the relocation r_info, if - we have a local symbol; -1 otherwise. */ - long symndx; - union - { - /* The input bfd in which the symbol is defined, if it's a local - symbol. */ - bfd *abfd; - /* If symndx == -1, the hash table entry corresponding to a global - symbol (even if it turns out to bind locally, in which case it - should ideally be replaced with section's symndx + addend). */ - struct elf_link_hash_entry *h; - } d; - /* The addend of the relocation that references the symbol. */ - bfd_vma addend; + switch (ELF32_R_TYPE (rel->r_info)) + { + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_BFIN_GNU_VTINHERIT: + if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) + return FALSE; + break; - /* The fields above are used to identify an entry. The fields below - contain information on how an entry is used and, later on, which - locations it was assigned. */ - /* The following 2 fields record whether the symbol+addend above was - ever referenced with a GOT relocation. The 17M4 suffix indicates a - GOT17M4 relocation; hilo is used for GOTLO/GOTHI pairs. */ - unsigned got17m4:1; - unsigned gothilo:1; - /* Whether a FUNCDESC relocation references symbol+addend. */ - unsigned fd:1; - /* Whether a FUNCDESC_GOT relocation references symbol+addend. */ - unsigned fdgot17m4:1; - unsigned fdgothilo:1; - /* Whether a FUNCDESC_GOTOFF relocation references symbol+addend. */ - unsigned fdgoff17m4:1; - unsigned fdgoffhilo:1; - /* Whether symbol+addend is referenced with GOTOFF17M4, GOTOFFLO or - GOTOFFHI relocations. The addend doesn't really matter, since we - envision that this will only be used to check whether the symbol - is mapped to the same segment as the got. */ - unsigned gotoff:1; - /* Whether symbol+addend is referenced by a LABEL24 relocation. */ - unsigned call:1; - /* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE - relocation. */ - unsigned sym:1; - /* Whether we need a PLT entry for a symbol. Should be implied by - something like: - (call && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h)) */ - unsigned plt:1; - /* Whether a function descriptor should be created in this link unit - for symbol+addend. Should be implied by something like: - (plt || fdgotoff17m4 || fdgotofflohi - || ((fd || fdgot17m4 || fdgothilo) - && (symndx != -1 || BFINFDPIC_FUNCDESC_LOCAL (info, d.h)))) */ - unsigned privfd:1; - /* Whether a lazy PLT entry is needed for this symbol+addend. - Should be implied by something like: - (privfd && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h) - && ! (info->flags & DF_BIND_NOW)) */ - unsigned lazyplt:1; - /* Whether we've already emitted GOT relocations and PLT entries as - needed for this symbol. */ - unsigned done:1; + /* This relocation describes which C++ vtable entries + are actually used. Record for later use during GC. */ + case R_BFIN_GNU_VTENTRY: + BFD_ASSERT (h != NULL); + if (h != NULL + && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return FALSE; + break; - /* The number of R_byte4_data, R_BFIN_FUNCDESC and R_BFIN_FUNCDESC_VALUE - relocations referencing the symbol. */ - unsigned relocs32, relocsfd, relocsfdv; + case R_got: + if (h != NULL + && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) + break; + /* Fall through. */ - /* The number of .rofixups entries and dynamic relocations allocated - for this symbol, minus any that might have already been used. */ - unsigned fixups, dynrelocs; + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = abfd; + if (!_bfd_elf_create_got_section (dynobj, info)) + return FALSE; + } - /* The offsets of the GOT entries assigned to symbol+addend, to the - function descriptor's address, and to a function descriptor, - respectively. Should be zero if unassigned. The offsets are - counted from the value that will be assigned to the PIC register, - not from the beginning of the .got section. */ - bfd_signed_vma got_entry, fdgot_entry, fd_entry; - /* The offsets of the PLT entries assigned to symbol+addend, - non-lazy and lazy, respectively. If unassigned, should be - (bfd_vma)-1. */ - bfd_vma plt_entry, lzplt_entry; -}; + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } -/* Compute a hash with the key fields of an bfinfdpic_relocs_info entry. */ -static hashval_t -bfinfdpic_relocs_info_hash (const void *entry_) -{ - const struct bfinfdpic_relocs_info *entry = entry_; + if (srelgot == NULL && (h != NULL || info->shared)) + { + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + if (srelgot == NULL) + { + flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS + | SEC_IN_MEMORY | SEC_LINKER_CREATED + | SEC_READONLY); + srelgot = bfd_make_section_with_flags (dynobj, ".rela.got", + flags); + if (srelgot == NULL + || !bfd_set_section_alignment (dynobj, srelgot, 2)) + return FALSE; + } + } - return (entry->symndx == -1 - ? (long) entry->d.h->root.root.hash - : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend; -} + if (h != NULL) + { + if (h->got.refcount == 0) + { + /* Make sure this symbol is output as a dynamic symbol. */ + if (h->dynindx == -1 && !h->forced_local) + { + if (!bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; + } -/* Test whether the key fields of two bfinfdpic_relocs_info entries are - identical. */ -static int -bfinfdpic_relocs_info_eq (const void *entry1, const void *entry2) -{ - const struct bfinfdpic_relocs_info *e1 = entry1; - const struct bfinfdpic_relocs_info *e2 = entry2; + /* Allocate space in the .got section. */ + sgot->size += 4; + /* Allocate relocation space. */ + srelgot->size += sizeof (Elf32_External_Rela); + } + h->got.refcount++; + } + else + { + /* This is a global offset table entry for a local symbol. */ + if (local_got_refcounts == NULL) + { + bfd_size_type size; - return e1->symndx == e2->symndx && e1->addend == e2->addend - && (e1->symndx == -1 ? e1->d.h == e2->d.h : e1->d.abfd == e2->d.abfd); + size = symtab_hdr->sh_info; + size *= sizeof (bfd_signed_vma); + local_got_refcounts = ((bfd_signed_vma *) + bfd_zalloc (abfd, size)); + if (local_got_refcounts == NULL) + return FALSE; + elf_local_got_refcounts (abfd) = local_got_refcounts; + } + if (local_got_refcounts[r_symndx] == 0) + { + sgot->size += 4; + if (info->shared) + { + /* If we are generating a shared object, we need to + output a R_68K_RELATIVE reloc so that the dynamic + linker can adjust this GOT entry. */ + srelgot->size += sizeof (Elf32_External_Rela); + } + } + local_got_refcounts[r_symndx]++; + } + break; + + default: + break; + } + } + + return TRUE; } -/* Find or create an entry in a hash table HT that matches the key - fields of the given ENTRY. If it's not found, memory for a new - entry is allocated in ABFD's obstack. */ -static struct bfinfdpic_relocs_info * -bfinfdpic_relocs_info_find (struct htab *ht, - bfd *abfd, - const struct bfinfdpic_relocs_info *entry, - enum insert_option insert) +static enum elf_reloc_type_class +elf32_bfin_reloc_type_class (const Elf_Internal_Rela * rela) { - struct bfinfdpic_relocs_info **loc = - (struct bfinfdpic_relocs_info **) htab_find_slot (ht, entry, insert); + switch ((int) ELF32_R_TYPE (rela->r_info)) + { + default: + return reloc_class_normal; + } +} + +static bfd_reloc_status_type +bfin_final_link_relocate (Elf_Internal_Rela *rel, reloc_howto_type *howto, + bfd *input_bfd, asection *input_section, + bfd_byte *contents, bfd_vma address, + bfd_vma value, bfd_vma addend) +{ + int r_type = ELF32_R_TYPE (rel->r_info); - if (! loc) - return NULL; + if (r_type == R_pcrel24 || r_type == R_pcrel24_jump_l) + { + bfd_reloc_status_type r = bfd_reloc_ok; + bfd_vma x; - if (*loc) - return *loc; + if (address > bfd_get_section_limit (input_bfd, input_section)) + return bfd_reloc_outofrange; - *loc = bfd_zalloc (abfd, sizeof (**loc)); + value += addend; - if (! *loc) - return *loc; + /* Perform usual pc-relative correction. */ + value -= input_section->output_section->vma + input_section->output_offset; + value -= address; - (*loc)->symndx = entry->symndx; - (*loc)->d = entry->d; - (*loc)->addend = entry->addend; - (*loc)->plt_entry = (bfd_vma)-1; - (*loc)->lzplt_entry = (bfd_vma)-1; + /* We are getting reloc_entry->address 2 byte off from + the start of instruction. Assuming absolute postion + of the reloc data. But, following code had been written assuming + reloc address is starting at begining of instruction. + To compensate that I have increased the value of + relocation by 1 (effectively 2) and used the addr -2 instead of addr. */ - return *loc; -} + value += 2; + address -= 2; -/* Obtain the address of the entry in HT associated with H's symbol + - addend, creating a new entry if none existed. ABFD is only used - for memory allocation purposes. */ -inline static struct bfinfdpic_relocs_info * -bfinfdpic_relocs_info_for_global (struct htab *ht, - bfd *abfd, - struct elf_link_hash_entry *h, - bfd_vma addend, - enum insert_option insert) -{ - struct bfinfdpic_relocs_info entry; + if ((value & 0xFF000000) != 0 + && (value & 0xFF000000) != 0xFF000000) + r = bfd_reloc_overflow; - entry.symndx = -1; - entry.d.h = h; - entry.addend = addend; + value >>= 1; - return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); -} + x = bfd_get_16 (input_bfd, contents + address); + x = (x & 0xff00) | ((value >> 16) & 0xff); + bfd_put_16 (input_bfd, x, contents + address); -/* Obtain the address of the entry in HT associated with the SYMNDXth - local symbol of the input bfd ABFD, plus the addend, creating a new - entry if none existed. */ -inline static struct bfinfdpic_relocs_info * -bfinfdpic_relocs_info_for_local (struct htab *ht, - bfd *abfd, - long symndx, - bfd_vma addend, - enum insert_option insert) -{ - struct bfinfdpic_relocs_info entry; + x = bfd_get_16 (input_bfd, contents + address + 2); + x = value & 0xFFFF; + bfd_put_16 (input_bfd, x, contents + address + 2); + return r; + } - entry.symndx = symndx; - entry.d.abfd = abfd; - entry.addend = addend; + return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, + rel->r_offset, value, addend); - return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); } -/* Merge fields set by check_relocs() of two entries that end up being - mapped to the same (presumably global) symbol. */ - -inline static void -bfinfdpic_pic_merge_early_relocs_info (struct bfinfdpic_relocs_info *e2, - struct bfinfdpic_relocs_info const *e1) +static bfd_boolean +bfin_relocate_section (bfd * output_bfd, + struct bfd_link_info *info, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * relocs, + Elf_Internal_Sym * local_syms, + asection ** local_sections) { - e2->got17m4 |= e1->got17m4; - e2->gothilo |= e1->gothilo; - e2->fd |= e1->fd; - e2->fdgot17m4 |= e1->fdgot17m4; - e2->fdgothilo |= e1->fdgothilo; - e2->fdgoff17m4 |= e1->fdgoff17m4; - e2->fdgoffhilo |= e1->fdgoffhilo; - e2->gotoff |= e1->gotoff; - e2->call |= e1->call; - e2->sym |= e1->sym; -} + bfd *dynobj; + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_vma *local_got_offsets; + asection *sgot; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + int i = 0; -/* Every block of 65535 lazy PLT entries shares a single call to the - resolver, inserted in the 32768th lazy PLT entry (i.e., entry # - 32767, counting from 0). All other lazy PLT entries branch to it - in a single instruction. */ + dynobj = elf_hash_table (info)->dynobj; + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + local_got_offsets = elf_local_got_offsets (input_bfd); -#define LZPLT_RESOLVER_EXTRA 10 -#define LZPLT_NORMAL_SIZE 6 -#define LZPLT_ENTRIES 1362 + sgot = NULL; -#define BFINFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) LZPLT_NORMAL_SIZE * LZPLT_ENTRIES + LZPLT_RESOLVER_EXTRA) -#define BFINFDPIC_LZPLT_RESOLV_LOC (LZPLT_NORMAL_SIZE * LZPLT_ENTRIES / 2) + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; rel++, i++) + { + int r_type; + reloc_howto_type *howto; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + asection *sec; + bfd_vma relocation = 0; + bfd_boolean unresolved_reloc; + bfd_reloc_status_type r; + bfd_vma address; -/* Add a dynamic relocation to the SRELOC section. */ + r_type = ELF32_R_TYPE (rel->r_info); + if (r_type < 0 || r_type >= 243) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } -inline static bfd_vma -_bfinfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset, - int reloc_type, long dynindx, bfd_vma addend, - struct bfinfdpic_relocs_info *entry) -{ - Elf_Internal_Rela outrel; - bfd_vma reloc_offset; + if (r_type == R_BFIN_GNU_VTENTRY + || r_type == R_BFIN_GNU_VTINHERIT) + continue; - outrel.r_offset = offset; - outrel.r_info = ELF32_R_INFO (dynindx, reloc_type); - outrel.r_addend = addend; + howto = bfin_reloc_type_lookup (input_bfd, r_type); + if (howto == NULL) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + r_symndx = ELF32_R_SYM (rel->r_info); - reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rel); - BFD_ASSERT (reloc_offset < sreloc->size); - bfd_elf32_swap_reloc_out (output_bfd, &outrel, - sreloc->contents + reloc_offset); - sreloc->reloc_count++; + h = NULL; + sym = NULL; + sec = NULL; + unresolved_reloc = FALSE; - /* If the entry's index is zero, this relocation was probably to a - linkonce section that got discarded. We reserved a dynamic - relocation, but it was for another entry than the one we got at - the time of emitting the relocation. Unfortunately there's no - simple way for us to catch this situation, since the relocation - is cleared right before calling relocate_section, at which point - we no longer know what the relocation used to point to. */ - if (entry->symndx) - { - BFD_ASSERT (entry->dynrelocs > 0); - entry->dynrelocs--; - } + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + } + else + { + bfd_boolean warned; - return reloc_offset; -} + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, + h, sec, relocation, + unresolved_reloc, warned); + } -/* Add a fixup to the ROFIXUP section. */ + if (sec != NULL && elf_discarded_section (sec)) + { + /* For relocs against symbols from removed linkonce sections, + or sections discarded by a linker script, we just want the + section contents zeroed. Avoid any special processing. */ + _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); + rel->r_info = 0; + rel->r_addend = 0; + continue; + } -static bfd_vma -_bfinfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset, - struct bfinfdpic_relocs_info *entry) -{ - bfd_vma fixup_offset; + if (info->relocatable) + continue; - if (rofixup->flags & SEC_EXCLUDE) - return -1; + address = rel->r_offset; - fixup_offset = rofixup->reloc_count * 4; - if (rofixup->contents) - { - BFD_ASSERT (fixup_offset < rofixup->size); - bfd_put_32 (output_bfd, offset, rofixup->contents + fixup_offset); - } - rofixup->reloc_count++; + /* Then, process normally. */ + switch (r_type) + { + case R_BFIN_GNU_VTINHERIT: + case R_BFIN_GNU_VTENTRY: + return bfd_reloc_ok; - if (entry && entry->symndx) - { - /* See discussion about symndx == 0 in _bfinfdpic_add_dyn_reloc - above. */ - BFD_ASSERT (entry->fixups > 0); - entry->fixups--; - } + case R_got: + /* Relocation is to the address of the entry for this symbol + in the global offset table. */ + if (h != NULL + && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) + goto do_default; + /* Fall through. */ + /* Relocation is the offset of the entry for this symbol in + the global offset table. */ - return fixup_offset; -} + { + bfd_vma off; -/* Find the segment number in which OSEC, and output section, is - located. */ + if (dynobj == NULL) + { + /* Create the .got section. */ + elf_hash_table (info)->dynobj = dynobj = output_bfd; + if (!_bfd_elf_create_got_section (dynobj, info)) + return FALSE; + } -static unsigned -_bfinfdpic_osec_to_segment (bfd *output_bfd, asection *osec) -{ - struct elf_segment_map *m; - Elf_Internal_Phdr *p; + if (sgot == NULL) + { + sgot = bfd_get_section_by_name (dynobj, ".got"); + BFD_ASSERT (sgot != NULL); + } - /* Find the segment that contains the output_section. */ - for (m = elf_tdata (output_bfd)->segment_map, - p = elf_tdata (output_bfd)->phdr; - m != NULL; - m = m->next, p++) - { - int i; + if (h != NULL) + { + bfd_boolean dyn; - for (i = m->count - 1; i >= 0; i--) - if (m->sections[i] == osec) - break; + off = h->got.offset; + BFD_ASSERT (off != (bfd_vma) - 1); + dyn = elf_hash_table (info)->dynamic_sections_created; - if (i >= 0) - break; - } + if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) + || (info->shared + && (info->symbolic + || h->dynindx == -1 + || h->forced_local) + && h->def_regular)) + { + /* This is actually a static link, or it is a + -Bsymbolic link and the symbol is defined + locally, or the symbol was forced to be local + because of a version file.. We must initialize + this entry in the global offset table. Since + the offset must always be a multiple of 4, we + use the least significant bit to record whether + we have initialized it already. - return p - elf_tdata (output_bfd)->phdr; -} + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, + sgot->contents + off); + h->got.offset |= 1; + } + } + else + unresolved_reloc = FALSE; + } + else + { + BFD_ASSERT (local_got_offsets != NULL); + off = local_got_offsets[r_symndx]; + BFD_ASSERT (off != (bfd_vma) - 1); -inline static bfd_boolean -_bfinfdpic_osec_readonly_p (bfd *output_bfd, asection *osec) -{ - unsigned seg = _bfinfdpic_osec_to_segment (output_bfd, osec); + /* The offset must always be a multiple of 4. We use + the least significant bit to record whether we have + already generated the necessary reloc. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_32 (output_bfd, relocation, sgot->contents + off); - return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W); -} + if (info->shared) + { + asection *s; + Elf_Internal_Rela outrel; + bfd_byte *loc; -/* Generate relocations for GOT entries, function descriptors, and - code for PLT and lazy PLT entries. */ + s = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (s != NULL); -inline static bfd_boolean -_bfinfdpic_emit_got_relocs_plt_entries (struct bfinfdpic_relocs_info *entry, - bfd *output_bfd, - struct bfd_link_info *info, - asection *sec, - Elf_Internal_Sym *sym, - bfd_vma addend) + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + off); + outrel.r_info = + ELF32_R_INFO (0, R_pcrel24); + outrel.r_addend = relocation; + loc = s->contents; + loc += + s->reloc_count++ * sizeof (Elf32_External_Rela); + bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + } -{ - bfd_vma fd_lazy_rel_offset = (bfd_vma)-1; - int dynindx = -1; + local_got_offsets[r_symndx] |= 1; + } + } - if (entry->done) - return TRUE; - entry->done = 1; + relocation = sgot->output_offset + off; + rel->r_addend = 0; + /* bfin : preg = [preg + 17bitdiv4offset] relocation is div by 4. */ + relocation /= 4; + } + goto do_default; - if (entry->got_entry || entry->fdgot_entry || entry->fd_entry) - { - /* If the symbol is dynamic, consider it for dynamic - relocations, otherwise decay to section + offset. */ - if (entry->symndx == -1 && entry->d.h->dynindx != -1) - dynindx = entry->d.h->dynindx; - else - { - if (sec->output_section - && ! bfd_is_abs_section (sec->output_section) - && ! bfd_is_und_section (sec->output_section)) - dynindx = elf_section_data (sec->output_section)->dynindx; - else - dynindx = 0; - } - } + default: + do_default: + r = bfin_final_link_relocate (rel, howto, input_bfd, input_section, + contents, address, + relocation, rel->r_addend); - /* Generate relocation for GOT entry pointing to the symbol. */ - if (entry->got_entry) - { - int idx = dynindx; - bfd_vma ad = addend; + break; + } - /* If the symbol is dynamic but binds locally, use - section+offset. */ - if (sec && (entry->symndx != -1 - || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + /* Dynamic relocs are not propagated for SEC_DEBUGGING sections + because such sections are not SEC_ALLOC and thus ld.so will + not process them. */ + if (unresolved_reloc + && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic)) { - if (entry->symndx == -1) - ad += entry->d.h->root.u.def.value; - else - ad += sym->st_value; - ad += sec->output_offset; - if (sec->output_section && elf_section_data (sec->output_section)) - idx = elf_section_data (sec->output_section)->dynindx; - else - idx = 0; + (*_bfd_error_handler) + (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"), + input_bfd, + input_section, (long) rel->r_offset, h->root.root.string); + return FALSE; } - /* If we're linking an executable at a fixed address, we can - omit the dynamic relocation as long as the symbol is local to - this module. */ - if (info->executable && !info->pie - && (entry->symndx != -1 - || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + if (r != bfd_reloc_ok) { - if (sec) - ad += sec->output_section->vma; - if (entry->symndx != -1 - || entry->d.h->root.type != bfd_link_hash_undefweak) - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section (info), - bfinfdpic_got_section (info)->output_section - ->vma - + bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info) - + entry->got_entry, entry); - } - else - _bfinfdpic_add_dyn_reloc (output_bfd, bfinfdpic_gotrel_section (info), - _bfd_elf_section_offset - (output_bfd, info, - bfinfdpic_got_section (info), - bfinfdpic_got_initial_offset (info) - + entry->got_entry) - + bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info)->output_offset, - R_byte4_data, idx, ad, entry); + const char *name; - bfd_put_32 (output_bfd, ad, - bfinfdpic_got_section (info)->contents - + bfinfdpic_got_initial_offset (info) - + entry->got_entry); - } + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (name == NULL) + return FALSE; + if (*name == '\0') + name = bfd_section_name (input_bfd, sec); + } - /* Generate relocation for GOT entry pointing to a canonical - function descriptor. */ - if (entry->fdgot_entry) - { - int reloc, idx; - bfd_vma ad = 0; - - if (! (entry->symndx == -1 - && entry->d.h->root.type == bfd_link_hash_undefweak - && BFINFDPIC_SYM_LOCAL (info, entry->d.h))) - { - /* If the symbol is dynamic and there may be dynamic symbol - resolution because we are, or are linked with, a shared - library, emit a FUNCDESC relocation such that the dynamic - linker will allocate the function descriptor. If the - symbol needs a non-local function descriptor but binds - locally (e.g., its visibility is protected, emit a - dynamic relocation decayed to section+offset. */ - if (entry->symndx == -1 - && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h) - && BFINFDPIC_SYM_LOCAL (info, entry->d.h) - && !(info->executable && !info->pie)) - { - reloc = R_BFIN_FUNCDESC; - idx = elf_section_data (entry->d.h->root.u.def.section - ->output_section)->dynindx; - ad = entry->d.h->root.u.def.section->output_offset - + entry->d.h->root.u.def.value; - } - else if (entry->symndx == -1 - && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)) + if (r == bfd_reloc_overflow) { - reloc = R_BFIN_FUNCDESC; - idx = dynindx; - ad = addend; - if (ad) + if (!(info->callbacks->reloc_overflow + (info, (h ? &h->root : NULL), name, howto->name, + (bfd_vma) 0, input_bfd, input_section, rel->r_offset))) return FALSE; } else { - /* Otherwise, we know we have a private function descriptor, - so reference it directly. */ - if (elf_hash_table (info)->dynamic_sections_created) - BFD_ASSERT (entry->privfd); - reloc = R_byte4_data; - idx = elf_section_data (bfinfdpic_got_section (info) - ->output_section)->dynindx; - ad = bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info) + entry->fd_entry; - } - - /* If there is room for dynamic symbol resolution, emit the - dynamic relocation. However, if we're linking an - executable at a fixed location, we won't have emitted a - dynamic symbol entry for the got section, so idx will be - zero, which means we can and should compute the address - of the private descriptor ourselves. */ - if (info->executable && !info->pie - && (entry->symndx != -1 - || BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h))) - { - ad += bfinfdpic_got_section (info)->output_section->vma; - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section (info), - bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info) - ->output_offset - + bfinfdpic_got_initial_offset (info) - + entry->fdgot_entry, entry); + (*_bfd_error_handler) + (_("%B(%A+0x%lx): reloc against `%s': error %d"), + input_bfd, input_section, + (long) rel->r_offset, name, (int) r); + return FALSE; } - else - _bfinfdpic_add_dyn_reloc (output_bfd, - bfinfdpic_gotrel_section (info), - _bfd_elf_section_offset - (output_bfd, info, - bfinfdpic_got_section (info), - bfinfdpic_got_initial_offset (info) - + entry->fdgot_entry) - + bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info) - ->output_offset, - reloc, idx, ad, entry); } - - bfd_put_32 (output_bfd, ad, - bfinfdpic_got_section (info)->contents - + bfinfdpic_got_initial_offset (info) - + entry->fdgot_entry); } - /* Generate relocation to fill in a private function descriptor in - the GOT. */ - if (entry->fd_entry) - { - int idx = dynindx; - bfd_vma ad = addend; - bfd_vma ofst; - long lowword, highword; + return TRUE; +} - /* If the symbol is dynamic but binds locally, use - section+offset. */ - if (sec && (entry->symndx != -1 - || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) - { - if (entry->symndx == -1) - ad += entry->d.h->root.u.def.value; - else - ad += sym->st_value; - ad += sec->output_offset; - if (sec->output_section && elf_section_data (sec->output_section)) - idx = elf_section_data (sec->output_section)->dynindx; - else - idx = 0; - } +static asection * +bfin_gc_mark_hook (asection * sec, + struct bfd_link_info *info, + Elf_Internal_Rela * rel, + struct elf_link_hash_entry *h, + Elf_Internal_Sym * sym) +{ + if (h != NULL) + switch (ELF32_R_TYPE (rel->r_info)) + { + case R_BFIN_GNU_VTINHERIT: + case R_BFIN_GNU_VTENTRY: + return NULL; + } - /* If we're linking an executable at a fixed address, we can - omit the dynamic relocation as long as the symbol is local to - this module. */ - if (info->executable && !info->pie - && (entry->symndx != -1 || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) - { - if (sec) - ad += sec->output_section->vma; - ofst = 0; - if (entry->symndx != -1 - || entry->d.h->root.type != bfd_link_hash_undefweak) - { - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section (info), - bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info) - ->output_offset - + bfinfdpic_got_initial_offset (info) - + entry->fd_entry, entry); - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section (info), - bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info) - ->output_offset - + bfinfdpic_got_initial_offset (info) - + entry->fd_entry + 4, entry); - } - } - else - { - ofst - = _bfinfdpic_add_dyn_reloc (output_bfd, - entry->lazyplt - ? bfinfdpic_pltrel_section (info) - : bfinfdpic_gotrel_section (info), - _bfd_elf_section_offset - (output_bfd, info, - bfinfdpic_got_section (info), - bfinfdpic_got_initial_offset (info) - + entry->fd_entry) - + bfinfdpic_got_section (info) - ->output_section->vma - + bfinfdpic_got_section (info) - ->output_offset, - R_BFIN_FUNCDESC_VALUE, idx, ad, entry); - } + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); +} - /* If we've omitted the dynamic relocation, just emit the fixed - addresses of the symbol and of the local GOT base offset. */ - if (info->executable && !info->pie && sec && sec->output_section) - { - lowword = ad; - highword = bfinfdpic_got_section (info)->output_section->vma - + bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info); - } - else if (entry->lazyplt) - { - if (ad) - return FALSE; +/* Update the got entry reference counts for the section being removed. */ - fd_lazy_rel_offset = ofst; +static bfd_boolean +bfin_gc_sweep_hook (bfd * abfd, + struct bfd_link_info *info, + asection * sec, + const Elf_Internal_Rela * relocs) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + bfd_signed_vma *local_got_refcounts; + const Elf_Internal_Rela *rel, *relend; + bfd *dynobj; + asection *sgot; + asection *srelgot; - /* A function descriptor used for lazy or local resolving is - initialized such that its high word contains the output - section index in which the PLT entries are located, and - the low word contains the address of the lazy PLT entry - entry point, that must be within the memory region - assigned to that section. */ - lowword = entry->lzplt_entry + 4 - + bfinfdpic_plt_section (info)->output_offset - + bfinfdpic_plt_section (info)->output_section->vma; - highword = _bfinfdpic_osec_to_segment - (output_bfd, bfinfdpic_plt_section (info)->output_section); - } - else - { - /* A function descriptor for a local function gets the index - of the section. For a non-local function, it's - disregarded. */ - lowword = ad; - if (entry->symndx == -1 && entry->d.h->dynindx != -1 - && entry->d.h->dynindx == idx) - highword = 0; - else - highword = _bfinfdpic_osec_to_segment - (output_bfd, sec->output_section); - } + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + return TRUE; - bfd_put_32 (output_bfd, lowword, - bfinfdpic_got_section (info)->contents - + bfinfdpic_got_initial_offset (info) - + entry->fd_entry); - bfd_put_32 (output_bfd, highword, - bfinfdpic_got_section (info)->contents - + bfinfdpic_got_initial_offset (info) - + entry->fd_entry + 4); - } + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (abfd); + local_got_refcounts = elf_local_got_refcounts (abfd); - /* Generate code for the PLT entry. */ - if (entry->plt_entry != (bfd_vma) -1) - { - bfd_byte *plt_code = bfinfdpic_plt_section (info)->contents - + entry->plt_entry; + sgot = bfd_get_section_by_name (dynobj, ".got"); + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (entry->fd_entry); - - /* Figure out what kind of PLT entry we need, depending on the - location of the function descriptor within the GOT. */ - if (entry->fd_entry >= -(1 << (18 - 1)) - && entry->fd_entry + 4 < (1 << (18 - 1))) - { - /* P1 = [P3 + fd_entry]; P3 = [P3 + fd_entry + 4] */ - bfd_put_32 (output_bfd, - 0xe519 | ((entry->fd_entry << 14) & 0xFFFF0000), - plt_code); - bfd_put_32 (output_bfd, - 0xe51b | (((entry->fd_entry + 4) << 14) & 0xFFFF0000), - plt_code + 4); - plt_code += 8; - } - else - { - /* P1.L = fd_entry; P1.H = fd_entry; - P3 = P3 + P1; - P1 = [P3]; - P3 = [P3 + 4]; */ - bfd_put_32 (output_bfd, - 0xe109 | (entry->fd_entry << 16), - plt_code); - bfd_put_32 (output_bfd, - 0xe149 | (entry->fd_entry & 0xFFFF0000), - plt_code + 4); - bfd_put_16 (output_bfd, 0x5ad9, plt_code + 8); - bfd_put_16 (output_bfd, 0x9159, plt_code + 10); - bfd_put_16 (output_bfd, 0xac5b, plt_code + 12); - plt_code += 14; - } - /* JUMP (P1) */ - bfd_put_16 (output_bfd, 0x0051, plt_code); - } - - /* Generate code for the lazy PLT entry. */ - if (entry->lzplt_entry != (bfd_vma) -1) - { - bfd_byte *lzplt_code = bfinfdpic_plt_section (info)->contents - + entry->lzplt_entry; - bfd_vma resolverStub_addr; - - bfd_put_32 (output_bfd, fd_lazy_rel_offset, lzplt_code); - lzplt_code += 4; - - resolverStub_addr = entry->lzplt_entry / BFINFDPIC_LZPLT_BLOCK_SIZE - * BFINFDPIC_LZPLT_BLOCK_SIZE + BFINFDPIC_LZPLT_RESOLV_LOC; - if (resolverStub_addr >= bfinfdpic_plt_initial_offset (info)) - resolverStub_addr = bfinfdpic_plt_initial_offset (info) - LZPLT_NORMAL_SIZE - LZPLT_RESOLVER_EXTRA; - - if (entry->lzplt_entry == resolverStub_addr) - { - /* This is a lazy PLT entry that includes a resolver call. - P2 = [P3]; - R3 = [P3 + 4]; - JUMP (P2); */ - bfd_put_32 (output_bfd, - 0xa05b915a, - lzplt_code); - bfd_put_16 (output_bfd, 0x0052, lzplt_code + 4); - } - else - { - /* JUMP.S resolverStub */ - bfd_put_16 (output_bfd, - 0x2000 - | (((resolverStub_addr - entry->lzplt_entry) - / 2) & (((bfd_vma)1 << 12) - 1)), - lzplt_code); - } - } - - return TRUE; -} - - -/* Look through the relocs for a section during the first phase, and - allocate space in the global offset table or procedure linkage - table. */ - -static bfd_boolean -bfin_check_relocs (bfd * abfd, - struct bfd_link_info *info, - asection *sec, - const Elf_Internal_Rela *relocs) -{ - bfd *dynobj; - Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes; - bfd_signed_vma *local_got_refcounts; - const Elf_Internal_Rela *rel; - const Elf_Internal_Rela *rel_end; - asection *sgot; - asection *srelgot; - asection *sreloc; - if (info->relocatable) - return TRUE; - - dynobj = elf_hash_table (info)->dynobj; - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - sym_hashes = elf_sym_hashes (abfd); - local_got_refcounts = elf_local_got_refcounts (abfd); - - sgot = NULL; - srelgot = NULL; - sreloc = NULL; - - rel_end = relocs + sec->reloc_count; - for (rel = relocs; rel < rel_end; rel++) - { - unsigned long r_symndx; - struct elf_link_hash_entry *h; - - r_symndx = ELF32_R_SYM (rel->r_info); - if (r_symndx < symtab_hdr->sh_info) - h = NULL; - else - h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + relend = relocs + sec->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + unsigned long r_symndx; + struct elf_link_hash_entry *h; switch (ELF32_R_TYPE (rel->r_info)) { - /* This relocation describes the C++ object vtable hierarchy. - Reconstruct it for later use during GC. */ - case R_BFIN_GNU_VTINHERIT: - if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) - return FALSE; - break; - - /* This relocation describes which C++ vtable entries - are actually used. Record for later use during GC. */ - case R_BFIN_GNU_VTENTRY: - if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) - return FALSE; - break; - case R_got: - if (h != NULL - && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) - break; - /* Fall through. */ - - if (dynobj == NULL) - { - /* Create the .got section. */ - elf_hash_table (info)->dynobj = dynobj = abfd; - if (!_bfd_elf_create_got_section (dynobj, info)) - return FALSE; - } - - if (sgot == NULL) - { - sgot = bfd_get_section_by_name (dynobj, ".got"); - BFD_ASSERT (sgot != NULL); - } - - if (srelgot == NULL && (h != NULL || info->shared)) - { - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - if (srelgot == NULL) - { - flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS - | SEC_IN_MEMORY | SEC_LINKER_CREATED - | SEC_READONLY); - srelgot = bfd_make_section_with_flags (dynobj, ".rela.got", - flags); - if (srelgot == NULL - || !bfd_set_section_alignment (dynobj, srelgot, 2)) - return FALSE; - } - } - - if (h != NULL) + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx >= symtab_hdr->sh_info) { - if (h->got.refcount == 0) + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + if (h->got.refcount > 0) { - /* Make sure this symbol is output as a dynamic symbol. */ - if (h->dynindx == -1 && !h->forced_local) + --h->got.refcount; + if (h->got.refcount == 0) { - if (!bfd_elf_link_record_dynamic_symbol (info, h)) - return FALSE; + /* We don't need the .got entry any more. */ + sgot->size -= 4; + srelgot->size -= sizeof (Elf32_External_Rela); } - - /* Allocate space in the .got section. */ - sgot->size += 4; - /* Allocate relocation space. */ - srelgot->size += sizeof (Elf32_External_Rela); } - h->got.refcount++; } - else + else if (local_got_refcounts != NULL) { - /* This is a global offset table entry for a local symbol. */ - if (local_got_refcounts == NULL) - { - bfd_size_type size; - - size = symtab_hdr->sh_info; - size *= sizeof (bfd_signed_vma); - local_got_refcounts = ((bfd_signed_vma *) - bfd_zalloc (abfd, size)); - if (local_got_refcounts == NULL) - return FALSE; - elf_local_got_refcounts (abfd) = local_got_refcounts; - } - if (local_got_refcounts[r_symndx] == 0) + if (local_got_refcounts[r_symndx] > 0) { - sgot->size += 4; - if (info->shared) + --local_got_refcounts[r_symndx]; + if (local_got_refcounts[r_symndx] == 0) { - /* If we are generating a shared object, we need to - output a R_68K_RELATIVE reloc so that the dynamic - linker can adjust this GOT entry. */ - srelgot->size += sizeof (Elf32_External_Rela); + /* We don't need the .got entry any more. */ + sgot->size -= 4; + if (info->shared) + srelgot->size -= sizeof (Elf32_External_Rela); } } - local_got_refcounts[r_symndx]++; } break; - default: break; } } - return TRUE; } + +extern const bfd_target bfd_elf32_bfinfdpic_vec; +#define IS_FDPIC(bfd) ((bfd)->xvec == &bfd_elf32_bfinfdpic_vec) -static enum elf_reloc_type_class -elf32_bfin_reloc_type_class (const Elf_Internal_Rela * rela) +/* An extension of the elf hash table data structure, containing some + additional Blackfin-specific data. */ +struct bfinfdpic_elf_link_hash_table { - switch ((int) ELF32_R_TYPE (rela->r_info)) - { - default: - return reloc_class_normal; - } -} - -/* Relocate an Blackfin ELF section. + struct elf_link_hash_table elf; - The RELOCATE_SECTION function is called by the new ELF backend linker - to handle the relocations for a section. + /* A pointer to the .got section. */ + asection *sgot; + /* A pointer to the .rel.got section. */ + asection *sgotrel; + /* A pointer to the .rofixup section. */ + asection *sgotfixup; + /* A pointer to the .plt section. */ + asection *splt; + /* A pointer to the .rel.plt section. */ + asection *spltrel; + /* GOT base offset. */ + bfd_vma got0; + /* Location of the first non-lazy PLT entry, i.e., the number of + bytes taken by lazy PLT entries. */ + bfd_vma plt0; + /* A hash table holding information about which symbols were + referenced with which PIC-related relocations. */ + struct htab *relocs_info; +}; - The relocs are always passed as Rela structures; if the section - actually uses Rel structures, the r_addend field will always be - zero. - - This function is responsible for adjusting the section contents as - necessary, and (if using Rela relocs and generating a relocatable - output file) adjusting the reloc addend as necessary. +/* Get the Blackfin ELF linker hash table from a link_info structure. */ - This function does not have to worry about setting the reloc - address or the reloc symbol index. +#define bfinfdpic_hash_table(info) \ + ((struct bfinfdpic_elf_link_hash_table *) ((info)->hash)) - LOCAL_SYMS is a pointer to the swapped in local symbols. +#define bfinfdpic_got_section(info) \ + (bfinfdpic_hash_table (info)->sgot) +#define bfinfdpic_gotrel_section(info) \ + (bfinfdpic_hash_table (info)->sgotrel) +#define bfinfdpic_gotfixup_section(info) \ + (bfinfdpic_hash_table (info)->sgotfixup) +#define bfinfdpic_plt_section(info) \ + (bfinfdpic_hash_table (info)->splt) +#define bfinfdpic_pltrel_section(info) \ + (bfinfdpic_hash_table (info)->spltrel) +#define bfinfdpic_relocs_info(info) \ + (bfinfdpic_hash_table (info)->relocs_info) +#define bfinfdpic_got_initial_offset(info) \ + (bfinfdpic_hash_table (info)->got0) +#define bfinfdpic_plt_initial_offset(info) \ + (bfinfdpic_hash_table (info)->plt0) - LOCAL_SECTIONS is an array giving the section in the input file - corresponding to the st_shndx field of each local symbol. +/* The name of the dynamic interpreter. This is put in the .interp + section. */ - The global hash table entry for the global symbols can be found - via elf_sym_hashes (input_bfd). +#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" - When generating relocatable output, this function must handle - STB_LOCAL/STT_SECTION symbols specially. The output symbol is - going to be the section symbol corresponding to the output - section, which means that the addend must be adjusted - accordingly. */ +#define DEFAULT_STACK_SIZE 0x20000 -static bfd_boolean -bfinfdpic_relocate_section (bfd * output_bfd, - struct bfd_link_info *info, - bfd * input_bfd, - asection * input_section, - bfd_byte * contents, - Elf_Internal_Rela * relocs, - Elf_Internal_Sym * local_syms, - asection ** local_sections) +/* This structure is used to collect the number of entries present in + each addressable range of the got. */ +struct _bfinfdpic_dynamic_got_info { - Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes; - Elf_Internal_Rela *rel; - Elf_Internal_Rela *relend; - unsigned isec_segment, got_segment, plt_segment, - check_segment[2]; - int silence_segment_error = !(info->shared || info->pie); + /* Several bits of information about the current link. */ + struct bfd_link_info *info; + /* Total size needed for GOT entries within the 18- or 32-bit + ranges. */ + bfd_vma got17m4, gothilo; + /* Total size needed for function descriptor entries within the 18- + or 32-bit ranges. */ + bfd_vma fd17m4, fdhilo; + /* Total size needed function descriptor entries referenced in PLT + entries, that would be profitable to place in offsets close to + the PIC register. */ + bfd_vma fdplt; + /* Total size needed by lazy PLT entries. */ + bfd_vma lzplt; + /* Number of relocations carried over from input object files. */ + unsigned long relocs; + /* Number of fixups introduced by relocations in input object files. */ + unsigned long fixups; +}; - if (info->relocatable) - return TRUE; +/* Create a Blackfin ELF linker hash table. */ - symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; - sym_hashes = elf_sym_hashes (input_bfd); - relend = relocs + input_section->reloc_count; +static struct bfd_link_hash_table * +bfinfdpic_elf_link_hash_table_create (bfd *abfd) +{ + struct bfinfdpic_elf_link_hash_table *ret; + bfd_size_type amt = sizeof (struct bfinfdpic_elf_link_hash_table); - isec_segment = _bfinfdpic_osec_to_segment (output_bfd, - input_section->output_section); - if (IS_FDPIC (output_bfd) && bfinfdpic_got_section (info)) - got_segment = _bfinfdpic_osec_to_segment (output_bfd, - bfinfdpic_got_section (info) - ->output_section); - else - got_segment = -1; - if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created) - plt_segment = _bfinfdpic_osec_to_segment (output_bfd, - bfinfdpic_plt_section (info) - ->output_section); - else - plt_segment = -1; + ret = bfd_zalloc (abfd, amt); + if (ret == NULL) + return NULL; - for (rel = relocs; rel < relend; rel ++) + if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, + _bfd_elf_link_hash_newfunc, + sizeof (struct elf_link_hash_entry))) { - reloc_howto_type *howto; - unsigned long r_symndx; - Elf_Internal_Sym *sym; - asection *sec; - struct elf_link_hash_entry *h; - bfd_vma relocation; - bfd_reloc_status_type r; - const char * name = NULL; - int r_type; - asection *osec; - struct bfinfdpic_relocs_info *picrel; - bfd_vma orig_addend = rel->r_addend; + free (ret); + return NULL; + } - r_type = ELF32_R_TYPE (rel->r_info); + return &ret->elf.root; +} - if (r_type == R_BFIN_GNU_VTINHERIT - || r_type == R_BFIN_GNU_VTENTRY) - continue; +/* Decide whether a reference to a symbol can be resolved locally or + not. If the symbol is protected, we want the local address, but + its function descriptor must be assigned by the dynamic linker. */ +#define BFINFDPIC_SYM_LOCAL(INFO, H) \ + (_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \ + || ! elf_hash_table (INFO)->dynamic_sections_created) +#define BFINFDPIC_FUNCDESC_LOCAL(INFO, H) \ + ((H)->dynindx == -1 || ! elf_hash_table (INFO)->dynamic_sections_created) - /* This is a final link. */ - r_symndx = ELF32_R_SYM (rel->r_info); - howto = bfin_reloc_type_lookup (input_bfd, r_type); - if (howto == NULL) - { - bfd_set_error (bfd_error_bad_value); - return FALSE; - } +/* This structure collects information on what kind of GOT, PLT or + function descriptors are required by relocations that reference a + certain symbol. */ +struct bfinfdpic_relocs_info +{ + /* The index of the symbol, as stored in the relocation r_info, if + we have a local symbol; -1 otherwise. */ + long symndx; + union + { + /* The input bfd in which the symbol is defined, if it's a local + symbol. */ + bfd *abfd; + /* If symndx == -1, the hash table entry corresponding to a global + symbol (even if it turns out to bind locally, in which case it + should ideally be replaced with section's symndx + addend). */ + struct elf_link_hash_entry *h; + } d; + /* The addend of the relocation that references the symbol. */ + bfd_vma addend; - h = NULL; - sym = NULL; - sec = NULL; + /* The fields above are used to identify an entry. The fields below + contain information on how an entry is used and, later on, which + locations it was assigned. */ + /* The following 2 fields record whether the symbol+addend above was + ever referenced with a GOT relocation. The 17M4 suffix indicates a + GOT17M4 relocation; hilo is used for GOTLO/GOTHI pairs. */ + unsigned got17m4; + unsigned gothilo; + /* Whether a FUNCDESC relocation references symbol+addend. */ + unsigned fd; + /* Whether a FUNCDESC_GOT relocation references symbol+addend. */ + unsigned fdgot17m4; + unsigned fdgothilo; + /* Whether a FUNCDESC_GOTOFF relocation references symbol+addend. */ + unsigned fdgoff17m4; + unsigned fdgoffhilo; + /* Whether symbol+addend is referenced with GOTOFF17M4, GOTOFFLO or + GOTOFFHI relocations. The addend doesn't really matter, since we + envision that this will only be used to check whether the symbol + is mapped to the same segment as the got. */ + unsigned gotoff; + /* Whether symbol+addend is referenced by a LABEL24 relocation. */ + unsigned call; + /* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE + relocation. */ + unsigned sym; + /* Whether we need a PLT entry for a symbol. Should be implied by + something like: + (call && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h)) */ + unsigned plt:1; + /* Whether a function descriptor should be created in this link unit + for symbol+addend. Should be implied by something like: + (plt || fdgotoff17m4 || fdgotofflohi + || ((fd || fdgot17m4 || fdgothilo) + && (symndx != -1 || BFINFDPIC_FUNCDESC_LOCAL (info, d.h)))) */ + unsigned privfd:1; + /* Whether a lazy PLT entry is needed for this symbol+addend. + Should be implied by something like: + (privfd && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h) + && ! (info->flags & DF_BIND_NOW)) */ + unsigned lazyplt:1; + /* Whether we've already emitted GOT relocations and PLT entries as + needed for this symbol. */ + unsigned done:1; - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - osec = sec = local_sections [r_symndx]; - relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + /* The number of R_byte4_data, R_BFIN_FUNCDESC and R_BFIN_FUNCDESC_VALUE + relocations referencing the symbol. */ + unsigned relocs32, relocsfd, relocsfdv; - name = bfd_elf_string_from_elf_section - (input_bfd, symtab_hdr->sh_link, sym->st_name); - name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name; - } - else - { - h = sym_hashes [r_symndx - symtab_hdr->sh_info]; + /* The number of .rofixups entries and dynamic relocations allocated + for this symbol, minus any that might have already been used. */ + unsigned fixups, dynrelocs; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + /* The offsets of the GOT entries assigned to symbol+addend, to the + function descriptor's address, and to a function descriptor, + respectively. Should be zero if unassigned. The offsets are + counted from the value that will be assigned to the PIC register, + not from the beginning of the .got section. */ + bfd_signed_vma got_entry, fdgot_entry, fd_entry; + /* The offsets of the PLT entries assigned to symbol+addend, + non-lazy and lazy, respectively. If unassigned, should be + (bfd_vma)-1. */ + bfd_vma plt_entry, lzplt_entry; +}; - name = h->root.root.string; +/* Compute a hash with the key fields of an bfinfdpic_relocs_info entry. */ +static hashval_t +bfinfdpic_relocs_info_hash (const void *entry_) +{ + const struct bfinfdpic_relocs_info *entry = entry_; - if ((h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - && ! BFINFDPIC_SYM_LOCAL (info, h)) - { - sec = NULL; - relocation = 0; - } - else - if (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - { - sec = h->root.u.def.section; - relocation = (h->root.u.def.value - + sec->output_section->vma - + sec->output_offset); - } - else if (h->root.type == bfd_link_hash_undefweak) - { - relocation = 0; - } - else if (info->unresolved_syms_in_objects == RM_IGNORE - && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) - relocation = 0; - else - { - if (! ((*info->callbacks->undefined_symbol) - (info, h->root.root.string, input_bfd, - input_section, rel->r_offset, - (info->unresolved_syms_in_objects == RM_GENERATE_ERROR - || ELF_ST_VISIBILITY (h->other))))) - return FALSE; - relocation = 0; - } - osec = sec; - } + return (entry->symndx == -1 + ? (long) entry->d.h->root.root.hash + : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend; +} - switch (r_type) - { - case R_pcrel24: - case R_pcrel24_jump_l: - case R_byte4_data: - if (! IS_FDPIC (output_bfd)) - goto non_fdpic; +/* Test whether the key fields of two bfinfdpic_relocs_info entries are + identical. */ +static int +bfinfdpic_relocs_info_eq (const void *entry1, const void *entry2) +{ + const struct bfinfdpic_relocs_info *e1 = entry1; + const struct bfinfdpic_relocs_info *e2 = entry2; - case R_BFIN_GOT17M4: - case R_BFIN_GOTHI: - case R_BFIN_GOTLO: - case R_BFIN_FUNCDESC_GOT17M4: - case R_BFIN_FUNCDESC_GOTHI: - case R_BFIN_FUNCDESC_GOTLO: - case R_BFIN_GOTOFF17M4: - case R_BFIN_GOTOFFHI: - case R_BFIN_GOTOFFLO: - case R_BFIN_FUNCDESC_GOTOFF17M4: - case R_BFIN_FUNCDESC_GOTOFFHI: - case R_BFIN_FUNCDESC_GOTOFFLO: - case R_BFIN_FUNCDESC: - case R_BFIN_FUNCDESC_VALUE: - if (h != NULL) - picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info - (info), input_bfd, h, - orig_addend, INSERT); - else - /* In order to find the entry we created before, we must - use the original addend, not the one that may have been - modified by _bfd_elf_rela_local_sym(). */ - picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info - (info), input_bfd, r_symndx, - orig_addend, INSERT); - if (! picrel) - return FALSE; + return e1->symndx == e2->symndx && e1->addend == e2->addend + && (e1->symndx == -1 ? e1->d.h == e2->d.h : e1->d.abfd == e2->d.abfd); +} - if (!_bfinfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info, - osec, sym, - rel->r_addend)) - { - (*_bfd_error_handler) - (_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"), - input_bfd, input_section, rel->r_offset, name); - return FALSE; +/* Find or create an entry in a hash table HT that matches the key + fields of the given ENTRY. If it's not found, memory for a new + entry is allocated in ABFD's obstack. */ +static struct bfinfdpic_relocs_info * +bfinfdpic_relocs_info_find (struct htab *ht, + bfd *abfd, + const struct bfinfdpic_relocs_info *entry, + enum insert_option insert) +{ + struct bfinfdpic_relocs_info **loc = + (struct bfinfdpic_relocs_info **) htab_find_slot (ht, entry, insert); - } + if (! loc) + return NULL; - break; + if (*loc) + return *loc; - default: - non_fdpic: - picrel = NULL; - if (h && ! BFINFDPIC_SYM_LOCAL (info, h)) - { - info->callbacks->warning - (info, _("relocation references symbol not defined in the module"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - break; - } + *loc = bfd_zalloc (abfd, sizeof (**loc)); - switch (r_type) - { - case R_pcrel24: - case R_pcrel24_jump_l: - check_segment[0] = isec_segment; - if (! IS_FDPIC (output_bfd)) - check_segment[1] = isec_segment; - else if (picrel->plt) - { - relocation = bfinfdpic_plt_section (info)->output_section->vma - + bfinfdpic_plt_section (info)->output_offset - + picrel->plt_entry; - check_segment[1] = plt_segment; - } - /* We don't want to warn on calls to undefined weak symbols, - as calls to them must be protected by non-NULL tests - anyway, and unprotected calls would invoke undefined - behavior. */ - else if (picrel->symndx == -1 - && picrel->d.h->root.type == bfd_link_hash_undefweak) - check_segment[1] = check_segment[0]; - else - check_segment[1] = sec - ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) - : (unsigned)-1; - break; + if (! *loc) + return *loc; - case R_BFIN_GOT17M4: - case R_BFIN_GOTHI: - case R_BFIN_GOTLO: - relocation = picrel->got_entry; - check_segment[0] = check_segment[1] = got_segment; - break; + (*loc)->symndx = entry->symndx; + (*loc)->d = entry->d; + (*loc)->addend = entry->addend; + (*loc)->plt_entry = (bfd_vma)-1; + (*loc)->lzplt_entry = (bfd_vma)-1; - case R_BFIN_FUNCDESC_GOT17M4: - case R_BFIN_FUNCDESC_GOTHI: - case R_BFIN_FUNCDESC_GOTLO: - relocation = picrel->fdgot_entry; - check_segment[0] = check_segment[1] = got_segment; - break; + return *loc; +} - case R_BFIN_GOTOFFHI: - case R_BFIN_GOTOFF17M4: - case R_BFIN_GOTOFFLO: - relocation -= bfinfdpic_got_section (info)->output_section->vma - + bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info); - check_segment[0] = got_segment; - check_segment[1] = sec - ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) - : (unsigned)-1; - break; +/* Obtain the address of the entry in HT associated with H's symbol + + addend, creating a new entry if none existed. ABFD is only used + for memory allocation purposes. */ +inline static struct bfinfdpic_relocs_info * +bfinfdpic_relocs_info_for_global (struct htab *ht, + bfd *abfd, + struct elf_link_hash_entry *h, + bfd_vma addend, + enum insert_option insert) +{ + struct bfinfdpic_relocs_info entry; - case R_BFIN_FUNCDESC_GOTOFF17M4: - case R_BFIN_FUNCDESC_GOTOFFHI: - case R_BFIN_FUNCDESC_GOTOFFLO: - relocation = picrel->fd_entry; - check_segment[0] = check_segment[1] = got_segment; - break; + entry.symndx = -1; + entry.d.h = h; + entry.addend = addend; - case R_BFIN_FUNCDESC: - { - int dynindx; - bfd_vma addend = rel->r_addend; + return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); +} - if (! (h && h->root.type == bfd_link_hash_undefweak - && BFINFDPIC_SYM_LOCAL (info, h))) - { - /* If the symbol is dynamic and there may be dynamic - symbol resolution because we are or are linked with a - shared library, emit a FUNCDESC relocation such that - the dynamic linker will allocate the function - descriptor. If the symbol needs a non-local function - descriptor but binds locally (e.g., its visibility is - protected, emit a dynamic relocation decayed to - section+offset. */ - if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h) - && BFINFDPIC_SYM_LOCAL (info, h) - && !(info->executable && !info->pie)) - { - dynindx = elf_section_data (h->root.u.def.section - ->output_section)->dynindx; - addend += h->root.u.def.section->output_offset - + h->root.u.def.value; - } - else if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h)) - { - if (addend) - { - info->callbacks->warning - (info, _("R_BFIN_FUNCDESC references dynamic symbol with nonzero addend"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - dynindx = h->dynindx; - } - else - { - /* Otherwise, we know we have a private function - descriptor, so reference it directly. */ - BFD_ASSERT (picrel->privfd); - r_type = R_byte4_data; - dynindx = elf_section_data (bfinfdpic_got_section (info) - ->output_section)->dynindx; - addend = bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info) - + picrel->fd_entry; - } +/* Obtain the address of the entry in HT associated with the SYMNDXth + local symbol of the input bfd ABFD, plus the addend, creating a new + entry if none existed. */ +inline static struct bfinfdpic_relocs_info * +bfinfdpic_relocs_info_for_local (struct htab *ht, + bfd *abfd, + long symndx, + bfd_vma addend, + enum insert_option insert) +{ + struct bfinfdpic_relocs_info entry; - /* If there is room for dynamic symbol resolution, emit - the dynamic relocation. However, if we're linking an - executable at a fixed location, we won't have emitted a - dynamic symbol entry for the got section, so idx will - be zero, which means we can and should compute the - address of the private descriptor ourselves. */ - if (info->executable && !info->pie - && (!h || BFINFDPIC_FUNCDESC_LOCAL (info, h))) - { - addend += bfinfdpic_got_section (info)->output_section->vma; - if ((bfd_get_section_flags (output_bfd, - input_section->output_section) - & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) - { - if (_bfinfdpic_osec_readonly_p (output_bfd, - input_section - ->output_section)) - { - info->callbacks->warning - (info, - _("cannot emit fixups in read-only section"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section - (info), - _bfd_elf_section_offset - (output_bfd, info, - input_section, rel->r_offset) - + input_section - ->output_section->vma - + input_section->output_offset, - picrel); - } - } - else if ((bfd_get_section_flags (output_bfd, - input_section->output_section) - & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) - { - bfd_vma offset; + entry.symndx = symndx; + entry.d.abfd = abfd; + entry.addend = addend; + + return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert); +} + +/* Merge fields set by check_relocs() of two entries that end up being + mapped to the same (presumably global) symbol. */ + +inline static void +bfinfdpic_pic_merge_early_relocs_info (struct bfinfdpic_relocs_info *e2, + struct bfinfdpic_relocs_info const *e1) +{ + e2->got17m4 |= e1->got17m4; + e2->gothilo |= e1->gothilo; + e2->fd |= e1->fd; + e2->fdgot17m4 |= e1->fdgot17m4; + e2->fdgothilo |= e1->fdgothilo; + e2->fdgoff17m4 |= e1->fdgoff17m4; + e2->fdgoffhilo |= e1->fdgoffhilo; + e2->gotoff |= e1->gotoff; + e2->call |= e1->call; + e2->sym |= e1->sym; +} + +/* Every block of 65535 lazy PLT entries shares a single call to the + resolver, inserted in the 32768th lazy PLT entry (i.e., entry # + 32767, counting from 0). All other lazy PLT entries branch to it + in a single instruction. */ + +#define LZPLT_RESOLVER_EXTRA 10 +#define LZPLT_NORMAL_SIZE 6 +#define LZPLT_ENTRIES 1362 + +#define BFINFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) LZPLT_NORMAL_SIZE * LZPLT_ENTRIES + LZPLT_RESOLVER_EXTRA) +#define BFINFDPIC_LZPLT_RESOLV_LOC (LZPLT_NORMAL_SIZE * LZPLT_ENTRIES / 2) + +/* Add a dynamic relocation to the SRELOC section. */ + +inline static bfd_vma +_bfinfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset, + int reloc_type, long dynindx, bfd_vma addend, + struct bfinfdpic_relocs_info *entry) +{ + Elf_Internal_Rela outrel; + bfd_vma reloc_offset; + + outrel.r_offset = offset; + outrel.r_info = ELF32_R_INFO (dynindx, reloc_type); + outrel.r_addend = addend; + + reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rel); + BFD_ASSERT (reloc_offset < sreloc->size); + bfd_elf32_swap_reloc_out (output_bfd, &outrel, + sreloc->contents + reloc_offset); + sreloc->reloc_count++; + + /* If the entry's index is zero, this relocation was probably to a + linkonce section that got discarded. We reserved a dynamic + relocation, but it was for another entry than the one we got at + the time of emitting the relocation. Unfortunately there's no + simple way for us to catch this situation, since the relocation + is cleared right before calling relocate_section, at which point + we no longer know what the relocation used to point to. */ + if (entry->symndx) + { + BFD_ASSERT (entry->dynrelocs > 0); + entry->dynrelocs--; + } + + return reloc_offset; +} + +/* Add a fixup to the ROFIXUP section. */ + +static bfd_vma +_bfinfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset, + struct bfinfdpic_relocs_info *entry) +{ + bfd_vma fixup_offset; + + if (rofixup->flags & SEC_EXCLUDE) + return -1; + + fixup_offset = rofixup->reloc_count * 4; + if (rofixup->contents) + { + BFD_ASSERT (fixup_offset < rofixup->size); + bfd_put_32 (output_bfd, offset, rofixup->contents + fixup_offset); + } + rofixup->reloc_count++; + + if (entry && entry->symndx) + { + /* See discussion about symndx == 0 in _bfinfdpic_add_dyn_reloc + above. */ + BFD_ASSERT (entry->fixups > 0); + entry->fixups--; + } + + return fixup_offset; +} + +/* Find the segment number in which OSEC, and output section, is + located. */ + +static unsigned +_bfinfdpic_osec_to_segment (bfd *output_bfd, asection *osec) +{ + Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section (output_bfd, osec); + + return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1; +} + +inline static bfd_boolean +_bfinfdpic_osec_readonly_p (bfd *output_bfd, asection *osec) +{ + unsigned seg = _bfinfdpic_osec_to_segment (output_bfd, osec); + + return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W); +} + +/* Generate relocations for GOT entries, function descriptors, and + code for PLT and lazy PLT entries. */ + +inline static bfd_boolean +_bfinfdpic_emit_got_relocs_plt_entries (struct bfinfdpic_relocs_info *entry, + bfd *output_bfd, + struct bfd_link_info *info, + asection *sec, + Elf_Internal_Sym *sym, + bfd_vma addend) + +{ + bfd_vma fd_lazy_rel_offset = (bfd_vma)-1; + int dynindx = -1; + + if (entry->done) + return TRUE; + entry->done = 1; + + if (entry->got_entry || entry->fdgot_entry || entry->fd_entry) + { + /* If the symbol is dynamic, consider it for dynamic + relocations, otherwise decay to section + offset. */ + if (entry->symndx == -1 && entry->d.h->dynindx != -1) + dynindx = entry->d.h->dynindx; + else + { + if (sec->output_section + && ! bfd_is_abs_section (sec->output_section) + && ! bfd_is_und_section (sec->output_section)) + dynindx = elf_section_data (sec->output_section)->dynindx; + else + dynindx = 0; + } + } + + /* Generate relocation for GOT entry pointing to the symbol. */ + if (entry->got_entry) + { + int idx = dynindx; + bfd_vma ad = addend; + + /* If the symbol is dynamic but binds locally, use + section+offset. */ + if (sec && (entry->symndx != -1 + || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + { + if (entry->symndx == -1) + ad += entry->d.h->root.u.def.value; + else + ad += sym->st_value; + ad += sec->output_offset; + if (sec->output_section && elf_section_data (sec->output_section)) + idx = elf_section_data (sec->output_section)->dynindx; + else + idx = 0; + } + + /* If we're linking an executable at a fixed address, we can + omit the dynamic relocation as long as the symbol is local to + this module. */ + if (info->executable && !info->pie + && (entry->symndx != -1 + || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + { + if (sec) + ad += sec->output_section->vma; + if (entry->symndx != -1 + || entry->d.h->root.type != bfd_link_hash_undefweak) + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section (info), + bfinfdpic_got_section (info)->output_section + ->vma + + bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info) + + entry->got_entry, entry); + } + else + _bfinfdpic_add_dyn_reloc (output_bfd, bfinfdpic_gotrel_section (info), + _bfd_elf_section_offset + (output_bfd, info, + bfinfdpic_got_section (info), + bfinfdpic_got_initial_offset (info) + + entry->got_entry) + + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info)->output_offset, + R_byte4_data, idx, ad, entry); + + bfd_put_32 (output_bfd, ad, + bfinfdpic_got_section (info)->contents + + bfinfdpic_got_initial_offset (info) + + entry->got_entry); + } + + /* Generate relocation for GOT entry pointing to a canonical + function descriptor. */ + if (entry->fdgot_entry) + { + int reloc, idx; + bfd_vma ad = 0; + + if (! (entry->symndx == -1 + && entry->d.h->root.type == bfd_link_hash_undefweak + && BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + { + /* If the symbol is dynamic and there may be dynamic symbol + resolution because we are, or are linked with, a shared + library, emit a FUNCDESC relocation such that the dynamic + linker will allocate the function descriptor. If the + symbol needs a non-local function descriptor but binds + locally (e.g., its visibility is protected, emit a + dynamic relocation decayed to section+offset. */ + if (entry->symndx == -1 + && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h) + && BFINFDPIC_SYM_LOCAL (info, entry->d.h) + && !(info->executable && !info->pie)) + { + reloc = R_BFIN_FUNCDESC; + idx = elf_section_data (entry->d.h->root.u.def.section + ->output_section)->dynindx; + ad = entry->d.h->root.u.def.section->output_offset + + entry->d.h->root.u.def.value; + } + else if (entry->symndx == -1 + && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)) + { + reloc = R_BFIN_FUNCDESC; + idx = dynindx; + ad = addend; + if (ad) + return FALSE; + } + else + { + /* Otherwise, we know we have a private function descriptor, + so reference it directly. */ + if (elf_hash_table (info)->dynamic_sections_created) + BFD_ASSERT (entry->privfd); + reloc = R_byte4_data; + idx = elf_section_data (bfinfdpic_got_section (info) + ->output_section)->dynindx; + ad = bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info) + entry->fd_entry; + } + + /* If there is room for dynamic symbol resolution, emit the + dynamic relocation. However, if we're linking an + executable at a fixed location, we won't have emitted a + dynamic symbol entry for the got section, so idx will be + zero, which means we can and should compute the address + of the private descriptor ourselves. */ + if (info->executable && !info->pie + && (entry->symndx != -1 + || BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h))) + { + ad += bfinfdpic_got_section (info)->output_section->vma; + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section (info), + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info) + ->output_offset + + bfinfdpic_got_initial_offset (info) + + entry->fdgot_entry, entry); + } + else + _bfinfdpic_add_dyn_reloc (output_bfd, + bfinfdpic_gotrel_section (info), + _bfd_elf_section_offset + (output_bfd, info, + bfinfdpic_got_section (info), + bfinfdpic_got_initial_offset (info) + + entry->fdgot_entry) + + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info) + ->output_offset, + reloc, idx, ad, entry); + } + + bfd_put_32 (output_bfd, ad, + bfinfdpic_got_section (info)->contents + + bfinfdpic_got_initial_offset (info) + + entry->fdgot_entry); + } + + /* Generate relocation to fill in a private function descriptor in + the GOT. */ + if (entry->fd_entry) + { + int idx = dynindx; + bfd_vma ad = addend; + bfd_vma ofst; + long lowword, highword; + + /* If the symbol is dynamic but binds locally, use + section+offset. */ + if (sec && (entry->symndx != -1 + || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + { + if (entry->symndx == -1) + ad += entry->d.h->root.u.def.value; + else + ad += sym->st_value; + ad += sec->output_offset; + if (sec->output_section && elf_section_data (sec->output_section)) + idx = elf_section_data (sec->output_section)->dynindx; + else + idx = 0; + } + + /* If we're linking an executable at a fixed address, we can + omit the dynamic relocation as long as the symbol is local to + this module. */ + if (info->executable && !info->pie + && (entry->symndx != -1 || BFINFDPIC_SYM_LOCAL (info, entry->d.h))) + { + if (sec) + ad += sec->output_section->vma; + ofst = 0; + if (entry->symndx != -1 + || entry->d.h->root.type != bfd_link_hash_undefweak) + { + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section (info), + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info) + ->output_offset + + bfinfdpic_got_initial_offset (info) + + entry->fd_entry, entry); + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section (info), + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info) + ->output_offset + + bfinfdpic_got_initial_offset (info) + + entry->fd_entry + 4, entry); + } + } + else + { + ofst + = _bfinfdpic_add_dyn_reloc (output_bfd, + entry->lazyplt + ? bfinfdpic_pltrel_section (info) + : bfinfdpic_gotrel_section (info), + _bfd_elf_section_offset + (output_bfd, info, + bfinfdpic_got_section (info), + bfinfdpic_got_initial_offset (info) + + entry->fd_entry) + + bfinfdpic_got_section (info) + ->output_section->vma + + bfinfdpic_got_section (info) + ->output_offset, + R_BFIN_FUNCDESC_VALUE, idx, ad, entry); + } + + /* If we've omitted the dynamic relocation, just emit the fixed + addresses of the symbol and of the local GOT base offset. */ + if (info->executable && !info->pie && sec && sec->output_section) + { + lowword = ad; + highword = bfinfdpic_got_section (info)->output_section->vma + + bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info); + } + else if (entry->lazyplt) + { + if (ad) + return FALSE; + + fd_lazy_rel_offset = ofst; + + /* A function descriptor used for lazy or local resolving is + initialized such that its high word contains the output + section index in which the PLT entries are located, and + the low word contains the address of the lazy PLT entry + entry point, that must be within the memory region + assigned to that section. */ + lowword = entry->lzplt_entry + 4 + + bfinfdpic_plt_section (info)->output_offset + + bfinfdpic_plt_section (info)->output_section->vma; + highword = _bfinfdpic_osec_to_segment + (output_bfd, bfinfdpic_plt_section (info)->output_section); + } + else + { + /* A function descriptor for a local function gets the index + of the section. For a non-local function, it's + disregarded. */ + lowword = ad; + if (entry->symndx == -1 && entry->d.h->dynindx != -1 + && entry->d.h->dynindx == idx) + highword = 0; + else + highword = _bfinfdpic_osec_to_segment + (output_bfd, sec->output_section); + } + + bfd_put_32 (output_bfd, lowword, + bfinfdpic_got_section (info)->contents + + bfinfdpic_got_initial_offset (info) + + entry->fd_entry); + bfd_put_32 (output_bfd, highword, + bfinfdpic_got_section (info)->contents + + bfinfdpic_got_initial_offset (info) + + entry->fd_entry + 4); + } + + /* Generate code for the PLT entry. */ + if (entry->plt_entry != (bfd_vma) -1) + { + bfd_byte *plt_code = bfinfdpic_plt_section (info)->contents + + entry->plt_entry; + + BFD_ASSERT (entry->fd_entry); + + /* Figure out what kind of PLT entry we need, depending on the + location of the function descriptor within the GOT. */ + if (entry->fd_entry >= -(1 << (18 - 1)) + && entry->fd_entry + 4 < (1 << (18 - 1))) + { + /* P1 = [P3 + fd_entry]; P3 = [P3 + fd_entry + 4] */ + bfd_put_32 (output_bfd, + 0xe519 | ((entry->fd_entry << 14) & 0xFFFF0000), + plt_code); + bfd_put_32 (output_bfd, + 0xe51b | (((entry->fd_entry + 4) << 14) & 0xFFFF0000), + plt_code + 4); + plt_code += 8; + } + else + { + /* P1.L = fd_entry; P1.H = fd_entry; + P3 = P3 + P1; + P1 = [P3]; + P3 = [P3 + 4]; */ + bfd_put_32 (output_bfd, + 0xe109 | (entry->fd_entry << 16), + plt_code); + bfd_put_32 (output_bfd, + 0xe149 | (entry->fd_entry & 0xFFFF0000), + plt_code + 4); + bfd_put_16 (output_bfd, 0x5ad9, plt_code + 8); + bfd_put_16 (output_bfd, 0x9159, plt_code + 10); + bfd_put_16 (output_bfd, 0xac5b, plt_code + 12); + plt_code += 14; + } + /* JUMP (P1) */ + bfd_put_16 (output_bfd, 0x0051, plt_code); + } + + /* Generate code for the lazy PLT entry. */ + if (entry->lzplt_entry != (bfd_vma) -1) + { + bfd_byte *lzplt_code = bfinfdpic_plt_section (info)->contents + + entry->lzplt_entry; + bfd_vma resolverStub_addr; + + bfd_put_32 (output_bfd, fd_lazy_rel_offset, lzplt_code); + lzplt_code += 4; + + resolverStub_addr = entry->lzplt_entry / BFINFDPIC_LZPLT_BLOCK_SIZE + * BFINFDPIC_LZPLT_BLOCK_SIZE + BFINFDPIC_LZPLT_RESOLV_LOC; + if (resolverStub_addr >= bfinfdpic_plt_initial_offset (info)) + resolverStub_addr = bfinfdpic_plt_initial_offset (info) - LZPLT_NORMAL_SIZE - LZPLT_RESOLVER_EXTRA; + + if (entry->lzplt_entry == resolverStub_addr) + { + /* This is a lazy PLT entry that includes a resolver call. + P2 = [P3]; + R3 = [P3 + 4]; + JUMP (P2); */ + bfd_put_32 (output_bfd, + 0xa05b915a, + lzplt_code); + bfd_put_16 (output_bfd, 0x0052, lzplt_code + 4); + } + else + { + /* JUMP.S resolverStub */ + bfd_put_16 (output_bfd, + 0x2000 + | (((resolverStub_addr - entry->lzplt_entry) + / 2) & (((bfd_vma)1 << 12) - 1)), + lzplt_code); + } + } - if (_bfinfdpic_osec_readonly_p (output_bfd, - input_section - ->output_section)) - { - info->callbacks->warning - (info, - _("cannot emit dynamic relocations in read-only section"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - offset = _bfd_elf_section_offset (output_bfd, info, - input_section, rel->r_offset); - /* Only output a reloc for a not deleted entry. */ - if (offset >= (bfd_vma) -2) - _bfinfdpic_add_dyn_reloc (output_bfd, - bfinfdpic_gotrel_section (info), - 0, - R_unused0, - dynindx, addend, picrel); - else - _bfinfdpic_add_dyn_reloc (output_bfd, - bfinfdpic_gotrel_section (info), - offset + input_section - ->output_section->vma - + input_section->output_offset, - r_type, - dynindx, addend, picrel); - } - else - addend += bfinfdpic_got_section (info)->output_section->vma; - } + return TRUE; +} + +/* Relocate an Blackfin ELF section. - /* We want the addend in-place because dynamic - relocations are REL. Setting relocation to it should - arrange for it to be installed. */ - relocation = addend - rel->r_addend; - } - check_segment[0] = check_segment[1] = got_segment; - break; + The RELOCATE_SECTION function is called by the new ELF backend linker + to handle the relocations for a section. - case R_byte4_data: - if (! IS_FDPIC (output_bfd)) - { - check_segment[0] = check_segment[1] = -1; - break; - } - /* Fall through. */ - case R_BFIN_FUNCDESC_VALUE: - { - int dynindx; - bfd_vma addend = rel->r_addend; + The relocs are always passed as Rela structures; if the section + actually uses Rel structures, the r_addend field will always be + zero. - /* If the symbol is dynamic but binds locally, use - section+offset. */ - if (h && ! BFINFDPIC_SYM_LOCAL (info, h)) - { - if (addend && r_type == R_BFIN_FUNCDESC_VALUE) - { - info->callbacks->warning - (info, _("R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - dynindx = h->dynindx; - } - else - { - if (h) - addend += h->root.u.def.value; - else - addend += sym->st_value; - if (osec) - addend += osec->output_offset; - if (osec && osec->output_section - && ! bfd_is_abs_section (osec->output_section) - && ! bfd_is_und_section (osec->output_section)) - dynindx = elf_section_data (osec->output_section)->dynindx; - else - dynindx = 0; - } + This function is responsible for adjusting the section contents as + necessary, and (if using Rela relocs and generating a relocatable + output file) adjusting the reloc addend as necessary. - /* If we're linking an executable at a fixed address, we - can omit the dynamic relocation as long as the symbol - is defined in the current link unit (which is implied - by its output section not being NULL). */ - if (info->executable && !info->pie - && (!h || BFINFDPIC_SYM_LOCAL (info, h))) - { - if (osec) - addend += osec->output_section->vma; - if (IS_FDPIC (input_bfd) - && (bfd_get_section_flags (output_bfd, - input_section->output_section) - & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) - { - if (_bfinfdpic_osec_readonly_p (output_bfd, - input_section - ->output_section)) - { - info->callbacks->warning - (info, - _("cannot emit fixups in read-only section"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - if (!h || h->root.type != bfd_link_hash_undefweak) - { - _bfinfdpic_add_rofixup (output_bfd, - bfinfdpic_gotfixup_section - (info), - _bfd_elf_section_offset - (output_bfd, info, - input_section, rel->r_offset) - + input_section - ->output_section->vma - + input_section->output_offset, - picrel); - if (r_type == R_BFIN_FUNCDESC_VALUE) - _bfinfdpic_add_rofixup - (output_bfd, - bfinfdpic_gotfixup_section (info), - _bfd_elf_section_offset - (output_bfd, info, - input_section, rel->r_offset) - + input_section->output_section->vma - + input_section->output_offset + 4, picrel); - } - } - } - else - { - if ((bfd_get_section_flags (output_bfd, - input_section->output_section) - & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) - { - if (_bfinfdpic_osec_readonly_p (output_bfd, - input_section - ->output_section)) - { - info->callbacks->warning - (info, - _("cannot emit dynamic relocations in read-only section"), - name, input_bfd, input_section, rel->r_offset); - return FALSE; - } - _bfinfdpic_add_dyn_reloc (output_bfd, - bfinfdpic_gotrel_section (info), - _bfd_elf_section_offset - (output_bfd, info, - input_section, rel->r_offset) - + input_section - ->output_section->vma - + input_section->output_offset, - r_type, dynindx, addend, picrel); - } - else if (osec) - addend += osec->output_section->vma; - /* We want the addend in-place because dynamic - relocations are REL. Setting relocation to it - should arrange for it to be installed. */ - relocation = addend - rel->r_addend; - } + This function does not have to worry about setting the reloc + address or the reloc symbol index. - if (r_type == R_BFIN_FUNCDESC_VALUE) - { - /* If we've omitted the dynamic relocation, just emit - the fixed addresses of the symbol and of the local - GOT base offset. */ - if (info->executable && !info->pie - && (!h || BFINFDPIC_SYM_LOCAL (info, h))) - bfd_put_32 (output_bfd, - bfinfdpic_got_section (info)->output_section->vma - + bfinfdpic_got_section (info)->output_offset - + bfinfdpic_got_initial_offset (info), - contents + rel->r_offset + 4); - else - /* A function descriptor used for lazy or local - resolving is initialized such that its high word - contains the output section index in which the - PLT entries are located, and the low word - contains the offset of the lazy PLT entry entry - point into that section. */ - bfd_put_32 (output_bfd, - h && ! BFINFDPIC_SYM_LOCAL (info, h) - ? 0 - : _bfinfdpic_osec_to_segment (output_bfd, - sec - ->output_section), - contents + rel->r_offset + 4); - } - } - check_segment[0] = check_segment[1] = got_segment; - break; + LOCAL_SYMS is a pointer to the swapped in local symbols. + + LOCAL_SECTIONS is an array giving the section in the input file + corresponding to the st_shndx field of each local symbol. + + The global hash table entry for the global symbols can be found + via elf_sym_hashes (input_bfd). + + When generating relocatable output, this function must handle + STB_LOCAL/STT_SECTION symbols specially. The output symbol is + going to be the section symbol corresponding to the output + section, which means that the addend must be adjusted + accordingly. */ + +static bfd_boolean +bfinfdpic_relocate_section (bfd * output_bfd, + struct bfd_link_info *info, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * relocs, + Elf_Internal_Sym * local_syms, + asection ** local_sections) +{ + Elf_Internal_Shdr *symtab_hdr; + struct elf_link_hash_entry **sym_hashes; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + unsigned isec_segment, got_segment, plt_segment, + check_segment[2]; + int silence_segment_error = !(info->shared || info->pie); + + symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + relend = relocs + input_section->reloc_count; + + isec_segment = _bfinfdpic_osec_to_segment (output_bfd, + input_section->output_section); + if (IS_FDPIC (output_bfd) && bfinfdpic_got_section (info)) + got_segment = _bfinfdpic_osec_to_segment (output_bfd, + bfinfdpic_got_section (info) + ->output_section); + else + got_segment = -1; + if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created) + plt_segment = _bfinfdpic_osec_to_segment (output_bfd, + bfinfdpic_plt_section (info) + ->output_section); + else + plt_segment = -1; + + for (rel = relocs; rel < relend; rel ++) + { + reloc_howto_type *howto; + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sec; + struct elf_link_hash_entry *h; + bfd_vma relocation; + bfd_reloc_status_type r; + const char * name = NULL; + int r_type; + asection *osec; + struct bfinfdpic_relocs_info *picrel; + bfd_vma orig_addend = rel->r_addend; - default: - check_segment[0] = isec_segment; - check_segment[1] = sec - ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) - : (unsigned)-1; - break; + r_type = ELF32_R_TYPE (rel->r_info); + + if (r_type == R_BFIN_GNU_VTINHERIT + || r_type == R_BFIN_GNU_VTENTRY) + continue; + + r_symndx = ELF32_R_SYM (rel->r_info); + howto = bfin_reloc_type_lookup (input_bfd, r_type); + if (howto == NULL) + { + bfd_set_error (bfd_error_bad_value); + return FALSE; } - if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd)) + h = NULL; + sym = NULL; + sec = NULL; + + if (r_symndx < symtab_hdr->sh_info) { -#if 1 /* If you take this out, remove the #error from fdpic-static-6.d - in the ld testsuite. */ - /* This helps catch problems in GCC while we can't do more - than static linking. The idea is to test whether the - input file basename is crt0.o only once. */ - if (silence_segment_error == 1) - silence_segment_error = - (strlen (input_bfd->filename) == 6 - && strcmp (input_bfd->filename, "crt0.o") == 0) - || (strlen (input_bfd->filename) > 6 - && strcmp (input_bfd->filename - + strlen (input_bfd->filename) - 7, - "/crt0.o") == 0) - ? -1 : 0; -#endif - if (!silence_segment_error - /* We don't want duplicate errors for undefined - symbols. */ - && !(picrel && picrel->symndx == -1 - && picrel->d.h->root.type == bfd_link_hash_undefined)) - info->callbacks->warning - (info, - (info->shared || info->pie) - ? _("relocations between different segments are not supported") - : _("warning: relocation references a different segment"), - name, input_bfd, input_section, rel->r_offset); - if (!silence_segment_error && (info->shared || info->pie)) - return FALSE; - elf_elfheader (output_bfd)->e_flags |= EF_BFIN_PIC; + sym = local_syms + r_symndx; + osec = sec = local_sections [r_symndx]; + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + + name = bfd_elf_string_from_elf_section + (input_bfd, symtab_hdr->sh_link, sym->st_name); + name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name; + } + else + { + bfd_boolean warned; + bfd_boolean unresolved_reloc; + + RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, + r_symndx, symtab_hdr, sym_hashes, + h, sec, relocation, + unresolved_reloc, warned); + osec = sec; } - switch (r_type) + if (sec != NULL && elf_discarded_section (sec)) { - case R_BFIN_GOTOFFHI: - /* We need the addend to be applied before we shift the - value right. */ - relocation += rel->r_addend; - /* Fall through. */ - case R_BFIN_GOTHI: - case R_BFIN_FUNCDESC_GOTHI: - case R_BFIN_FUNCDESC_GOTOFFHI: - relocation >>= 16; - /* Fall through. */ + /* For relocs against symbols from removed linkonce sections, + or sections discarded by a linker script, we just want the + section contents zeroed. Avoid any special processing. */ + _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); + rel->r_info = 0; + rel->r_addend = 0; + continue; + } - case R_BFIN_GOTLO: - case R_BFIN_FUNCDESC_GOTLO: - case R_BFIN_GOTOFFLO: - case R_BFIN_FUNCDESC_GOTOFFLO: - relocation &= 0xffff; - break; + if (info->relocatable) + continue; - default: - break; + if (h != NULL + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && !BFINFDPIC_SYM_LOCAL (info, h)) + { + osec = sec = NULL; + relocation = 0; } switch (r_type) { case R_pcrel24: case R_pcrel24_jump_l: - if (! IS_FDPIC (output_bfd) || ! picrel->plt) - break; - /* Fall through. */ + case R_byte4_data: + if (! IS_FDPIC (output_bfd)) + goto non_fdpic; - /* When referencing a GOT entry, a function descriptor or a - PLT, we don't want the addend to apply to the reference, - but rather to the referenced symbol. The actual entry - will have already been created taking the addend into - account, so cancel it out here. */ case R_BFIN_GOT17M4: case R_BFIN_GOTHI: case R_BFIN_GOTLO: case R_BFIN_FUNCDESC_GOT17M4: case R_BFIN_FUNCDESC_GOTHI: case R_BFIN_FUNCDESC_GOTLO: + case R_BFIN_GOTOFF17M4: + case R_BFIN_GOTOFFHI: + case R_BFIN_GOTOFFLO: case R_BFIN_FUNCDESC_GOTOFF17M4: case R_BFIN_FUNCDESC_GOTOFFHI: case R_BFIN_FUNCDESC_GOTOFFLO: - /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF17M4 - here, since we do want to apply the addend to the others. - Note that we've applied the addend to GOTOFFHI before we - shifted it right. */ - case R_BFIN_GOTOFFHI: - relocation -= rel->r_addend; - break; - - default: - break; - } - - if (r_type == R_pcrel24 - || r_type == R_pcrel24_jump_l) - { - bfd_vma x; - bfd_vma address = rel->r_offset; - - relocation += rel->r_addend; - - /* Perform usual pc-relative correction. */ - relocation -= input_section->output_section->vma + input_section->output_offset; - relocation -= address; - - /* We are getting reloc_entry->address 2 byte off from - the start of instruction. Assuming absolute postion - of the reloc data. But, following code had been written assuming - reloc address is starting at begining of instruction. - To compensate that I have increased the value of - relocation by 1 (effectively 2) and used the addr -2 instead of addr. */ + case R_BFIN_FUNCDESC: + case R_BFIN_FUNCDESC_VALUE: + if (h != NULL) + picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info + (info), input_bfd, h, + orig_addend, INSERT); + else + /* In order to find the entry we created before, we must + use the original addend, not the one that may have been + modified by _bfd_elf_rela_local_sym(). */ + picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info + (info), input_bfd, r_symndx, + orig_addend, INSERT); + if (! picrel) + return FALSE; - relocation += 2; - address -= 2; + if (!_bfinfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info, + osec, sym, + rel->r_addend)) + { + (*_bfd_error_handler) + (_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"), + input_bfd, input_section, rel->r_offset, name); + return FALSE; - relocation >>= 1; + } - x = bfd_get_16 (input_bfd, contents + address); - x = (x & 0xff00) | ((relocation >> 16) & 0xff); - bfd_put_16 (input_bfd, x, contents + address); + break; - x = bfd_get_16 (input_bfd, contents + address + 2); - x = relocation & 0xFFFF; - bfd_put_16 (input_bfd, x, contents + address + 2); - r = bfd_reloc_ok; + default: + non_fdpic: + picrel = NULL; + if (h && ! BFINFDPIC_SYM_LOCAL (info, h)) + { + info->callbacks->warning + (info, _("relocation references symbol not defined in the module"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + break; } - else - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, rel->r_offset, - relocation, rel->r_addend); - if (r != bfd_reloc_ok) + switch (r_type) { - const char * msg = (const char *) NULL; - - switch (r) + case R_pcrel24: + case R_pcrel24_jump_l: + check_segment[0] = isec_segment; + if (! IS_FDPIC (output_bfd)) + check_segment[1] = isec_segment; + else if (picrel->plt) { - case bfd_reloc_overflow: - r = info->callbacks->reloc_overflow - (info, (h ? &h->root : NULL), name, howto->name, - (bfd_vma) 0, input_bfd, input_section, rel->r_offset); - break; - - case bfd_reloc_undefined: - r = info->callbacks->undefined_symbol - (info, name, input_bfd, input_section, rel->r_offset, TRUE); - break; - - case bfd_reloc_outofrange: - msg = _("internal error: out of range error"); - break; - - case bfd_reloc_notsupported: - msg = _("internal error: unsupported relocation error"); - break; - - case bfd_reloc_dangerous: - msg = _("internal error: dangerous relocation"); - break; - - default: - msg = _("internal error: unknown error"); - break; + relocation = bfinfdpic_plt_section (info)->output_section->vma + + bfinfdpic_plt_section (info)->output_offset + + picrel->plt_entry; + check_segment[1] = plt_segment; } + /* We don't want to warn on calls to undefined weak symbols, + as calls to them must be protected by non-NULL tests + anyway, and unprotected calls would invoke undefined + behavior. */ + else if (picrel->symndx == -1 + && picrel->d.h->root.type == bfd_link_hash_undefweak) + check_segment[1] = check_segment[0]; + else + check_segment[1] = sec + ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) + : (unsigned)-1; + break; - if (msg) - r = info->callbacks->warning - (info, msg, name, input_bfd, input_section, rel->r_offset); - - if (! r) - return FALSE; - } - } - - return TRUE; -} - -static bfd_boolean -bfin_relocate_section (bfd * output_bfd, - struct bfd_link_info *info, - bfd * input_bfd, - asection * input_section, - bfd_byte * contents, - Elf_Internal_Rela * relocs, - Elf_Internal_Sym * local_syms, - asection ** local_sections) -{ - bfd *dynobj; - Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes; - bfd_vma *local_got_offsets; - asection *sgot; - asection *sreloc; - Elf_Internal_Rela *rel; - Elf_Internal_Rela *relend; - int i = 0; - - if (info->relocatable) - return TRUE; - - dynobj = elf_hash_table (info)->dynobj; - symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; - sym_hashes = elf_sym_hashes (input_bfd); - local_got_offsets = elf_local_got_offsets (input_bfd); - - sgot = NULL; - sreloc = NULL; + case R_BFIN_GOT17M4: + case R_BFIN_GOTHI: + case R_BFIN_GOTLO: + relocation = picrel->got_entry; + check_segment[0] = check_segment[1] = got_segment; + break; - rel = relocs; - relend = relocs + input_section->reloc_count; - for (; rel < relend; rel++, i++) - { - int r_type; - reloc_howto_type *howto; - unsigned long r_symndx; - struct elf_link_hash_entry *h; - Elf_Internal_Sym *sym; - asection *sec; - bfd_vma relocation = 0; - bfd_boolean unresolved_reloc; - bfd_reloc_status_type r; - bfd_vma address; + case R_BFIN_FUNCDESC_GOT17M4: + case R_BFIN_FUNCDESC_GOTHI: + case R_BFIN_FUNCDESC_GOTLO: + relocation = picrel->fdgot_entry; + check_segment[0] = check_segment[1] = got_segment; + break; - r_type = ELF32_R_TYPE (rel->r_info); - if (r_type < 0 || r_type >= 243) - { - bfd_set_error (bfd_error_bad_value); - return FALSE; - } + case R_BFIN_GOTOFFHI: + case R_BFIN_GOTOFF17M4: + case R_BFIN_GOTOFFLO: + relocation -= bfinfdpic_got_section (info)->output_section->vma + + bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info); + check_segment[0] = got_segment; + check_segment[1] = sec + ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) + : (unsigned)-1; + break; - if (r_type == R_BFIN_GNU_VTENTRY - || r_type == R_BFIN_GNU_VTINHERIT) - continue; + case R_BFIN_FUNCDESC_GOTOFF17M4: + case R_BFIN_FUNCDESC_GOTOFFHI: + case R_BFIN_FUNCDESC_GOTOFFLO: + relocation = picrel->fd_entry; + check_segment[0] = check_segment[1] = got_segment; + break; - howto = bfin_reloc_type_lookup (input_bfd, r_type); - if (howto == NULL) - { - bfd_set_error (bfd_error_bad_value); - return FALSE; - } - r_symndx = ELF32_R_SYM (rel->r_info); + case R_BFIN_FUNCDESC: + { + int dynindx; + bfd_vma addend = rel->r_addend; - h = NULL; - sym = NULL; - sec = NULL; - unresolved_reloc = FALSE; + if (! (h && h->root.type == bfd_link_hash_undefweak + && BFINFDPIC_SYM_LOCAL (info, h))) + { + /* If the symbol is dynamic and there may be dynamic + symbol resolution because we are or are linked with a + shared library, emit a FUNCDESC relocation such that + the dynamic linker will allocate the function + descriptor. If the symbol needs a non-local function + descriptor but binds locally (e.g., its visibility is + protected, emit a dynamic relocation decayed to + section+offset. */ + if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h) + && BFINFDPIC_SYM_LOCAL (info, h) + && !(info->executable && !info->pie)) + { + dynindx = elf_section_data (h->root.u.def.section + ->output_section)->dynindx; + addend += h->root.u.def.section->output_offset + + h->root.u.def.value; + } + else if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h)) + { + if (addend) + { + info->callbacks->warning + (info, _("R_BFIN_FUNCDESC references dynamic symbol with nonzero addend"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + dynindx = h->dynindx; + } + else + { + /* Otherwise, we know we have a private function + descriptor, so reference it directly. */ + BFD_ASSERT (picrel->privfd); + r_type = R_byte4_data; + dynindx = elf_section_data (bfinfdpic_got_section (info) + ->output_section)->dynindx; + addend = bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info) + + picrel->fd_entry; + } - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - sec = local_sections[r_symndx]; - relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); - } - else - { - bfd_boolean warned; - h = NULL; - RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, - r_symndx, symtab_hdr, sym_hashes, - h, sec, relocation, - unresolved_reloc, warned); - } + /* If there is room for dynamic symbol resolution, emit + the dynamic relocation. However, if we're linking an + executable at a fixed location, we won't have emitted a + dynamic symbol entry for the got section, so idx will + be zero, which means we can and should compute the + address of the private descriptor ourselves. */ + if (info->executable && !info->pie + && (!h || BFINFDPIC_FUNCDESC_LOCAL (info, h))) + { + addend += bfinfdpic_got_section (info)->output_section->vma; + if ((bfd_get_section_flags (output_bfd, + input_section->output_section) + & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) + { + if (_bfinfdpic_osec_readonly_p (output_bfd, + input_section + ->output_section)) + { + info->callbacks->warning + (info, + _("cannot emit fixups in read-only section"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section + (info), + _bfd_elf_section_offset + (output_bfd, info, + input_section, rel->r_offset) + + input_section + ->output_section->vma + + input_section->output_offset, + picrel); + } + } + else if ((bfd_get_section_flags (output_bfd, + input_section->output_section) + & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) + { + bfd_vma offset; - address = rel->r_offset; + if (_bfinfdpic_osec_readonly_p (output_bfd, + input_section + ->output_section)) + { + info->callbacks->warning + (info, + _("cannot emit dynamic relocations in read-only section"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + offset = _bfd_elf_section_offset (output_bfd, info, + input_section, rel->r_offset); + /* Only output a reloc for a not deleted entry. */ + if (offset >= (bfd_vma) -2) + _bfinfdpic_add_dyn_reloc (output_bfd, + bfinfdpic_gotrel_section (info), + 0, + R_unused0, + dynindx, addend, picrel); + else + _bfinfdpic_add_dyn_reloc (output_bfd, + bfinfdpic_gotrel_section (info), + offset + input_section + ->output_section->vma + + input_section->output_offset, + r_type, + dynindx, addend, picrel); + } + else + addend += bfinfdpic_got_section (info)->output_section->vma; + } - /* Then, process normally. */ - switch (r_type) - { - case R_BFIN_GNU_VTINHERIT: - case R_BFIN_GNU_VTENTRY: - return bfd_reloc_ok; + /* We want the addend in-place because dynamic + relocations are REL. Setting relocation to it should + arrange for it to be installed. */ + relocation = addend - rel->r_addend; + } + check_segment[0] = check_segment[1] = got_segment; + break; - case R_got: - /* Relocation is to the address of the entry for this symbol - in the global offset table. */ - if (h != NULL - && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0) - goto do_default; + case R_byte4_data: + if (! IS_FDPIC (output_bfd)) + { + check_segment[0] = check_segment[1] = -1; + break; + } /* Fall through. */ - /* Relocation is the offset of the entry for this symbol in - the global offset table. */ - + case R_BFIN_FUNCDESC_VALUE: { - bfd_vma off; + int dynindx; + bfd_vma addend = rel->r_addend; + bfd_vma offset; + offset = _bfd_elf_section_offset (output_bfd, info, + input_section, rel->r_offset); - if (sgot == NULL) + /* If the symbol is dynamic but binds locally, use + section+offset. */ + if (h && ! BFINFDPIC_SYM_LOCAL (info, h)) { - sgot = bfd_get_section_by_name (dynobj, ".got"); - BFD_ASSERT (sgot != NULL); + if (addend && r_type == R_BFIN_FUNCDESC_VALUE) + { + info->callbacks->warning + (info, _("R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + dynindx = h->dynindx; + } + else + { + if (h) + addend += h->root.u.def.value; + else + addend += sym->st_value; + if (osec) + addend += osec->output_offset; + if (osec && osec->output_section + && ! bfd_is_abs_section (osec->output_section) + && ! bfd_is_und_section (osec->output_section)) + dynindx = elf_section_data (osec->output_section)->dynindx; + else + dynindx = 0; } - if (h != NULL) + /* If we're linking an executable at a fixed address, we + can omit the dynamic relocation as long as the symbol + is defined in the current link unit (which is implied + by its output section not being NULL). */ + if (info->executable && !info->pie + && (!h || BFINFDPIC_SYM_LOCAL (info, h))) { - bfd_boolean dyn; - - off = h->got.offset; - BFD_ASSERT (off != (bfd_vma) - 1); - dyn = elf_hash_table (info)->dynamic_sections_created; - - if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) - || (info->shared - && (info->symbolic - || h->dynindx == -1 - || h->forced_local) - && h->def_regular)) + if (osec) + addend += osec->output_section->vma; + if (IS_FDPIC (input_bfd) + && (bfd_get_section_flags (output_bfd, + input_section->output_section) + & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) { - /* This is actually a static link, or it is a - -Bsymbolic link and the symbol is defined - locally, or the symbol was forced to be local - because of a version file.. We must initialize - this entry in the global offset table. Since - the offset must always be a multiple of 4, we - use the least significant bit to record whether - we have initialized it already. - - When doing a dynamic link, we create a .rela.got - relocation entry to initialize the value. This - is done in the finish_dynamic_symbol routine. */ - if ((off & 1) != 0) - off &= ~1; - else + if (_bfinfdpic_osec_readonly_p (output_bfd, + input_section + ->output_section)) { - bfd_put_32 (output_bfd, relocation, - sgot->contents + off); - h->got.offset |= 1; + info->callbacks->warning + (info, + _("cannot emit fixups in read-only section"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; + } + if (!h || h->root.type != bfd_link_hash_undefweak) + { + /* Only output a reloc for a not deleted entry. */ + if (offset >= (bfd_vma)-2) + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section + (info), -1, picrel); + else + _bfinfdpic_add_rofixup (output_bfd, + bfinfdpic_gotfixup_section + (info), + offset + input_section + ->output_section->vma + + input_section->output_offset, + picrel); + + if (r_type == R_BFIN_FUNCDESC_VALUE) + { + if (offset >= (bfd_vma)-2) + _bfinfdpic_add_rofixup + (output_bfd, + bfinfdpic_gotfixup_section (info), + -1, picrel); + else + _bfinfdpic_add_rofixup + (output_bfd, + bfinfdpic_gotfixup_section (info), + offset + input_section->output_section->vma + + input_section->output_offset + 4, picrel); + } } } - else - unresolved_reloc = FALSE; } else { - BFD_ASSERT (local_got_offsets != NULL); - off = local_got_offsets[r_symndx]; - BFD_ASSERT (off != (bfd_vma) - 1); - - /* The offset must always be a multiple of 4. We use - the least significant bit to record whether we have - already generated the necessary reloc. */ - if ((off & 1) != 0) - off &= ~1; - else + if ((bfd_get_section_flags (output_bfd, + input_section->output_section) + & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)) { - bfd_put_32 (output_bfd, relocation, sgot->contents + off); - - if (info->shared) + if (_bfinfdpic_osec_readonly_p (output_bfd, + input_section + ->output_section)) { - asection *s; - Elf_Internal_Rela outrel; - bfd_byte *loc; - - s = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (s != NULL); - - outrel.r_offset = (sgot->output_section->vma - + sgot->output_offset + off); - outrel.r_info = - ELF32_R_INFO (0, R_pcrel24); - outrel.r_addend = relocation; - loc = s->contents; - loc += - s->reloc_count++ * sizeof (Elf32_External_Rela); - bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); + info->callbacks->warning + (info, + _("cannot emit dynamic relocations in read-only section"), + name, input_bfd, input_section, rel->r_offset); + return FALSE; } - - local_got_offsets[r_symndx] |= 1; + /* Only output a reloc for a not deleted entry. */ + if (offset >= (bfd_vma)-2) + _bfinfdpic_add_dyn_reloc (output_bfd, + bfinfdpic_gotrel_section (info), + 0, R_unused0, dynindx, addend, picrel); + else + _bfinfdpic_add_dyn_reloc (output_bfd, + bfinfdpic_gotrel_section (info), + offset + + input_section + ->output_section->vma + + input_section->output_offset, + r_type, dynindx, addend, picrel); } + else if (osec) + addend += osec->output_section->vma; + /* We want the addend in-place because dynamic + relocations are REL. Setting relocation to it + should arrange for it to be installed. */ + relocation = addend - rel->r_addend; } - relocation = sgot->output_offset + off; - rel->r_addend = 0; - /* bfin : preg = [preg + 17bitdiv4offset] relocation is div by 4. */ - relocation /= 4; + if (r_type == R_BFIN_FUNCDESC_VALUE && offset < (bfd_vma)-2) + { + /* If we've omitted the dynamic relocation, just emit + the fixed addresses of the symbol and of the local + GOT base offset. */ + if (info->executable && !info->pie + && (!h || BFINFDPIC_SYM_LOCAL (info, h))) + bfd_put_32 (output_bfd, + bfinfdpic_got_section (info)->output_section->vma + + bfinfdpic_got_section (info)->output_offset + + bfinfdpic_got_initial_offset (info), + contents + rel->r_offset + 4); + else + /* A function descriptor used for lazy or local + resolving is initialized such that its high word + contains the output section index in which the + PLT entries are located, and the low word + contains the offset of the lazy PLT entry entry + point into that section. */ + bfd_put_32 (output_bfd, + h && ! BFINFDPIC_SYM_LOCAL (info, h) + ? 0 + : _bfinfdpic_osec_to_segment (output_bfd, + sec + ->output_section), + contents + rel->r_offset + 4); + } } - goto do_default; - - case R_pcrel24: - case R_pcrel24_jump_l: - { - bfd_vma x; - - relocation += rel->r_addend; - - /* Perform usual pc-relative correction. */ - relocation -= input_section->output_section->vma + input_section->output_offset; - relocation -= address; - - /* We are getting reloc_entry->address 2 byte off from - the start of instruction. Assuming absolute postion - of the reloc data. But, following code had been written assuming - reloc address is starting at begining of instruction. - To compensate that I have increased the value of - relocation by 1 (effectively 2) and used the addr -2 instead of addr. */ + check_segment[0] = check_segment[1] = got_segment; + break; - relocation += 2; - address -= 2; + default: + check_segment[0] = isec_segment; + check_segment[1] = sec + ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section) + : (unsigned)-1; + break; + } - relocation >>= 1; + if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd)) + { +#if 1 /* If you take this out, remove the #error from fdpic-static-6.d + in the ld testsuite. */ + /* This helps catch problems in GCC while we can't do more + than static linking. The idea is to test whether the + input file basename is crt0.o only once. */ + if (silence_segment_error == 1) + silence_segment_error = + (strlen (input_bfd->filename) == 6 + && strcmp (input_bfd->filename, "crt0.o") == 0) + || (strlen (input_bfd->filename) > 6 + && strcmp (input_bfd->filename + + strlen (input_bfd->filename) - 7, + "/crt0.o") == 0) + ? -1 : 0; +#endif + if (!silence_segment_error + /* We don't want duplicate errors for undefined + symbols. */ + && !(picrel && picrel->symndx == -1 + && picrel->d.h->root.type == bfd_link_hash_undefined)) + info->callbacks->warning + (info, + (info->shared || info->pie) + ? _("relocations between different segments are not supported") + : _("warning: relocation references a different segment"), + name, input_bfd, input_section, rel->r_offset); + if (!silence_segment_error && (info->shared || info->pie)) + return FALSE; + elf_elfheader (output_bfd)->e_flags |= EF_BFIN_PIC; + } - x = bfd_get_16 (input_bfd, contents + address); - x = (x & 0xff00) | ((relocation >> 16) & 0xff); - bfd_put_16 (input_bfd, x, contents + address); + switch (r_type) + { + case R_BFIN_GOTOFFHI: + /* We need the addend to be applied before we shift the + value right. */ + relocation += rel->r_addend; + /* Fall through. */ + case R_BFIN_GOTHI: + case R_BFIN_FUNCDESC_GOTHI: + case R_BFIN_FUNCDESC_GOTOFFHI: + relocation >>= 16; + /* Fall through. */ - x = bfd_get_16 (input_bfd, contents + address + 2); - x = relocation & 0xFFFF; - bfd_put_16 (input_bfd, x, contents + address + 2); - r = bfd_reloc_ok; - } + case R_BFIN_GOTLO: + case R_BFIN_FUNCDESC_GOTLO: + case R_BFIN_GOTOFFLO: + case R_BFIN_FUNCDESC_GOTOFFLO: + relocation &= 0xffff; break; default: - do_default: - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, address, - relocation, rel->r_addend); - break; } - /* Dynamic relocs are not propagated for SEC_DEBUGGING sections - because such sections are not SEC_ALLOC and thus ld.so will - not process them. */ - if (unresolved_reloc - && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic)) + switch (r_type) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"), - input_bfd, - input_section, (long) rel->r_offset, h->root.root.string); - return FALSE; + case R_pcrel24: + case R_pcrel24_jump_l: + if (! IS_FDPIC (output_bfd) || ! picrel->plt) + break; + /* Fall through. */ + + /* When referencing a GOT entry, a function descriptor or a + PLT, we don't want the addend to apply to the reference, + but rather to the referenced symbol. The actual entry + will have already been created taking the addend into + account, so cancel it out here. */ + case R_BFIN_GOT17M4: + case R_BFIN_GOTHI: + case R_BFIN_GOTLO: + case R_BFIN_FUNCDESC_GOT17M4: + case R_BFIN_FUNCDESC_GOTHI: + case R_BFIN_FUNCDESC_GOTLO: + case R_BFIN_FUNCDESC_GOTOFF17M4: + case R_BFIN_FUNCDESC_GOTOFFHI: + case R_BFIN_FUNCDESC_GOTOFFLO: + /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF17M4 + here, since we do want to apply the addend to the others. + Note that we've applied the addend to GOTOFFHI before we + shifted it right. */ + case R_BFIN_GOTOFFHI: + relocation -= rel->r_addend; + break; + + default: + break; } + r = bfin_final_link_relocate (rel, howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + if (r != bfd_reloc_ok) { - const char *name; + const char * msg = (const char *) NULL; - if (h != NULL) - name = h->root.root.string; - else + switch (r) { - name = bfd_elf_string_from_elf_section (input_bfd, - symtab_hdr->sh_link, - sym->st_name); - if (name == NULL) - return FALSE; - if (*name == '\0') - name = bfd_section_name (input_bfd, sec); - } + case bfd_reloc_overflow: + r = info->callbacks->reloc_overflow + (info, (h ? &h->root : NULL), name, howto->name, + (bfd_vma) 0, input_bfd, input_section, rel->r_offset); + break; - if (r == bfd_reloc_overflow) - { - if (!(info->callbacks->reloc_overflow - (info, (h ? &h->root : NULL), name, howto->name, - (bfd_vma) 0, input_bfd, input_section, rel->r_offset))) - return FALSE; - } - else - { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): reloc against `%s': error %d"), - input_bfd, input_section, - (long) rel->r_offset, name, (int) r); - return FALSE; - } - } - } + case bfd_reloc_undefined: + r = info->callbacks->undefined_symbol + (info, name, input_bfd, input_section, rel->r_offset, TRUE); + break; - return TRUE; -} + case bfd_reloc_outofrange: + msg = _("internal error: out of range error"); + break; -static asection * -bfin_gc_mark_hook (asection * sec, - struct bfd_link_info *info ATTRIBUTE_UNUSED, - Elf_Internal_Rela * rel, - struct elf_link_hash_entry *h, - Elf_Internal_Sym * sym) -{ - if (h != NULL) - { - switch (ELF32_R_TYPE (rel->r_info)) - { + case bfd_reloc_notsupported: + msg = _("internal error: unsupported relocation error"); + break; - case R_BFIN_GNU_VTINHERIT: - case R_BFIN_GNU_VTENTRY: - break; + case bfd_reloc_dangerous: + msg = _("internal error: dangerous relocation"); + break; - default: - switch (h->root.type) - { default: + msg = _("internal error: unknown error"); break; + } - case bfd_link_hash_defined: - case bfd_link_hash_defweak: - return h->root.u.def.section; + if (msg) + r = info->callbacks->warning + (info, msg, name, input_bfd, input_section, rel->r_offset); - case bfd_link_hash_common: - return h->root.u.c.p->section; - } + if (! r) + return FALSE; } } - else - return bfd_section_from_elf_index (sec->owner, sym->st_shndx); - - return NULL; -} - -/* Update the got entry reference counts for the section being removed. */ - -static bfd_boolean -bfinfdpic_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED, - struct bfd_link_info *info ATTRIBUTE_UNUSED, - asection *sec ATTRIBUTE_UNUSED, - const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED) -{ return TRUE; } -/* Update the got entry reference counts for the section being removed. */ +/* Update the relocation information for the relocations of the section + being removed. */ static bfd_boolean -bfin_gc_sweep_hook (bfd * abfd, - struct bfd_link_info *info, - asection * sec, - const Elf_Internal_Rela * relocs) +bfinfdpic_gc_sweep_hook (bfd *abfd, + struct bfd_link_info *info, + asection *sec, + const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes; - bfd_signed_vma *local_got_refcounts; - const Elf_Internal_Rela *rel, *relend; - bfd *dynobj; - asection *sgot; - asection *srelgot; + struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + const Elf_Internal_Rela *rel; + const Elf_Internal_Rela *rel_end; + struct bfinfdpic_relocs_info *picrel; - dynobj = elf_hash_table (info)->dynobj; - if (dynobj == NULL) - return TRUE; + BFD_ASSERT (IS_FDPIC (abfd)); symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); - local_got_refcounts = elf_local_got_refcounts (abfd); - - sgot = bfd_get_section_by_name (dynobj, ".got"); - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof(Elf32_External_Sym); + if (!elf_bad_symtab (abfd)) + sym_hashes_end -= symtab_hdr->sh_info; - relend = relocs + sec->reloc_count; - for (rel = relocs; rel < relend; rel++) + rel_end = relocs + sec->reloc_count; + for (rel = relocs; rel < rel_end; rel++) { - unsigned long r_symndx; struct elf_link_hash_entry *h; + unsigned long r_symndx; + + r_symndx = ELF32_R_SYM (rel->r_info); + if (r_symndx < symtab_hdr->sh_info) + h = NULL; + else + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + + if (h != NULL) + picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info (info), + abfd, h, + rel->r_addend, NO_INSERT); + else + picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info + (info), abfd, r_symndx, + rel->r_addend, NO_INSERT); + + if (!picrel) + return TRUE; switch (ELF32_R_TYPE (rel->r_info)) - { - case R_got: - r_symndx = ELF32_R_SYM (rel->r_info); - if (r_symndx >= symtab_hdr->sh_info) - { - h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - if (h->got.refcount > 0) - { - --h->got.refcount; - if (h->got.refcount == 0) - { - /* We don't need the .got entry any more. */ - sgot->size -= 4; - srelgot->size -= sizeof (Elf32_External_Rela); - } - } - } - else if (local_got_refcounts != NULL) - { - if (local_got_refcounts[r_symndx] > 0) - { - --local_got_refcounts[r_symndx]; - if (local_got_refcounts[r_symndx] == 0) - { - /* We don't need the .got entry any more. */ - sgot->size -= 4; - if (info->shared) - srelgot->size -= sizeof (Elf32_External_Rela); - } - } - } + { + case R_pcrel24: + case R_pcrel24_jump_l: + picrel->call--; + break; + + case R_BFIN_FUNCDESC_VALUE: + picrel->relocsfdv--; + if (bfd_get_section_flags (abfd, sec) & SEC_ALLOC) + picrel->relocs32++; + /* Fall through. */ + + case R_byte4_data: + picrel->sym--; + if (bfd_get_section_flags (abfd, sec) & SEC_ALLOC) + picrel->relocs32--; + break; + + case R_BFIN_GOT17M4: + picrel->got17m4--; + break; + + case R_BFIN_GOTHI: + case R_BFIN_GOTLO: + picrel->gothilo--; + break; + + case R_BFIN_FUNCDESC_GOT17M4: + picrel->fdgot17m4--; + break; + + case R_BFIN_FUNCDESC_GOTHI: + case R_BFIN_FUNCDESC_GOTLO: + picrel->fdgothilo--; + break; + + case R_BFIN_GOTOFF17M4: + case R_BFIN_GOTOFFHI: + case R_BFIN_GOTOFFLO: + picrel->gotoff--; + break; + + case R_BFIN_FUNCDESC_GOTOFF17M4: + picrel->fdgoff17m4--; + break; + + case R_BFIN_FUNCDESC_GOTOFFHI: + case R_BFIN_FUNCDESC_GOTOFFLO: + picrel->fdgoffhilo--; break; + + case R_BFIN_FUNCDESC: + picrel->fd--; + picrel->relocsfd--; + break; + default: break; - } + } } + return TRUE; } @@ -3461,37 +3582,6 @@ elf32_bfinfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) return TRUE; } -/* The name of the dynamic interpreter. This is put in the .interp - section. */ - -#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" - -#define DEFAULT_STACK_SIZE 0x20000 - -/* This structure is used to collect the number of entries present in - each addressable range of the got. */ -struct _bfinfdpic_dynamic_got_info -{ - /* Several bits of information about the current link. */ - struct bfd_link_info *info; - /* Total size needed for GOT entries within the 18- or 32-bit - ranges. */ - bfd_vma got17m4, gothilo; - /* Total size needed for function descriptor entries within the 18- - or 32-bit ranges. */ - bfd_vma fd17m4, fdhilo; - /* Total size needed function descriptor entries referenced in PLT - entries, that would be profitable to place in offsets close to - the PIC register. */ - bfd_vma fdplt; - /* Total size needed by lazy PLT entries. */ - bfd_vma lzplt; - /* Number of relocations carried over from input object files. */ - unsigned long relocs; - /* Number of fixups introduced by relocations in input object files. */ - unsigned long fixups; -}; - /* Compute the total GOT size required by each symbol in each range. Symbols may require up to 4 words in the GOT: an entry pointing to the symbol, an entry pointing to its function descriptor, and a @@ -4520,7 +4610,7 @@ bfinfdpic_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + struct elf_link_hash_entry **sym_hashes; const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; bfd *dynobj; @@ -4531,9 +4621,6 @@ bfinfdpic_check_relocs (bfd *abfd, struct bfd_link_info *info, symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); - sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof(Elf32_External_Sym); - if (!elf_bad_symtab (abfd)) - sym_hashes_end -= symtab_hdr->sh_info; dynobj = elf_hash_table (info)->dynobj; rel_end = relocs + sec->reloc_count; @@ -4616,7 +4703,7 @@ bfinfdpic_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_pcrel24: case R_pcrel24_jump_l: if (IS_FDPIC (abfd)) - picrel->call = 1; + picrel->call++; break; case R_BFIN_FUNCDESC_VALUE: @@ -4629,46 +4716,46 @@ bfinfdpic_check_relocs (bfd *abfd, struct bfd_link_info *info, if (! IS_FDPIC (abfd)) break; - picrel->sym = 1; + picrel->sym++; if (bfd_get_section_flags (abfd, sec) & SEC_ALLOC) picrel->relocs32++; break; case R_BFIN_GOT17M4: - picrel->got17m4 = 1; + picrel->got17m4++; break; case R_BFIN_GOTHI: case R_BFIN_GOTLO: - picrel->gothilo = 1; + picrel->gothilo++; break; case R_BFIN_FUNCDESC_GOT17M4: - picrel->fdgot17m4 = 1; + picrel->fdgot17m4++; break; case R_BFIN_FUNCDESC_GOTHI: case R_BFIN_FUNCDESC_GOTLO: - picrel->fdgothilo = 1; + picrel->fdgothilo++; break; case R_BFIN_GOTOFF17M4: case R_BFIN_GOTOFFHI: case R_BFIN_GOTOFFLO: - picrel->gotoff = 1; + picrel->gotoff++; break; case R_BFIN_FUNCDESC_GOTOFF17M4: - picrel->fdgoff17m4 = 1; + picrel->fdgoff17m4++; break; case R_BFIN_FUNCDESC_GOTOFFHI: case R_BFIN_FUNCDESC_GOTOFFLO: - picrel->fdgoffhilo = 1; + picrel->fdgoffhilo++; break; case R_BFIN_FUNCDESC: - picrel->fd = 1; + picrel->fd++; picrel->relocsfd++; break; @@ -4682,7 +4769,9 @@ bfinfdpic_check_relocs (bfd *abfd, struct bfd_link_info *info, /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_BFIN_GNU_VTENTRY: - if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + BFD_ASSERT (h != NULL); + if (h != NULL + && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) return FALSE; break; @@ -4736,6 +4825,10 @@ bfin_elf_copy_private_bfd_data (bfd *ibfd, bfd *obfd) elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; elf_flags_init (obfd) = TRUE; + + /* Copy object attributes. */ + _bfd_elf_copy_obj_attributes (ibfd, obfd); + return TRUE; } @@ -4818,8 +4911,7 @@ elf32_bfin_print_private_bfd_data (bfd * abfd, PTR ptr) static bfd_boolean elf32_bfin_merge_private_bfd_data (bfd *ibfd, bfd *obfd) { - flagword old_flags, old_partial; - flagword new_flags, new_partial; + flagword old_flags, new_flags; bfd_boolean error = FALSE; new_flags = elf_elfheader (ibfd)->e_flags; @@ -4837,37 +4929,10 @@ elf32_bfin_merge_private_bfd_data (bfd *ibfd, bfd *obfd) if (!elf_flags_init (obfd)) /* First call, no flags set. */ { elf_flags_init (obfd) = TRUE; - old_flags = new_flags; - } - - else if (new_flags == old_flags) /* Compatible flags are ok. */ - ; - - else /* Possibly incompatible flags. */ - { - /* We don't have to do anything if the pic flags are the same, or the new - module(s) were compiled with -mlibrary-pic. */ - new_partial = (new_flags & EF_BFIN_PIC_FLAGS); - old_partial = (old_flags & EF_BFIN_PIC_FLAGS); - if (new_partial == old_partial) - ; - - /* If we have mixtures of -fpic and -fPIC, or in both bits. */ - else if (new_partial != 0 && old_partial != 0) - old_flags |= new_partial; - - /* One module was compiled for pic and the other was not, see if we have - had any relocations that are not pic-safe. */ - else - old_flags |= new_partial; - + elf_elfheader (obfd)->e_flags = new_flags; } - /* Update the old flags now with changes made above. */ - elf_elfheader (obfd)->e_flags = old_flags; - - if (((new_flags & EF_BFIN_FDPIC) == 0) - != (! IS_FDPIC (ibfd))) + if (((new_flags & EF_BFIN_FDPIC) == 0) != (! IS_FDPIC (obfd))) { error = TRUE; if (IS_FDPIC (obfd)) @@ -5501,6 +5566,14 @@ error_return: free (internal_relocs); return FALSE; } + +struct bfd_elf_special_section const elf32_bfin_special_sections[] = +{ + { ".l1.text", 8, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR }, + { ".l1.data", 8, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, + { NULL, 0, 0, 0, 0 } +}; + #define TARGET_LITTLE_SYM bfd_elf32_bfin_vec #define TARGET_LITTLE_NAME "elf32-bfin" @@ -5510,6 +5583,8 @@ error_return: #define elf_symbol_leading_char '_' #define bfd_elf32_bfd_reloc_type_lookup bfin_bfd_reloc_type_lookup +#define bfd_elf32_bfd_reloc_name_lookup \ + bfin_bfd_reloc_name_lookup #define elf_info_to_howto bfin_info_to_howto #define elf_info_to_howto_rel 0 #define elf_backend_object_p elf32_bfin_object_p @@ -5547,6 +5622,7 @@ error_return: elf32_bfin_print_private_bfd_data #define elf_backend_reloc_type_class elf32_bfin_reloc_type_class #define elf_backend_can_gc_sections 1 +#define elf_backend_special_sections elf32_bfin_special_sections #define elf_backend_can_refcount 1 #define elf_backend_want_got_plt 0 #define elf_backend_plt_readonly 1