X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felfxx-mips.c;h=72516f71f1c3743de350754cb231be5d077581d0;hb=45d6a902ae8c0f9a0dc22ba33e61f3e6f8bd6492;hp=a2fcd038bc3b9d3cd8e15a3f8bd4b72b04f4e2e2;hpb=f0abc2a11f47c3ecdfe0b54421092d17c70fc5f2;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index a2fcd038bc..72516f71f1 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -9,21 +9,21 @@ Traditional MIPS targets support added by Koundinya.K, Dansk Data Elektronik & Operations Research Group. -This file is part of BFD, the Binary File Descriptor library. + 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 -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This file handles functionality common to the different MIPS ABI's. */ @@ -49,16 +49,25 @@ struct mips_got_entry { /* The input bfd in which the symbol is defined. */ bfd *abfd; - /* The index of the symbol, as stored in the relocation r_info. If - it's -1, the addend is a complete address into the - executable/shared library. */ - unsigned long symndx; - /* The addend of the relocation that should be added to the symbol - value. */ - bfd_vma addend; + /* The index of the symbol, as stored in the relocation r_info, if + we have a local symbol; -1 otherwise. */ + long symndx; + union + { + /* If abfd == NULL, an address that must be stored in the got. */ + bfd_vma address; + /* If abfd != NULL && symndx != -1, the addend of the relocation + that should be added to the symbol value. */ + bfd_vma addend; + /* If abfd != NULL && symndx == -1, the hash table entry + corresponding to a global symbol in the got (or, local, if + h->forced_local). */ + struct mips_elf_link_hash_entry *h; + } d; /* The offset from the beginning of the .got section to the entry - corresponding to this symbol+addend. */ - unsigned long gotidx; + corresponding to this symbol+addend. If it's a global symbol + whose offset is yet to be decided, it's going to be -1. */ + long gotidx; }; /* This structure is used to hold .got information when linking. */ @@ -76,6 +85,56 @@ struct mips_got_info unsigned int assigned_gotno; /* A hash table holding members of the got. */ struct htab *got_entries; + /* A hash table mapping input bfds to other mips_got_info. NULL + unless multi-got was necessary. */ + struct htab *bfd2got; + /* In multi-got links, a pointer to the next got (err, rather, most + of the time, it points to the previous got). */ + struct mips_got_info *next; +}; + +/* Map an input bfd to a got in a multi-got link. */ + +struct mips_elf_bfd2got_hash { + bfd *bfd; + struct mips_got_info *g; +}; + +/* Structure passed when traversing the bfd2got hash table, used to + create and merge bfd's gots. */ + +struct mips_elf_got_per_bfd_arg +{ + /* A hashtable that maps bfds to gots. */ + htab_t bfd2got; + /* The output bfd. */ + bfd *obfd; + /* The link information. */ + struct bfd_link_info *info; + /* A pointer to the primary got, i.e., the one that's going to get + the implicit relocations from DT_MIPS_LOCAL_GOTNO and + DT_MIPS_GOTSYM. */ + struct mips_got_info *primary; + /* A non-primary got we're trying to merge with other input bfd's + gots. */ + struct mips_got_info *current; + /* The maximum number of got entries that can be addressed with a + 16-bit offset. */ + unsigned int max_count; + /* The number of local and global entries in the primary got. */ + unsigned int primary_count; + /* The number of local and global entries in the current got. */ + unsigned int current_count; +}; + +/* Another structure used to pass arguments for got entries traversal. */ + +struct mips_elf_set_global_got_offset_arg +{ + struct mips_got_info *g; + int value; + unsigned int needed_relocs; + struct bfd_link_info *info; }; struct _mips_elf_section_data @@ -89,7 +148,7 @@ struct _mips_elf_section_data }; #define mips_elf_section_data(sec) \ - ((struct _mips_elf_section_data *) (sec)->used_by_bfd) + ((struct _mips_elf_section_data *) elf_section_data (sec)) /* This structure is passed to mips_elf_sort_hash_table_f when sorting the dynamic symbols. */ @@ -102,6 +161,11 @@ struct mips_elf_hash_sort_data /* The least dynamic symbol table index corresponding to a symbol with a GOT entry. */ long min_got_dynindx; + /* The greatest dynamic symbol table index corresponding to a symbol + with a GOT entry that is not referenced (e.g., a dynamic symbol + with dynamic relocations pointing to it from non-primary + GOTs). */ + long max_unref_got_dynindx; /* The greatest dynamic symbol table index not corresponding to a symbol without a GOT entry. */ long max_non_got_dynindx; @@ -295,17 +359,17 @@ typedef struct loader for use by the static exception system. */ typedef struct runtime_pdr { - bfd_vma adr; /* memory address of start of procedure */ - long regmask; /* save register mask */ - long regoffset; /* save register offset */ - long fregmask; /* save floating point register mask */ - long fregoffset; /* save floating point register offset */ - long frameoffset; /* frame size */ - short framereg; /* frame pointer register */ - short pcreg; /* offset or reg of return pc */ - long irpss; /* index into the runtime string table */ + bfd_vma adr; /* Memory address of start of procedure. */ + long regmask; /* Save register mask. */ + long regoffset; /* Save register offset. */ + long fregmask; /* Save floating point register mask. */ + long fregoffset; /* Save floating point register offset. */ + long frameoffset; /* Frame size. */ + short framereg; /* Frame pointer register. */ + short pcreg; /* Offset or reg of return pc. */ + long irpss; /* Index into the runtime string table. */ long reserved; - struct exception_info *exception_info;/* pointer to exception array */ + struct exception_info *exception_info;/* Pointer to exception array. */ } RPDR, *pRPDR; #define cbRPDR sizeof (RPDR) #define rpdNil ((pRPDR) 0) @@ -335,30 +399,36 @@ static void bfd_mips_elf_swap_msym_out PARAMS ((bfd *, const Elf32_Internal_Msym *, Elf32_External_Msym *)); static int sort_dynamic_relocs PARAMS ((const void *, const void *)); +static int sort_dynamic_relocs_64 + PARAMS ((const void *, const void *)); static bfd_boolean mips_elf_output_extsym PARAMS ((struct mips_elf_link_hash_entry *, PTR)); static int gptab_compare PARAMS ((const void *, const void *)); -static asection * mips_elf_got_section PARAMS ((bfd *)); +static asection * mips_elf_rel_dyn_section PARAMS ((bfd *, bfd_boolean)); +static asection * mips_elf_got_section PARAMS ((bfd *, bfd_boolean)); static struct mips_got_info *mips_elf_got_info PARAMS ((bfd *, asection **)); +static long mips_elf_get_global_gotsym_index PARAMS ((bfd *abfd)); static bfd_vma mips_elf_local_got_index - PARAMS ((bfd *, struct bfd_link_info *, bfd_vma)); + PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma)); static bfd_vma mips_elf_global_got_index - PARAMS ((bfd *, struct elf_link_hash_entry *)); + PARAMS ((bfd *, bfd *, struct elf_link_hash_entry *)); static bfd_vma mips_elf_got_page - PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *)); + PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *)); static bfd_vma mips_elf_got16_entry - PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean)); + PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean)); static bfd_vma mips_elf_got_offset_from_index - PARAMS ((bfd *, bfd *, bfd_vma)); + PARAMS ((bfd *, bfd *, bfd *, bfd_vma)); static struct mips_got_entry *mips_elf_create_local_got_entry - PARAMS ((bfd *, struct mips_got_info *, asection *, bfd_vma)); + PARAMS ((bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma)); static bfd_boolean mips_elf_sort_hash_table PARAMS ((struct bfd_link_info *, unsigned long)); static bfd_boolean mips_elf_sort_hash_table_f PARAMS ((struct mips_elf_link_hash_entry *, PTR)); +static bfd_boolean mips_elf_record_local_got_symbol + PARAMS ((bfd *, long, bfd_vma, struct mips_got_info *)); static bfd_boolean mips_elf_record_global_got_symbol - PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *, + PARAMS ((struct elf_link_hash_entry *, bfd *, struct bfd_link_info *, struct mips_got_info *)); static const Elf_Internal_Rela *mips_elf_next_relocation PARAMS ((bfd *, unsigned int, const Elf_Internal_Rela *, @@ -373,7 +443,7 @@ static bfd_vma mips_elf_highest PARAMS ((bfd_vma)); static bfd_boolean mips_elf_create_compact_rel_section PARAMS ((bfd *, struct bfd_link_info *)); static bfd_boolean mips_elf_create_got_section - PARAMS ((bfd *, struct bfd_link_info *)); + PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean)); static asection *mips_elf_create_msym_section PARAMS ((bfd *)); static bfd_reloc_status_type mips_elf_calculate_relocation @@ -401,9 +471,28 @@ static void mips_elf_irix6_finish_dynamic_symbol PARAMS ((bfd *, const char *, Elf_Internal_Sym *)); static bfd_boolean mips_mach_extends_p PARAMS ((unsigned long, unsigned long)); static bfd_boolean mips_32bit_flags_p PARAMS ((flagword)); +static INLINE hashval_t mips_elf_hash_bfd_vma PARAMS ((bfd_vma)); static hashval_t mips_elf_got_entry_hash PARAMS ((const PTR)); static int mips_elf_got_entry_eq PARAMS ((const PTR, const PTR)); +static bfd_boolean mips_elf_multi_got + PARAMS ((bfd *, struct bfd_link_info *, struct mips_got_info *, + asection *, bfd_size_type)); +static hashval_t mips_elf_multi_got_entry_hash PARAMS ((const PTR)); +static int mips_elf_multi_got_entry_eq PARAMS ((const PTR, const PTR)); +static hashval_t mips_elf_bfd2got_entry_hash PARAMS ((const PTR)); +static int mips_elf_bfd2got_entry_eq PARAMS ((const PTR, const PTR)); +static int mips_elf_make_got_per_bfd PARAMS ((void **, void *)); +static int mips_elf_merge_gots PARAMS ((void **, void *)); +static int mips_elf_set_global_got_offset PARAMS ((void**, void *)); +static int mips_elf_resolve_final_got_entry PARAMS ((void**, void *)); +static void mips_elf_resolve_final_got_entries + PARAMS ((struct mips_got_info *)); +static bfd_vma mips_elf_adjust_gp + PARAMS ((bfd *, struct mips_got_info *, bfd *)); +static struct mips_got_info *mips_elf_got_for_ibfd + PARAMS ((struct mips_got_info *, bfd *)); + /* This will be used when we sort the dynamic relocation records. */ static bfd *reldyn_sorting_bfd; @@ -453,7 +542,7 @@ static bfd *reldyn_sorting_bfd; /* The default alignment for sections, as a power of two. */ #define MIPS_ELF_LOG_FILE_ALIGN(abfd) \ - (get_elf_backend_data (abfd)->s->file_align == 8 ? 3 : 2) + (get_elf_backend_data (abfd)->s->log_file_align) /* Get word-sized data. */ #define MIPS_ELF_GET_WORD(abfd, ptr) \ @@ -505,15 +594,19 @@ static bfd *reldyn_sorting_bfd; /* The number of local .got entries we reserve. */ #define MIPS_RESERVED_GOTNO (2) +/* The offset of $gp from the beginning of the .got section. */ +#define ELF_MIPS_GP_OFFSET(abfd) (0x7ff0) + +/* The maximum size of the GOT for it to be addressable using 16-bit + offsets from $gp. */ +#define MIPS_ELF_GOT_MAX_SIZE(abfd) (ELF_MIPS_GP_OFFSET(abfd) + 0x7fff) + /* Instructions which appear in a stub. For some reason the stub is slightly different on an SGI system. */ -#define ELF_MIPS_GP_OFFSET(abfd) (SGI_COMPAT (abfd) ? 0x7ff0 : 0x8000) #define STUB_LW(abfd) \ - (SGI_COMPAT (abfd) \ - ? (ABI_64_P (abfd) \ - ? 0xdf998010 /* ld t9,0x8010(gp) */ \ - : 0x8f998010) /* lw t9,0x8010(gp) */ \ - : 0x8f998010) /* lw t9,0x8000(gp) */ + ((ABI_64_P (abfd) \ + ? 0xdf998010 /* ld t9,0x8010(gp) */ \ + : 0x8f998010)) /* lw t9,0x8010(gp) */ #define STUB_MOVE(abfd) \ (SGI_COMPAT (abfd) ? 0x03e07825 : 0x03e07821) /* move t7,ra */ #define STUB_JALR 0x0320f809 /* jal t9 */ @@ -1239,6 +1332,26 @@ sort_dynamic_relocs (arg1, arg2) return ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info); } +/* Like sort_dynamic_relocs, but used for elf64 relocations. */ + +static int +sort_dynamic_relocs_64 (arg1, arg2) + const PTR arg1; + const PTR arg2; +{ + Elf_Internal_Rela int_reloc1[3]; + Elf_Internal_Rela int_reloc2[3]; + + (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in) + (reldyn_sorting_bfd, arg1, int_reloc1); + (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in) + (reldyn_sorting_bfd, arg2, int_reloc2); + + return (ELF64_R_SYM (int_reloc1[0].r_info) + - ELF64_R_SYM (int_reloc2[0].r_info)); +} + + /* This routine is used to write out ECOFF debugging external symbol information. It is called via mips_elf_link_hash_traverse. The ECOFF external symbol information must match the ELF external @@ -1447,17 +1560,36 @@ gptab_compare (p1, p2) } /* Functions to manage the got entry hash table. */ + +/* Use all 64 bits of a bfd_vma for the computation of a 32-bit + hash number. */ + +static INLINE hashval_t +mips_elf_hash_bfd_vma (addr) + bfd_vma addr; +{ +#ifdef BFD64 + return addr + (addr >> 32); +#else + return addr; +#endif +} + +/* got_entries only match if they're identical, except for gotidx, so + use all fields to compute the hash, and compare the appropriate + union members. */ + static hashval_t mips_elf_got_entry_hash (entry_) const PTR entry_; { const struct mips_got_entry *entry = (struct mips_got_entry *)entry_; - return htab_hash_pointer (entry->abfd) + entry->symndx -#ifdef BFD64 - + (entry->addend >> 32) -#endif - + entry->addend; + return entry->symndx + + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address) + : entry->abfd->id + + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend) + : entry->d.h->root.root.root.hash)); } static int @@ -1469,16 +1601,87 @@ mips_elf_got_entry_eq (entry1, entry2) const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2; return e1->abfd == e2->abfd && e1->symndx == e2->symndx - && e1->addend == e2->addend; + && (! e1->abfd ? e1->d.address == e2->d.address + : e1->symndx >= 0 ? e1->d.addend == e2->d.addend + : e1->d.h == e2->d.h); +} + +/* multi_got_entries are still a match in the case of global objects, + even if the input bfd in which they're referenced differs, so the + hash computation and compare functions are adjusted + accordingly. */ + +static hashval_t +mips_elf_multi_got_entry_hash (entry_) + const PTR entry_; +{ + const struct mips_got_entry *entry = (struct mips_got_entry *)entry_; + + return entry->symndx + + (! entry->abfd + ? mips_elf_hash_bfd_vma (entry->d.address) + : entry->symndx >= 0 + ? (entry->abfd->id + + mips_elf_hash_bfd_vma (entry->d.addend)) + : entry->d.h->root.root.root.hash); +} + +static int +mips_elf_multi_got_entry_eq (entry1, entry2) + const PTR entry1; + const PTR entry2; +{ + const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1; + const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2; + + return e1->symndx == e2->symndx + && (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend + : e1->abfd == NULL || e2->abfd == NULL + ? e1->abfd == e2->abfd && e1->d.address == e2->d.address + : e1->d.h == e2->d.h); } +/* Returns the dynamic relocation section for DYNOBJ. */ + +static asection * +mips_elf_rel_dyn_section (dynobj, create_p) + bfd *dynobj; + bfd_boolean create_p; +{ + static const char dname[] = ".rel.dyn"; + asection *sreloc; + + sreloc = bfd_get_section_by_name (dynobj, dname); + if (sreloc == NULL && create_p) + { + sreloc = bfd_make_section (dynobj, dname); + if (sreloc == NULL + || ! bfd_set_section_flags (dynobj, sreloc, + (SEC_ALLOC + | SEC_LOAD + | SEC_HAS_CONTENTS + | SEC_IN_MEMORY + | SEC_LINKER_CREATED + | SEC_READONLY)) + || ! bfd_set_section_alignment (dynobj, sreloc, + 4)) + return NULL; + } + return sreloc; +} + /* Returns the GOT section for ABFD. */ static asection * -mips_elf_got_section (abfd) +mips_elf_got_section (abfd, maybe_excluded) bfd *abfd; + bfd_boolean maybe_excluded; { - return bfd_get_section_by_name (abfd, ".got"); + asection *sgot = bfd_get_section_by_name (abfd, ".got"); + if (sgot == NULL + || (! maybe_excluded && (sgot->flags & SEC_EXCLUDE) != 0)) + return NULL; + return sgot; } /* Returns the GOT information associated with the link indicated by @@ -1493,24 +1696,48 @@ mips_elf_got_info (abfd, sgotp) asection *sgot; struct mips_got_info *g; - sgot = mips_elf_got_section (abfd); + sgot = mips_elf_got_section (abfd, TRUE); BFD_ASSERT (sgot != NULL); BFD_ASSERT (mips_elf_section_data (sgot) != NULL); g = mips_elf_section_data (sgot)->u.got_info; BFD_ASSERT (g != NULL); if (sgotp) - *sgotp = sgot; + *sgotp = (sgot->flags & SEC_EXCLUDE) == 0 ? sgot : NULL; + return g; } +/* Obtain the lowest dynamic index of a symbol that was assigned a + global GOT entry. */ +static long +mips_elf_get_global_gotsym_index (abfd) + bfd *abfd; +{ + asection *sgot; + struct mips_got_info *g; + + if (abfd == NULL) + return 0; + + sgot = mips_elf_got_section (abfd, TRUE); + if (sgot == NULL || mips_elf_section_data (sgot) == NULL) + return 0; + + g = mips_elf_section_data (sgot)->u.got_info; + if (g == NULL || g->global_gotsym == NULL) + return 0; + + return g->global_gotsym->dynindx; +} + /* Returns the GOT offset at which the indicated address can be found. If there is not yet a GOT entry for this value, create one. Returns -1 if no satisfactory GOT offset can be found. */ static bfd_vma -mips_elf_local_got_index (abfd, info, value) - bfd *abfd; +mips_elf_local_got_index (abfd, ibfd, info, value) + bfd *abfd, *ibfd; struct bfd_link_info *info; bfd_vma value; { @@ -1520,7 +1747,7 @@ mips_elf_local_got_index (abfd, info, value) g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, g, sgot, value); + entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value); if (entry) return entry->gotidx; else @@ -1530,18 +1757,38 @@ mips_elf_local_got_index (abfd, info, value) /* Returns the GOT index for the global symbol indicated by H. */ static bfd_vma -mips_elf_global_got_index (abfd, h) - bfd *abfd; +mips_elf_global_got_index (abfd, ibfd, h) + bfd *abfd, *ibfd; struct elf_link_hash_entry *h; { bfd_vma index; asection *sgot; - struct mips_got_info *g; + struct mips_got_info *g, *gg; long global_got_dynindx = 0; - g = mips_elf_got_info (abfd, &sgot); - if (g->global_gotsym != NULL) - global_got_dynindx = g->global_gotsym->dynindx; + gg = g = mips_elf_got_info (abfd, &sgot); + if (g->bfd2got && ibfd) + { + struct mips_got_entry e, *p; + + BFD_ASSERT (h->dynindx >= 0); + + g = mips_elf_got_for_ibfd (g, ibfd); + if (g->next != gg) + { + e.abfd = ibfd; + e.symndx = -1; + e.d.h = (struct mips_elf_link_hash_entry *)h; + + p = (struct mips_got_entry *) htab_find (g->got_entries, &e); + + BFD_ASSERT (p->gotidx > 0); + return p->gotidx; + } + } + + if (gg->global_gotsym != NULL) + global_got_dynindx = gg->global_gotsym->dynindx; /* Once we determine the global GOT entry with the lowest dynamic symbol table index, we must put all dynamic symbols with greater @@ -1562,8 +1809,8 @@ mips_elf_global_got_index (abfd, h) OFFSETP, if it is non-NULL. */ static bfd_vma -mips_elf_got_page (abfd, info, value, offsetp) - bfd *abfd; +mips_elf_got_page (abfd, ibfd, info, value, offsetp) + bfd *abfd, *ibfd; struct bfd_link_info *info; bfd_vma value; bfd_vma *offsetp; @@ -1575,7 +1822,7 @@ mips_elf_got_page (abfd, info, value, offsetp) g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, g, sgot, + entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, (value + 0x8000) & (~(bfd_vma)0xffff)); @@ -1585,7 +1832,7 @@ mips_elf_got_page (abfd, info, value, offsetp) index = entry->gotidx; if (offsetp) - *offsetp = value - entry->addend; + *offsetp = value - entry->d.address; return index; } @@ -1594,8 +1841,8 @@ mips_elf_got_page (abfd, info, value, offsetp) for value. Return the index into the GOT for this entry. */ static bfd_vma -mips_elf_got16_entry (abfd, info, value, external) - bfd *abfd; +mips_elf_got16_entry (abfd, ibfd, info, value, external) + bfd *abfd, *ibfd; struct bfd_link_info *info; bfd_vma value; bfd_boolean external; @@ -1615,7 +1862,7 @@ mips_elf_got16_entry (abfd, info, value, external) g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); - entry = mips_elf_create_local_got_entry (abfd, g, sgot, value); + entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value); if (entry) return entry->gotidx; else @@ -1626,35 +1873,46 @@ mips_elf_got16_entry (abfd, info, value, external) in the GOT. */ static bfd_vma -mips_elf_got_offset_from_index (dynobj, output_bfd, index) +mips_elf_got_offset_from_index (dynobj, output_bfd, input_bfd, index) bfd *dynobj; bfd *output_bfd; + bfd *input_bfd; bfd_vma index; { asection *sgot; bfd_vma gp; + struct mips_got_info *g; - sgot = mips_elf_got_section (dynobj); - gp = _bfd_get_gp_value (output_bfd); - return (sgot->output_section->vma + sgot->output_offset + index - - gp); + g = mips_elf_got_info (dynobj, &sgot); + gp = _bfd_get_gp_value (output_bfd) + + mips_elf_adjust_gp (output_bfd, g, input_bfd); + + return sgot->output_section->vma + sgot->output_offset + index - gp; } /* Create a local GOT entry for VALUE. Return the index of the entry, or -1 if it could not be created. */ static struct mips_got_entry * -mips_elf_create_local_got_entry (abfd, g, sgot, value) - bfd *abfd; - struct mips_got_info *g; +mips_elf_create_local_got_entry (abfd, ibfd, gg, sgot, value) + bfd *abfd, *ibfd; + struct mips_got_info *gg; asection *sgot; bfd_vma value; { struct mips_got_entry entry, **loc; + struct mips_got_info *g; - entry.abfd = abfd; - entry.symndx = (unsigned long)-1; - entry.addend = value; + entry.abfd = NULL; + entry.symndx = -1; + entry.d.address = value; + + g = mips_elf_got_for_ibfd (gg, ibfd); + if (g == NULL) + { + g = mips_elf_got_for_ibfd (gg, abfd); + BFD_ASSERT (g != NULL); + } loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry, INSERT); @@ -1672,7 +1930,7 @@ mips_elf_create_local_got_entry (abfd, g, sgot, value) if (g->assigned_gotno >= g->local_gotno) { - (*loc)->gotidx = (unsigned long)-1; + (*loc)->gotidx = -1; /* We didn't allocate enough space in the GOT. */ (*_bfd_error_handler) (_("not enough GOT space for local GOT entries")); @@ -1704,8 +1962,19 @@ mips_elf_sort_hash_table (info, max_local) dynobj = elf_hash_table (info)->dynobj; + g = mips_elf_got_info (dynobj, NULL); + hsd.low = NULL; - hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount; + hsd.max_unref_got_dynindx = + hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount + /* In the multi-got case, assigned_gotno of the master got_info + indicate the number of entries that aren't referenced in the + primary GOT, but that must have entries because there are + dynamic relocations that reference it. Since they aren't + referenced, we move them to the end of the GOT, so that they + don't prevent other entries that are referenced from getting + too large offsets. */ + - (g->next ? g->assigned_gotno : 0); hsd.max_non_got_dynindx = max_local; mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *) elf_hash_table (info)), @@ -1715,10 +1984,11 @@ mips_elf_sort_hash_table (info, max_local) /* There should have been enough room in the symbol table to accommodate both the GOT and non-GOT symbols. */ BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx); + BFD_ASSERT ((unsigned long)hsd.max_unref_got_dynindx + <= elf_hash_table (info)->dynsymcount); /* Now we know which dynamic symbol has the lowest dynamic symbol table index in the GOT. */ - g = mips_elf_got_info (dynobj, NULL); g->global_gotsym = hsd.low; return TRUE; @@ -1744,7 +2014,17 @@ mips_elf_sort_hash_table_f (h, data) if (h->root.dynindx == -1) return TRUE; - if (h->root.got.offset != 1) + /* Global symbols that need GOT entries that are not explicitly + referenced are marked with got offset 2. Those that are + referenced get a 1, and those that don't need GOT entries get + -1. */ + if (h->root.got.offset == 2) + { + if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx) + hsd->low = (struct elf_link_hash_entry *) h; + h->root.dynindx = hsd->max_unref_got_dynindx++; + } + else if (h->root.got.offset != 1) h->root.dynindx = hsd->max_non_got_dynindx++; else { @@ -1752,46 +2032,643 @@ mips_elf_sort_hash_table_f (h, data) hsd->low = (struct elf_link_hash_entry *) h; } - return TRUE; -} + return TRUE; +} + +/* If H is a symbol that needs a global GOT entry, but has a dynamic + symbol table index lower than any we've seen to date, record it for + posterity. */ + +static bfd_boolean +mips_elf_record_global_got_symbol (h, abfd, info, g) + struct elf_link_hash_entry *h; + bfd *abfd; + struct bfd_link_info *info; + struct mips_got_info *g; +{ + struct mips_got_entry entry, **loc; + + /* A global symbol in the GOT must also be in the dynamic symbol + table. */ + if (h->dynindx == -1) + { + switch (ELF_ST_VISIBILITY (h->other)) + { + case STV_INTERNAL: + case STV_HIDDEN: + _bfd_mips_elf_hide_symbol (info, h, TRUE); + break; + } + if (!bfd_elf32_link_record_dynamic_symbol (info, h)) + return FALSE; + } + + entry.abfd = abfd; + entry.symndx = -1; + entry.d.h = (struct mips_elf_link_hash_entry *) h; + + loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry, + INSERT); + + /* If we've already marked this entry as needing GOT space, we don't + need to do it again. */ + if (*loc) + return TRUE; + + *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry); + + if (! *loc) + return FALSE; + + entry.gotidx = -1; + memcpy (*loc, &entry, sizeof entry); + + if (h->got.offset != MINUS_ONE) + return TRUE; + + /* By setting this to a value other than -1, we are indicating that + there needs to be a GOT entry for H. Avoid using zero, as the + generic ELF copy_indirect_symbol tests for <= 0. */ + h->got.offset = 1; + + return TRUE; +} + +/* Reserve space in G for a GOT entry containing the value of symbol + SYMNDX in input bfd ABDF, plus ADDEND. */ + +static bfd_boolean +mips_elf_record_local_got_symbol (abfd, symndx, addend, g) + bfd *abfd; + long symndx; + bfd_vma addend; + struct mips_got_info *g; +{ + struct mips_got_entry entry, **loc; + + entry.abfd = abfd; + entry.symndx = symndx; + entry.d.addend = addend; + loc = (struct mips_got_entry **) + htab_find_slot (g->got_entries, &entry, INSERT); + + if (*loc) + return TRUE; + + entry.gotidx = g->local_gotno++; + + *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry); + + if (! *loc) + return FALSE; + + memcpy (*loc, &entry, sizeof entry); + + return TRUE; +} + +/* Compute the hash value of the bfd in a bfd2got hash entry. */ + +static hashval_t +mips_elf_bfd2got_entry_hash (entry_) + const PTR entry_; +{ + const struct mips_elf_bfd2got_hash *entry + = (struct mips_elf_bfd2got_hash *)entry_; + + return entry->bfd->id; +} + +/* Check whether two hash entries have the same bfd. */ + +static int +mips_elf_bfd2got_entry_eq (entry1, entry2) + const PTR entry1; + const PTR entry2; +{ + const struct mips_elf_bfd2got_hash *e1 + = (const struct mips_elf_bfd2got_hash *)entry1; + const struct mips_elf_bfd2got_hash *e2 + = (const struct mips_elf_bfd2got_hash *)entry2; + + return e1->bfd == e2->bfd; +} + +/* In a multi-got link, determine the GOT to be used for IBDF. G must + be the master GOT data. */ + +static struct mips_got_info * +mips_elf_got_for_ibfd (g, ibfd) + struct mips_got_info *g; + bfd *ibfd; +{ + struct mips_elf_bfd2got_hash e, *p; + + if (! g->bfd2got) + return g; + + e.bfd = ibfd; + p = (struct mips_elf_bfd2got_hash *) htab_find (g->bfd2got, &e); + return p ? p->g : NULL; +} + +/* Create one separate got for each bfd that has entries in the global + got, such that we can tell how many local and global entries each + bfd requires. */ + +static int +mips_elf_make_got_per_bfd (entryp, p) + void **entryp; + void *p; +{ + struct mips_got_entry *entry = (struct mips_got_entry *)*entryp; + struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p; + htab_t bfd2got = arg->bfd2got; + struct mips_got_info *g; + struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot; + void **bfdgotp; + + /* Find the got_info for this GOT entry's input bfd. Create one if + none exists. */ + bfdgot_entry.bfd = entry->abfd; + bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT); + bfdgot = (struct mips_elf_bfd2got_hash *)*bfdgotp; + + if (bfdgot != NULL) + g = bfdgot->g; + else + { + bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc + (arg->obfd, sizeof (struct mips_elf_bfd2got_hash)); + + if (bfdgot == NULL) + { + arg->obfd = 0; + return 0; + } + + *bfdgotp = bfdgot; + + bfdgot->bfd = entry->abfd; + bfdgot->g = g = (struct mips_got_info *) + bfd_alloc (arg->obfd, sizeof (struct mips_got_info)); + if (g == NULL) + { + arg->obfd = 0; + return 0; + } + + g->global_gotsym = NULL; + g->global_gotno = 0; + g->local_gotno = 0; + g->assigned_gotno = -1; + g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash, + mips_elf_multi_got_entry_eq, + (htab_del) NULL); + if (g->got_entries == NULL) + { + arg->obfd = 0; + return 0; + } + + g->bfd2got = NULL; + g->next = NULL; + } + + /* Insert the GOT entry in the bfd's got entry hash table. */ + entryp = htab_find_slot (g->got_entries, entry, INSERT); + if (*entryp != NULL) + return 1; + + *entryp = entry; + + if (entry->symndx >= 0 || entry->d.h->forced_local) + ++g->local_gotno; + else + ++g->global_gotno; + + return 1; +} + +/* Attempt to merge gots of different input bfds. Try to use as much + as possible of the primary got, since it doesn't require explicit + dynamic relocations, but don't use bfds that would reference global + symbols out of the addressable range. Failing the primary got, + attempt to merge with the current got, or finish the current got + and then make make the new got current. */ + +static int +mips_elf_merge_gots (bfd2got_, p) + void **bfd2got_; + void *p; +{ + struct mips_elf_bfd2got_hash *bfd2got + = (struct mips_elf_bfd2got_hash *)*bfd2got_; + struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p; + unsigned int lcount = bfd2got->g->local_gotno; + unsigned int gcount = bfd2got->g->global_gotno; + unsigned int maxcnt = arg->max_count; + + /* If we don't have a primary GOT and this is not too big, use it as + a starting point for the primary GOT. */ + if (! arg->primary && lcount + gcount <= maxcnt) + { + arg->primary = bfd2got->g; + arg->primary_count = lcount + gcount; + } + /* If it looks like we can merge this bfd's entries with those of + the primary, merge them. The heuristics is conservative, but we + don't have to squeeze it too hard. */ + else if (arg->primary + && (arg->primary_count + lcount + gcount) <= maxcnt) + { + struct mips_got_info *g = bfd2got->g; + int old_lcount = arg->primary->local_gotno; + int old_gcount = arg->primary->global_gotno; + + bfd2got->g = arg->primary; + + htab_traverse (g->got_entries, + mips_elf_make_got_per_bfd, + arg); + if (arg->obfd == NULL) + return 0; + + htab_delete (g->got_entries); + /* We don't have to worry about releasing memory of the actual + got entries, since they're all in the master got_entries hash + table anyway. */ + + BFD_ASSERT (old_lcount + lcount == arg->primary->local_gotno); + BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno); + + arg->primary_count = arg->primary->local_gotno + + arg->primary->global_gotno; + } + /* If we can merge with the last-created got, do it. */ + else if (arg->current + && arg->current_count + lcount + gcount <= maxcnt) + { + struct mips_got_info *g = bfd2got->g; + int old_lcount = arg->current->local_gotno; + int old_gcount = arg->current->global_gotno; + + bfd2got->g = arg->current; + + htab_traverse (g->got_entries, + mips_elf_make_got_per_bfd, + arg); + if (arg->obfd == NULL) + return 0; + + htab_delete (g->got_entries); + + BFD_ASSERT (old_lcount + lcount == arg->current->local_gotno); + BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno); + + arg->current_count = arg->current->local_gotno + + arg->current->global_gotno; + } + /* Well, we couldn't merge, so create a new GOT. Don't check if it + fits; if it turns out that it doesn't, we'll get relocation + overflows anyway. */ + else + { + bfd2got->g->next = arg->current; + arg->current = bfd2got->g; + + arg->current_count = lcount + gcount; + } + + return 1; +} + +/* If passed a NULL mips_got_info in the argument, set the marker used + to tell whether a global symbol needs a got entry (in the primary + got) to the given VALUE. + + If passed a pointer G to a mips_got_info in the argument (it must + not be the primary GOT), compute the offset from the beginning of + the (primary) GOT section to the entry in G corresponding to the + global symbol. G's assigned_gotno must contain the index of the + first available global GOT entry in G. VALUE must contain the size + of a GOT entry in bytes. For each global GOT entry that requires a + dynamic relocation, NEEDED_RELOCS is incremented, and the symbol is + marked as not elligible for lazy resolution through a function + stub. */ +static int +mips_elf_set_global_got_offset (entryp, p) + void **entryp; + void *p; +{ + struct mips_got_entry *entry = (struct mips_got_entry *)*entryp; + struct mips_elf_set_global_got_offset_arg *arg + = (struct mips_elf_set_global_got_offset_arg *)p; + struct mips_got_info *g = arg->g; + + if (entry->abfd != NULL && entry->symndx == -1 + && entry->d.h->root.dynindx != -1) + { + if (g) + { + BFD_ASSERT (g->global_gotsym == NULL); + + entry->gotidx = arg->value * (long) g->assigned_gotno++; + /* We can't do lazy update of GOT entries for + non-primary GOTs since the PLT entries don't use the + right offsets, so punt at it for now. */ + entry->d.h->no_fn_stub = TRUE; + if (arg->info->shared + || (elf_hash_table (arg->info)->dynamic_sections_created + && ((entry->d.h->root.elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0) + && ((entry->d.h->root.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0))) + ++arg->needed_relocs; + } + else + entry->d.h->root.got.offset = arg->value; + } + + return 1; +} + +/* Follow indirect and warning hash entries so that each got entry + points to the final symbol definition. P must point to a pointer + to the hash table we're traversing. Since this traversal may + modify the hash table, we set this pointer to NULL to indicate + we've made a potentially-destructive change to the hash table, so + the traversal must be restarted. */ +static int +mips_elf_resolve_final_got_entry (entryp, p) + void **entryp; + void *p; +{ + struct mips_got_entry *entry = (struct mips_got_entry *)*entryp; + htab_t got_entries = *(htab_t *)p; + + if (entry->abfd != NULL && entry->symndx == -1) + { + struct mips_elf_link_hash_entry *h = entry->d.h; + + while (h->root.root.type == bfd_link_hash_indirect + || h->root.root.type == bfd_link_hash_warning) + h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link; + + if (entry->d.h == h) + return 1; + + entry->d.h = h; + + /* If we can't find this entry with the new bfd hash, re-insert + it, and get the traversal restarted. */ + if (! htab_find (got_entries, entry)) + { + htab_clear_slot (got_entries, entryp); + entryp = htab_find_slot (got_entries, entry, INSERT); + if (! *entryp) + *entryp = entry; + /* Abort the traversal, since the whole table may have + moved, and leave it up to the parent to restart the + process. */ + *(htab_t *)p = NULL; + return 0; + } + /* We might want to decrement the global_gotno count, but it's + either too early or too late for that at this point. */ + } + + return 1; +} + +/* Turn indirect got entries in a got_entries table into their final + locations. */ +static void +mips_elf_resolve_final_got_entries (g) + struct mips_got_info *g; +{ + htab_t got_entries; + + do + { + got_entries = g->got_entries; + + htab_traverse (got_entries, + mips_elf_resolve_final_got_entry, + &got_entries); + } + while (got_entries == NULL); +} + +/* Return the offset of an input bfd IBFD's GOT from the beginning of + the primary GOT. */ +static bfd_vma +mips_elf_adjust_gp (abfd, g, ibfd) + bfd *abfd; + struct mips_got_info *g; + bfd *ibfd; +{ + if (g->bfd2got == NULL) + return 0; + + g = mips_elf_got_for_ibfd (g, ibfd); + if (! g) + return 0; + + BFD_ASSERT (g->next); + + g = g->next; + + return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd); +} + +/* Turn a single GOT that is too big for 16-bit addressing into + a sequence of GOTs, each one 16-bit addressable. */ + +static bfd_boolean +mips_elf_multi_got (abfd, info, g, got, pages) + bfd *abfd; + struct bfd_link_info *info; + struct mips_got_info *g; + asection *got; + bfd_size_type pages; +{ + struct mips_elf_got_per_bfd_arg got_per_bfd_arg; + struct mips_elf_set_global_got_offset_arg set_got_offset_arg; + struct mips_got_info *gg; + unsigned int assign; + + g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash, + mips_elf_bfd2got_entry_eq, + (htab_del) NULL); + if (g->bfd2got == NULL) + return FALSE; + + got_per_bfd_arg.bfd2got = g->bfd2got; + got_per_bfd_arg.obfd = abfd; + got_per_bfd_arg.info = info; + + /* Count how many GOT entries each input bfd requires, creating a + map from bfd to got info while at that. */ + mips_elf_resolve_final_got_entries (g); + htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg); + if (got_per_bfd_arg.obfd == NULL) + return FALSE; + + got_per_bfd_arg.current = NULL; + got_per_bfd_arg.primary = NULL; + /* Taking out PAGES entries is a worst-case estimate. We could + compute the maximum number of pages that each separate input bfd + uses, but it's probably not worth it. */ + got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd) + / MIPS_ELF_GOT_SIZE (abfd)) + - MIPS_RESERVED_GOTNO - pages); + + /* Try to merge the GOTs of input bfds together, as long as they + don't seem to exceed the maximum GOT size, choosing one of them + to be the primary GOT. */ + htab_traverse (g->bfd2got, mips_elf_merge_gots, &got_per_bfd_arg); + if (got_per_bfd_arg.obfd == NULL) + return FALSE; + + /* If we find any suitable primary GOT, create an empty one. */ + if (got_per_bfd_arg.primary == NULL) + { + g->next = (struct mips_got_info *) + bfd_alloc (abfd, sizeof (struct mips_got_info)); + if (g->next == NULL) + return FALSE; + + g->next->global_gotsym = NULL; + g->next->global_gotno = 0; + g->next->local_gotno = 0; + g->next->assigned_gotno = 0; + g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash, + mips_elf_multi_got_entry_eq, + (htab_del) NULL); + if (g->next->got_entries == NULL) + return FALSE; + g->next->bfd2got = NULL; + } + else + g->next = got_per_bfd_arg.primary; + g->next->next = got_per_bfd_arg.current; + + /* GG is now the master GOT, and G is the primary GOT. */ + gg = g; + g = g->next; + + /* Map the output bfd to the primary got. That's what we're going + to use for bfds that use GOT16 or GOT_PAGE relocations that we + didn't mark in check_relocs, and we want a quick way to find it. + We can't just use gg->next because we're going to reverse the + list. */ + { + struct mips_elf_bfd2got_hash *bfdgot; + void **bfdgotp; + + bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc + (abfd, sizeof (struct mips_elf_bfd2got_hash)); + + if (bfdgot == NULL) + return FALSE; -/* If H is a symbol that needs a global GOT entry, but has a dynamic - symbol table index lower than any we've seen to date, record it for - posterity. */ + bfdgot->bfd = abfd; + bfdgot->g = g; + bfdgotp = htab_find_slot (gg->bfd2got, bfdgot, INSERT); -static bfd_boolean -mips_elf_record_global_got_symbol (h, info, g) - struct elf_link_hash_entry *h; - struct bfd_link_info *info; - struct mips_got_info *g ATTRIBUTE_UNUSED; -{ - /* A global symbol in the GOT must also be in the dynamic symbol - table. */ - if (h->dynindx == -1) + BFD_ASSERT (*bfdgotp == NULL); + *bfdgotp = bfdgot; + } + + /* The IRIX dynamic linker requires every symbol that is referenced + in a dynamic relocation to be present in the primary GOT, so + arrange for them to appear after those that are actually + referenced. + + GNU/Linux could very well do without it, but it would slow down + the dynamic linker, since it would have to resolve every dynamic + symbol referenced in other GOTs more than once, without help from + the cache. Also, knowing that every external symbol has a GOT + helps speed up the resolution of local symbols too, so GNU/Linux + follows IRIX's practice. + + The number 2 is used by mips_elf_sort_hash_table_f to count + global GOT symbols that are unreferenced in the primary GOT, with + an initial dynamic index computed from gg->assigned_gotno, where + the number of unreferenced global entries in the primary GOT is + preserved. */ + if (1) { - switch (ELF_ST_VISIBILITY (h->other)) - { - case STV_INTERNAL: - case STV_HIDDEN: - _bfd_mips_elf_hide_symbol (info, h, TRUE); - break; - } - if (!bfd_elf32_link_record_dynamic_symbol (info, h)) - return FALSE; + gg->assigned_gotno = gg->global_gotno - g->global_gotno; + g->global_gotno = gg->global_gotno; + set_got_offset_arg.value = 2; + } + else + { + /* This could be used for dynamic linkers that don't optimize + symbol resolution while applying relocations so as to use + primary GOT entries or assuming the symbol is locally-defined. + With this code, we assign lower dynamic indices to global + symbols that are not referenced in the primary GOT, so that + their entries can be omitted. */ + gg->assigned_gotno = 0; + set_got_offset_arg.value = -1; } - /* If we've already marked this entry as needing GOT space, we don't - need to do it again. */ - if (h->got.offset != MINUS_ONE) - return TRUE; + /* Reorder dynamic symbols as described above (which behavior + depends on the setting of VALUE). */ + set_got_offset_arg.g = NULL; + htab_traverse (gg->got_entries, mips_elf_set_global_got_offset, + &set_got_offset_arg); + set_got_offset_arg.value = 1; + htab_traverse (g->got_entries, mips_elf_set_global_got_offset, + &set_got_offset_arg); + if (! mips_elf_sort_hash_table (info, 1)) + return FALSE; - /* By setting this to a value other than -1, we are indicating that - there needs to be a GOT entry for H. Avoid using zero, as the - generic ELF copy_indirect_symbol tests for <= 0. */ - h->got.offset = 1; + /* Now go through the GOTs assigning them offset ranges. + [assigned_gotno, local_gotno[ will be set to the range of local + entries in each GOT. We can then compute the end of a GOT by + adding local_gotno to global_gotno. We reverse the list and make + it circular since then we'll be able to quickly compute the + beginning of a GOT, by computing the end of its predecessor. To + avoid special cases for the primary GOT, while still preserving + assertions that are valid for both single- and multi-got links, + we arrange for the main got struct to have the right number of + global entries, but set its local_gotno such that the initial + offset of the primary GOT is zero. Remember that the primary GOT + will become the last item in the circular linked list, so it + points back to the master GOT. */ + gg->local_gotno = -g->global_gotno; + gg->global_gotno = g->global_gotno; + assign = 0; + gg->next = gg; + + do + { + struct mips_got_info *gn; + + assign += MIPS_RESERVED_GOTNO; + g->assigned_gotno = assign; + g->local_gotno += assign + pages; + assign = g->local_gotno + g->global_gotno; + + /* Take g out of the direct list, and push it onto the reversed + list that gg points to. */ + gn = g->next; + g->next = gg->next; + gg->next = g; + g = gn; + } + while (g); + got->_raw_size = (gg->next->local_gotno + + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd); + return TRUE; } + /* Returns the first relocation of type r_type found, beginning with RELOCATION. RELEND is one-past-the-end of the relocation table. */ @@ -1967,9 +2844,10 @@ mips_elf_create_compact_rel_section (abfd, info) /* Create the .got section to hold the global offset table. */ static bfd_boolean -mips_elf_create_got_section (abfd, info) +mips_elf_create_got_section (abfd, info, maybe_exclude) bfd *abfd; struct bfd_link_info *info; + bfd_boolean maybe_exclude; { flagword flags; register asection *s; @@ -1979,12 +2857,20 @@ mips_elf_create_got_section (abfd, info) bfd_size_type amt; /* This function may be called more than once. */ - if (mips_elf_got_section (abfd)) - return TRUE; + s = mips_elf_got_section (abfd, TRUE); + if (s) + { + if (! maybe_exclude) + s->flags &= ~SEC_EXCLUDE; + return TRUE; + } flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); + if (maybe_exclude) + flags |= SEC_EXCLUDE; + s = bfd_make_section (abfd, ".got"); if (s == NULL || ! bfd_set_section_flags (abfd, s, flags) @@ -2010,9 +2896,6 @@ mips_elf_create_got_section (abfd, info) && ! bfd_elf32_link_record_dynamic_symbol (info, h)) return FALSE; - /* The first several global offset table entries are reserved. */ - s->_raw_size = MIPS_RESERVED_GOTNO * MIPS_ELF_GOT_SIZE (abfd); - amt = sizeof (struct mips_got_info); g = (struct mips_got_info *) bfd_alloc (abfd, amt); if (g == NULL) @@ -2020,6 +2903,8 @@ mips_elf_create_got_section (abfd, info) g->global_gotsym = NULL; g->local_gotno = MIPS_RESERVED_GOTNO; g->assigned_gotno = MIPS_RESERVED_GOTNO; + g->bfd2got = NULL; + g->next = NULL; g->got_entries = htab_try_create (1, mips_elf_got_entry_hash, mips_elf_got_entry_eq, (htab_del) NULL); @@ -2234,7 +3119,6 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, addresses. */ symbol = 0; else if (info->shared - && (!info->symbolic || info->allow_shlib_undefined) && !info->no_undefined && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT) symbol = 0; @@ -2336,6 +3220,18 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, and we're going to need it, get it now. */ switch (r_type) { + case R_MIPS_GOT_PAGE: + case R_MIPS_GOT_OFST: + /* If this symbol got a global GOT entry, we have to decay + GOT_PAGE/GOT_OFST to GOT_DISP/addend. */ + local_p = local_p || ! h + || (h->root.dynindx + < mips_elf_get_global_gotsym_index (elf_hash_table (info) + ->dynobj)); + if (local_p || r_type == R_MIPS_GOT_OFST) + break; + /* Fall through. */ + case R_MIPS_CALL16: case R_MIPS_GOT16: case R_MIPS_GOT_DISP: @@ -2346,8 +3242,13 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, /* Find the index into the GOT where this value is located. */ if (!local_p) { - BFD_ASSERT (addend == 0); + /* GOT_PAGE may take a non-zero addend, that is ignored in a + GOT_PAGE relocation that decays to GOT_DISP because the + symbol turns out to be global. The addend is then added + as GOT_OFST. */ + BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE); g = mips_elf_global_got_index (elf_hash_table (info)->dynobj, + input_bfd, (struct elf_link_hash_entry *) h); if (! elf_hash_table(info)->dynamic_sections_created || (info->shared @@ -2358,8 +3259,8 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, symbol is defined locally, or was forced to be local. We must initialize this entry in the GOT. */ bfd *tmpbfd = elf_hash_table (info)->dynobj; - asection *sgot = mips_elf_got_section(tmpbfd); - MIPS_ELF_PUT_WORD (tmpbfd, symbol + addend, sgot->contents + g); + asection *sgot = mips_elf_got_section (tmpbfd, FALSE); + MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g); } } else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16) @@ -2368,14 +3269,15 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, break; else { - g = mips_elf_local_got_index (abfd, info, symbol + addend); + g = mips_elf_local_got_index (abfd, input_bfd, + info, symbol + addend); if (g == MINUS_ONE) return bfd_reloc_outofrange; } /* Convert GOT indices to actual offsets. */ g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, g); + abfd, input_bfd, g); break; case R_MIPS_HI16: @@ -2386,6 +3288,11 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, case R_MIPS_LITERAL: gp0 = _bfd_get_gp_value (input_bfd); gp = _bfd_get_gp_value (abfd); + if (elf_hash_table (info)->dynobj) + gp += mips_elf_adjust_gp (abfd, + mips_elf_got_info + (elf_hash_table (info)->dynobj, NULL), + input_bfd); break; default: @@ -2558,12 +3465,13 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, follows. */ forced = ! mips_elf_local_relocation_p (input_bfd, relocation, local_sections, FALSE); - value = mips_elf_got16_entry (abfd, info, symbol + addend, forced); + value = mips_elf_got16_entry (abfd, input_bfd, info, + symbol + addend, forced); if (value == MINUS_ONE) return bfd_reloc_outofrange; value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, value); + abfd, input_bfd, value); overflowed_p = mips_elf_overflow_p (value, 16); break; } @@ -2571,6 +3479,7 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, /* Fall through. */ case R_MIPS_GOT_DISP: + got_disp: value = g; overflowed_p = mips_elf_overflow_p (value, 16); break; @@ -2602,16 +3511,24 @@ mips_elf_calculate_relocation (abfd, input_bfd, input_section, info, break; case R_MIPS_GOT_PAGE: - value = mips_elf_got_page (abfd, info, symbol + addend, NULL); + /* GOT_PAGE relocations that reference non-local symbols decay + to GOT_DISP. The corresponding GOT_OFST relocation decays to + 0. */ + if (! local_p) + goto got_disp; + value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL); if (value == MINUS_ONE) return bfd_reloc_outofrange; value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj, - abfd, value); + abfd, input_bfd, value); overflowed_p = mips_elf_overflow_p (value, 16); break; case R_MIPS_GOT_OFST: - mips_elf_got_page (abfd, info, symbol + addend, &value); + if (local_p) + mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value); + else + value = addend; overflowed_p = mips_elf_overflow_p (value, 16); break; @@ -2882,7 +3799,7 @@ mips_elf_allocate_dynamic_relocations (abfd, n) { asection *s; - s = bfd_get_section_by_name (abfd, ".rel.dyn"); + s = mips_elf_rel_dyn_section (abfd, FALSE); BFD_ASSERT (s != NULL); if (s->_raw_size == 0) @@ -2919,7 +3836,7 @@ mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec, r_type = ELF_R_TYPE (output_bfd, rel->r_info); dynobj = elf_hash_table (info)->dynobj; - sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn"); + sreloc = mips_elf_rel_dyn_section (dynobj, FALSE); BFD_ASSERT (sreloc != NULL); BFD_ASSERT (sreloc->contents != NULL); BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd) @@ -2957,11 +3874,9 @@ mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec, } #endif - if (outrel[0].r_offset == (bfd_vma) -1) + if (outrel[0].r_offset == (bfd_vma) -1 + || outrel[0].r_offset == (bfd_vma) -2) skip = TRUE; - /* FIXME: For -2 runtime relocation needs to be skipped, but - properly resolved statically and installed. */ - BFD_ASSERT (outrel[0].r_offset != (bfd_vma) -2); /* If we've decided to skip this relocation, just output an empty record. Note that R_MIPS_NONE == 0, so that this call to memset @@ -2971,7 +3886,6 @@ mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec, else { long indx; - bfd_vma section_offset; /* We must now calculate the dynamic symbol table index to use in the relocation. */ @@ -3001,15 +3915,18 @@ mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec, abort (); } - /* Figure out how far the target of the relocation is from - the beginning of its section. */ - section_offset = symbol - sec->output_section->vma; - /* The relocation we're building is section-relative. - Therefore, the original addend must be adjusted by the - section offset. */ - *addendp += section_offset; - /* Now, the relocation is just against the section. */ - symbol = sec->output_section->vma; + /* Instead of generating a relocation using the section + symbol, we may as well make it a fully relative + relocation. We want to avoid generating relocations to + local symbols because we used to generate them + incorrectly, without adding the original symbol value, + which is mandated by the ABI for section symbols. In + order to give dynamic loaders and applications time to + phase out the incorrect use, we refrain from emitting + section-relative relocations. It's not like they're + useful, after all. This should be a bit more efficient + as well. */ + indx = 0; } /* If the relocation was previously an absolute relocation and @@ -3023,6 +3940,18 @@ mips_elf_create_dynamic_relocation (output_bfd, info, rel, h, sec, know where the shared library will wind up at load-time. */ outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx, R_MIPS_REL32); + /* For strict adherence to the ABI specification, we should + generate a R_MIPS_64 relocation record by itself before the + _REL32/_64 record as well, such that the addend is read in as + a 64-bit value (REL32 is a 32-bit relocation, after all). + However, since none of the existing ELF64 MIPS dynamic + loaders seems to care, we don't waste space with these + artificial relocations. If this turns out to not be true, + mips_elf_allocate_dynamic_relocation() should be tweaked so + as to make room for a pair of dynamic relocations per + invocation if ABI_64_P, and here we should generate an + additional relocation record with R_MIPS_64 by itself for a + NULL symbol before this relocation record. */ outrel[1].r_info = ELF_R_INFO (output_bfd, (unsigned long) 0, ABI_64_P (output_bfd) ? R_MIPS_64 @@ -3733,8 +4662,7 @@ _bfd_mips_elf_fake_sections (abfd, hdr, sec) esd->rel_hdr2 = (Elf_Internal_Shdr *) bfd_zalloc (abfd, amt); if (!esd->rel_hdr2) return FALSE; - _bfd_elf_init_reloc_shdr (abfd, esd->rel_hdr2, sec, - !elf_section_data (sec)->use_rela_p); + _bfd_elf_init_reloc_shdr (abfd, esd->rel_hdr2, sec, !sec->use_rela_p); } return TRUE; @@ -3976,7 +4904,10 @@ _bfd_mips_elf_create_dynamic_sections (abfd, info) } /* We need to create .got section. */ - if (! mips_elf_create_got_section (abfd, info)) + if (! mips_elf_create_got_section (abfd, info, FALSE)) + return FALSE; + + if (! mips_elf_rel_dyn_section (elf_hash_table (info)->dynobj, TRUE)) return FALSE; /* Create the .msym section on IRIX6. It is used by the dynamic @@ -4175,10 +5106,10 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) sizeof CALL_FP_STUB - 1) == 0) continue; - sec_relocs = (MNAME(abfd,_bfd_elf,link_read_relocs) - (abfd, o, (PTR) NULL, - (Elf_Internal_Rela *) NULL, - info->keep_memory)); + sec_relocs + = _bfd_elf_link_read_relocs (abfd, o, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + info->keep_memory); if (sec_relocs == NULL) return FALSE; @@ -4305,7 +5236,7 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) } else { - sgot = mips_elf_got_section (dynobj); + sgot = mips_elf_got_section (dynobj, FALSE); if (sgot == NULL) g = NULL; else @@ -4366,7 +5297,7 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) case R_MIPS_GOT_DISP: if (dynobj == NULL) elf_hash_table (info)->dynobj = dynobj = abfd; - if (! mips_elf_create_got_section (dynobj, info)) + if (! mips_elf_create_got_section (dynobj, info, FALSE)) return FALSE; g = mips_elf_got_info (dynobj, &sgot); break; @@ -4389,8 +5320,6 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) || r_type == R_MIPS_GOT_LO16 || r_type == R_MIPS_GOT_DISP)) { - struct mips_got_entry entry, **loc; - /* We may need a local GOT entry for this relocation. We don't count R_MIPS_GOT_PAGE because we can estimate the maximum number of pages needed by looking at the size of @@ -4398,26 +5327,9 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) R_MIPS_CALL16. We don't count R_MIPS_GOT_HI16, or R_MIPS_CALL_HI16 because these are always followed by an R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */ - - entry.abfd = abfd; - entry.symndx = r_symndx; - entry.addend = rel->r_addend; - loc = (struct mips_got_entry **) - htab_find_slot (g->got_entries, &entry, INSERT); - - if (*loc == NULL) - { - entry.gotidx = g->local_gotno++; - - *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry); - - if (! *loc) - return FALSE; - - memcpy (*loc, &entry, sizeof entry); - - sgot->_raw_size += MIPS_ELF_GOT_SIZE (dynobj); - } + if (! mips_elf_record_local_got_symbol (abfd, r_symndx, + rel->r_addend, g)) + return FALSE; } switch (r_type) @@ -4438,7 +5350,7 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) if (h != NULL) { /* This symbol requires a global offset table entry. */ - if (! mips_elf_record_global_got_symbol (h, info, g)) + if (! mips_elf_record_global_got_symbol (h, abfd, info, g)) return FALSE; /* We need a stub, not a plt entry for the undefined @@ -4449,12 +5361,50 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) } break; + case R_MIPS_GOT_PAGE: + /* If this is a global, overridable symbol, GOT_PAGE will + decay to GOT_DISP, so we'll need a GOT entry for it. */ + if (h == NULL) + break; + else + { + struct mips_elf_link_hash_entry *hmips = + (struct mips_elf_link_hash_entry *) h; + + while (hmips->root.root.type == bfd_link_hash_indirect + || hmips->root.root.type == bfd_link_hash_warning) + hmips = (struct mips_elf_link_hash_entry *) + hmips->root.root.u.i.link; + + if ((hmips->root.root.type == bfd_link_hash_defined + || hmips->root.root.type == bfd_link_hash_defweak) + && hmips->root.root.u.def.section + && ! (info->shared && ! info->symbolic + && ! (hmips->root.elf_link_hash_flags + & ELF_LINK_FORCED_LOCAL)) + /* If we've encountered any other relocation + referencing the symbol, we'll have marked it as + dynamic, and, even though we might be able to get + rid of the GOT entry should we know for sure all + previous relocations were GOT_PAGE ones, at this + point we can't tell, so just keep using the + symbol as dynamic. This is very important in the + multi-got case, since we don't decide whether to + decay GOT_PAGE to GOT_DISP on a per-GOT basis: if + the symbol is dynamic, we'll need a GOT entry for + every GOT in which the symbol is referenced with + a GOT_PAGE relocation. */ + && hmips->root.dynindx == -1) + break; + } + /* Fall through. */ + case R_MIPS_GOT16: case R_MIPS_GOT_HI16: case R_MIPS_GOT_LO16: case R_MIPS_GOT_DISP: /* This symbol requires a global offset table entry. */ - if (h && ! mips_elf_record_global_got_symbol (h, info, g)) + if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g)) return FALSE; break; @@ -4466,24 +5416,9 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) { if (sreloc == NULL) { - const char *dname = ".rel.dyn"; - - sreloc = bfd_get_section_by_name (dynobj, dname); + sreloc = mips_elf_rel_dyn_section (dynobj, TRUE); if (sreloc == NULL) - { - sreloc = bfd_make_section (dynobj, dname); - if (sreloc == NULL - || ! bfd_set_section_flags (dynobj, sreloc, - (SEC_ALLOC - | SEC_LOAD - | SEC_HAS_CONTENTS - | SEC_IN_MEMORY - | SEC_LINKER_CREATED - | SEC_READONLY)) - || ! bfd_set_section_alignment (dynobj, sreloc, - 4)) - return FALSE; - } + return FALSE; } #define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY) if (info->shared) @@ -4518,9 +5453,16 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) this symbol, a symbol must have a dynamic symbol table index greater that DT_MIPS_GOTSYM if there are dynamic relocations against it. */ - if (h != NULL - && ! mips_elf_record_global_got_symbol (h, info, g)) - return FALSE; + if (h != NULL) + { + if (dynobj == NULL) + elf_hash_table (info)->dynobj = dynobj = abfd; + if (! mips_elf_create_got_section (dynobj, info, TRUE)) + return FALSE; + g = mips_elf_got_info (dynobj, &sgot); + if (! mips_elf_record_global_got_symbol (h, abfd, info, g)) + return FALSE; + } } if (SGI_COMPAT (abfd)) @@ -4596,6 +5538,185 @@ _bfd_mips_elf_check_relocs (abfd, info, sec, relocs) return TRUE; } +bfd_boolean +_bfd_mips_relax_section (abfd, sec, link_info, again) + bfd *abfd; + asection *sec; + struct bfd_link_info *link_info; + bfd_boolean *again; +{ + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Shdr *symtab_hdr; + bfd_byte *contents = NULL; + bfd_byte *free_contents = NULL; + size_t extsymoff; + bfd_boolean changed_contents = FALSE; + bfd_vma sec_start = sec->output_section->vma + sec->output_offset; + Elf_Internal_Sym *isymbuf = NULL; + + /* We are not currently changing any sizes, so only one pass. */ + *again = FALSE; + + if (link_info->relocateable) + return TRUE; + + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + link_info->keep_memory); + if (internal_relocs == NULL) + return TRUE; + + irelend = internal_relocs + sec->reloc_count + * get_elf_backend_data (abfd)->s->int_rels_per_ext_rel; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info; + + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma symval; + bfd_signed_vma sym_offset; + unsigned int r_type; + unsigned long r_symndx; + asection *sym_sec; + unsigned long instruction; + + /* Turn jalr into bgezal, and jr into beq, if they're marked + with a JALR relocation, that indicate where they jump to. + This saves some pipeline bubbles. */ + r_type = ELF_R_TYPE (abfd, irel->r_info); + if (r_type != R_MIPS_JALR) + continue; + + r_symndx = ELF_R_SYM (abfd, irel->r_info); + /* Compute the address of the jump target. */ + if (r_symndx >= extsymoff) + { + struct mips_elf_link_hash_entry *h + = ((struct mips_elf_link_hash_entry *) + elf_sym_hashes (abfd) [r_symndx - extsymoff]); + + while (h->root.root.type == bfd_link_hash_indirect + || h->root.root.type == bfd_link_hash_warning) + h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link; + + /* If a symbol is undefined, or if it may be overridden, + skip it. */ + if (! ((h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + && h->root.root.u.def.section) + || (link_info->shared && ! link_info->symbolic + && ! (h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL))) + continue; + + sym_sec = h->root.root.u.def.section; + if (sym_sec->output_section) + symval = (h->root.root.u.def.value + + sym_sec->output_section->vma + + sym_sec->output_offset); + else + symval = h->root.root.u.def.value; + } + else + { + Elf_Internal_Sym *isym; + + /* Read this BFD's symbols if we haven't done so already. */ + if (isymbuf == NULL && symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + goto relax_return; + } + + isym = isymbuf + r_symndx; + if (isym->st_shndx == SHN_UNDEF) + continue; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec + = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value + + sym_sec->output_section->vma + + sym_sec->output_offset; + } + + /* Compute branch offset, from delay slot of the jump to the + branch target. */ + sym_offset = (symval + irel->r_addend) + - (sec_start + irel->r_offset + 4); + + /* Branch offset must be properly aligned. */ + if ((sym_offset & 3) != 0) + continue; + + sym_offset >>= 2; + + /* Check that it's in range. */ + if (sym_offset < -0x8000 || sym_offset >= 0x8000) + continue; + + /* Get the section contents if we haven't done so already. */ + if (contents == NULL) + { + /* Get cached copy if it exists. */ + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else + { + contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (contents == NULL) + goto relax_return; + + free_contents = contents; + if (! bfd_get_section_contents (abfd, sec, contents, + (file_ptr) 0, sec->_raw_size)) + goto relax_return; + } + } + + instruction = bfd_get_32 (abfd, contents + irel->r_offset); + + /* If it was jalr , turn it into bgezal $zero, . */ + if ((instruction & 0xfc1fffff) == 0x0000f809) + instruction = 0x04110000; + /* If it was jr , turn it into b . */ + else if ((instruction & 0xfc1fffff) == 0x00000008) + instruction = 0x10000000; + else + continue; + + instruction |= (sym_offset & 0xffff); + bfd_put_32 (abfd, instruction, contents + irel->r_offset); + changed_contents = TRUE; + } + + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + { + if (!changed_contents && !link_info->keep_memory) + free (contents); + else + { + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + return TRUE; + + relax_return: + if (free_contents != NULL) + free (free_contents); + return FALSE; +} + /* Adjust a symbol defined by a dynamic object and referenced by a regular object. The current definition is in some section of the dynamic object, but we're not including those sections. We have to @@ -4712,19 +5833,86 @@ _bfd_mips_elf_always_size_sections (output_bfd, info) { asection *ri; + bfd *dynobj; + asection *s; + struct mips_got_info *g; + int i; + bfd_size_type loadable_size = 0; + bfd_size_type local_gotno; + bfd *sub; + /* The .reginfo section has a fixed size. */ ri = bfd_get_section_by_name (output_bfd, ".reginfo"); if (ri != NULL) bfd_set_section_size (output_bfd, ri, (bfd_size_type) sizeof (Elf32_External_RegInfo)); - if (info->relocateable - || ! mips_elf_hash_table (info)->mips16_stubs_seen) + if (! (info->relocateable + || ! mips_elf_hash_table (info)->mips16_stubs_seen)) + mips_elf_link_hash_traverse (mips_elf_hash_table (info), + mips_elf_check_mips16_stubs, + (PTR) NULL); + + dynobj = elf_hash_table (info)->dynobj; + if (dynobj == NULL) + /* Relocatable links don't have it. */ + return TRUE; + + g = mips_elf_got_info (dynobj, &s); + if (s == NULL) return TRUE; - mips_elf_link_hash_traverse (mips_elf_hash_table (info), - mips_elf_check_mips16_stubs, - (PTR) NULL); + /* Calculate the total loadable size of the output. That + will give us the maximum number of GOT_PAGE entries + required. */ + for (sub = info->input_bfds; sub; sub = sub->link_next) + { + asection *subsection; + + for (subsection = sub->sections; + subsection; + subsection = subsection->next) + { + if ((subsection->flags & SEC_ALLOC) == 0) + continue; + loadable_size += ((subsection->_raw_size + 0xf) + &~ (bfd_size_type) 0xf); + } + } + + /* There has to be a global GOT entry for every symbol with + a dynamic symbol table index of DT_MIPS_GOTSYM or + higher. Therefore, it make sense to put those symbols + that need GOT entries at the end of the symbol table. We + do that here. */ + if (! mips_elf_sort_hash_table (info, 1)) + return FALSE; + + if (g->global_gotsym != NULL) + i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx; + else + /* If there are no global symbols, or none requiring + relocations, then GLOBAL_GOTSYM will be NULL. */ + i = 0; + + /* In the worst case, we'll get one stub per dynamic symbol, plus + one to account for the dummy entry at the end required by IRIX + rld. */ + loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1); + + /* Assume there are two loadable segments consisting of + contiguous sections. Is 5 enough? */ + local_gotno = (loadable_size >> 16) + 5; + + g->local_gotno += local_gotno; + s->_raw_size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd); + + g->global_gotno = i; + s->_raw_size += i * MIPS_ELF_GOT_SIZE (output_bfd); + + if (s->_raw_size > MIPS_ELF_GOT_MAX_SIZE (output_bfd) + && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno)) + return FALSE; return TRUE; } @@ -4739,7 +5927,6 @@ _bfd_mips_elf_size_dynamic_sections (output_bfd, info) bfd *dynobj; asection *s; bfd_boolean reltext; - struct mips_got_info *g = NULL; dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); @@ -4815,61 +6002,64 @@ _bfd_mips_elf_size_dynamic_sections (output_bfd, info) to copy relocs into the output file. */ if (strcmp (name, ".rel.dyn") != 0) s->reloc_count = 0; + + /* If combreloc is enabled, elf_link_sort_relocs() will + sort relocations, but in a different way than we do, + and before we're done creating relocations. Also, it + will move them around between input sections' + relocation's contents, so our sorting would be + broken, so don't let it run. */ + info->combreloc = 0; } } else if (strncmp (name, ".got", 4) == 0) { - int i; - bfd_size_type loadable_size = 0; - bfd_size_type local_gotno; - bfd *sub; - - BFD_ASSERT (mips_elf_section_data (s) != NULL); - g = mips_elf_section_data (s)->u.got_info; - BFD_ASSERT (g != NULL); - - /* Calculate the total loadable size of the output. That - will give us the maximum number of GOT_PAGE entries - required. */ - for (sub = info->input_bfds; sub; sub = sub->link_next) + /* _bfd_mips_elf_always_size_sections() has already done + most of the work, but some symbols may have been mapped + to versions that we must now resolve in the got_entries + hash tables. */ + struct mips_got_info *gg = mips_elf_got_info (dynobj, NULL); + struct mips_got_info *g = gg; + struct mips_elf_set_global_got_offset_arg set_got_offset_arg; + unsigned int needed_relocs = 0; + + if (gg->next) { - asection *subsection; + set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd); + set_got_offset_arg.info = info; - for (subsection = sub->sections; - subsection; - subsection = subsection->next) + mips_elf_resolve_final_got_entries (gg); + for (g = gg->next; g && g->next != gg; g = g->next) { - if ((subsection->flags & SEC_ALLOC) == 0) - continue; - loadable_size += ((subsection->_raw_size + 0xf) - &~ (bfd_size_type) 0xf); + unsigned int save_assign; + + mips_elf_resolve_final_got_entries (g); + + /* Assign offsets to global GOT entries. */ + save_assign = g->assigned_gotno; + g->assigned_gotno = g->local_gotno; + set_got_offset_arg.g = g; + set_got_offset_arg.needed_relocs = 0; + htab_traverse (g->got_entries, + mips_elf_set_global_got_offset, + &set_got_offset_arg); + needed_relocs += set_got_offset_arg.needed_relocs; + BFD_ASSERT (g->assigned_gotno - g->local_gotno + <= g->global_gotno); + + g->assigned_gotno = save_assign; + if (info->shared) + { + needed_relocs += g->local_gotno - g->assigned_gotno; + BFD_ASSERT (g->assigned_gotno == g->next->local_gotno + + g->next->global_gotno + + MIPS_RESERVED_GOTNO); + } } - } - loadable_size += MIPS_FUNCTION_STUB_SIZE; - - /* Assume there are two loadable segments consisting of - contiguous sections. Is 5 enough? */ - local_gotno = (loadable_size >> 16) + 5; - - g->local_gotno += local_gotno; - s->_raw_size += local_gotno * MIPS_ELF_GOT_SIZE (dynobj); - - /* There has to be a global GOT entry for every symbol with - a dynamic symbol table index of DT_MIPS_GOTSYM or - higher. Therefore, it make sense to put those symbols - that need GOT entries at the end of the symbol table. We - do that here. */ - if (! mips_elf_sort_hash_table (info, 1)) - return FALSE; - if (g->global_gotsym != NULL) - i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx; - else - /* If there are no global symbols, or none requiring - relocations, then GLOBAL_GOTSYM will be NULL. */ - i = 0; - g->global_gotno = i; - s->_raw_size += i * MIPS_ELF_GOT_SIZE (dynobj); + if (needed_relocs) + mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs); + } } else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0) { @@ -4954,7 +6144,7 @@ _bfd_mips_elf_size_dynamic_sections (output_bfd, info) if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0)) return FALSE; - if (bfd_get_section_by_name (dynobj, ".rel.dyn")) + if (mips_elf_rel_dyn_section (dynobj, FALSE)) { if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0)) return FALSE; @@ -5497,7 +6687,7 @@ _bfd_mips_elf_finish_dynamic_symbol (output_bfd, info, h, sym) bfd_vma gval; asection *sgot; asection *smsym; - struct mips_got_info *g; + struct mips_got_info *g, *gg; const char *name; struct mips_elf_link_hash_entry *mh; @@ -5545,9 +6735,9 @@ _bfd_mips_elf_finish_dynamic_symbol (output_bfd, info, h, sym) BFD_ASSERT (h->dynindx != -1 || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0); - sgot = mips_elf_got_section (dynobj); + sgot = mips_elf_got_section (dynobj, FALSE); BFD_ASSERT (sgot != NULL); - BFD_ASSERT (elf_section_data (sgot) != NULL); + BFD_ASSERT (mips_elf_section_data (sgot) != NULL); g = mips_elf_section_data (sgot)->u.got_info; BFD_ASSERT (g != NULL); @@ -5569,15 +6759,69 @@ _bfd_mips_elf_finish_dynamic_symbol (output_bfd, info, h, sym) That's because such the functions are now no longer defined in a shared object.) */ - if (info->shared && h->root.type == bfd_link_hash_undefined) + if ((info->shared && h->root.type == bfd_link_hash_undefined) + || h->root.type == bfd_link_hash_undefweak) value = 0; else value = h->root.u.def.value; } - offset = mips_elf_global_got_index (dynobj, h); + offset = mips_elf_global_got_index (dynobj, output_bfd, h); MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset); } + if (g->next && h->dynindx != -1) + { + struct mips_got_entry e, *p; + bfd_vma offset; + bfd_vma value; + Elf_Internal_Rela rel[3]; + bfd_vma addend = 0; + + gg = g; + + e.abfd = output_bfd; + e.symndx = -1; + e.d.h = (struct mips_elf_link_hash_entry *)h; + + if (info->shared + || h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_undefweak) + value = 0; + else if (sym->st_value) + value = sym->st_value; + else + value = h->root.u.def.value; + + memset (rel, 0, sizeof (rel)); + rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32); + + for (g = g->next; g->next != gg; g = g->next) + { + if (g->got_entries + && (p = (struct mips_got_entry *) htab_find (g->got_entries, + &e))) + { + offset = p->gotidx; + rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset; + + MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset); + + if ((info->shared + || (elf_hash_table (info)->dynamic_sections_created + && p->d.h != NULL + && ((p->d.h->root.elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0) + && ((p->d.h->root.elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0))) + && ! (mips_elf_create_dynamic_relocation + (output_bfd, info, rel, + e.d.h, NULL, value, &addend, sgot))) + return FALSE; + BFD_ASSERT (addend == 0); + } + } + } + /* Create a .msym entry, if appropriate. */ smsym = bfd_get_section_by_name (dynobj, ".msym"); if (smsym) @@ -5684,19 +6928,21 @@ _bfd_mips_elf_finish_dynamic_sections (output_bfd, info) bfd *dynobj; asection *sdyn; asection *sgot; - struct mips_got_info *g; + struct mips_got_info *gg, *g; dynobj = elf_hash_table (info)->dynobj; sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); - sgot = bfd_get_section_by_name (dynobj, ".got"); + sgot = mips_elf_got_section (dynobj, FALSE); if (sgot == NULL) - g = NULL; + gg = g = NULL; else { - BFD_ASSERT (elf_section_data (sgot) != NULL); - g = mips_elf_section_data (sgot)->u.got_info; + BFD_ASSERT (mips_elf_section_data (sgot) != NULL); + gg = mips_elf_section_data (sgot)->u.got_info; + BFD_ASSERT (gg != NULL); + g = mips_elf_got_for_ibfd (gg, output_bfd); BFD_ASSERT (g != NULL); } @@ -5726,7 +6972,7 @@ _bfd_mips_elf_finish_dynamic_sections (output_bfd, info) switch (dyn.d_tag) { case DT_RELENT: - s = (bfd_get_section_by_name (dynobj, ".rel.dyn")); + s = mips_elf_rel_dyn_section (dynobj, FALSE); BFD_ASSERT (s != NULL); dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj); break; @@ -5812,9 +7058,9 @@ _bfd_mips_elf_finish_dynamic_sections (output_bfd, info) break; case DT_MIPS_GOTSYM: - if (g->global_gotsym) + if (gg->global_gotsym) { - dyn.d_un.d_val = g->global_gotsym->dynindx; + dyn.d_un.d_val = gg->global_gotsym->dynindx; break; } /* In case if we don't have global got symbols we default @@ -5877,6 +7123,41 @@ _bfd_mips_elf_finish_dynamic_sections (output_bfd, info) elf_section_data (sgot->output_section)->this_hdr.sh_entsize = MIPS_ELF_GOT_SIZE (output_bfd); + /* Generate dynamic relocations for the non-primary gots. */ + if (gg != NULL && gg->next) + { + Elf_Internal_Rela rel[3]; + bfd_vma addend = 0; + + memset (rel, 0, sizeof (rel)); + rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32); + + for (g = gg->next; g->next != gg; g = g->next) + { + bfd_vma index = g->next->local_gotno + g->next->global_gotno; + + MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0, sgot->contents + + index++ * MIPS_ELF_GOT_SIZE (output_bfd)); + MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0x80000000, sgot->contents + + index++ * MIPS_ELF_GOT_SIZE (output_bfd)); + + if (! info->shared) + continue; + + while (index < g->assigned_gotno) + { + rel[0].r_offset = rel[1].r_offset = rel[2].r_offset + = index++ * MIPS_ELF_GOT_SIZE (output_bfd); + if (!(mips_elf_create_dynamic_relocation + (output_bfd, info, rel, NULL, + bfd_abs_section_ptr, + 0, &addend, sgot))) + return FALSE; + BFD_ASSERT (addend == 0); + } + } + } + { asection *smsym; asection *s; @@ -5939,24 +7220,22 @@ _bfd_mips_elf_finish_dynamic_sections (output_bfd, info) /* We need to sort the entries of the dynamic relocation section. */ - if (!ABI_64_P (output_bfd)) + s = mips_elf_rel_dyn_section (dynobj, FALSE); + + if (s != NULL + && s->_raw_size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd)) { - asection *reldyn; + reldyn_sorting_bfd = output_bfd; - reldyn = bfd_get_section_by_name (dynobj, ".rel.dyn"); - if (reldyn != NULL && reldyn->reloc_count > 2) - { - reldyn_sorting_bfd = output_bfd; - qsort ((Elf32_External_Rel *) reldyn->contents + 1, - (size_t) reldyn->reloc_count - 1, - sizeof (Elf32_External_Rel), sort_dynamic_relocs); - } + if (ABI_64_P (output_bfd)) + qsort ((Elf64_External_Rel *) s->contents + 1, + (size_t) s->reloc_count - 1, + sizeof (Elf64_Mips_External_Rel), sort_dynamic_relocs_64); + else + qsort ((Elf32_External_Rel *) s->contents + 1, + (size_t) s->reloc_count - 1, + sizeof (Elf32_External_Rel), sort_dynamic_relocs); } - - /* Clean up a first relocation in .rel.dyn. */ - s = bfd_get_section_by_name (dynobj, ".rel.dyn"); - if (s != NULL && s->_raw_size > 0) - memset (s->contents, 0, MIPS_ELF_REL_SIZE (dynobj)); } return TRUE; @@ -6524,14 +7803,60 @@ _bfd_mips_elf_hide_symbol (info, entry, force_local) h->forced_local = TRUE; dynobj = elf_hash_table (info)->dynobj; - got = bfd_get_section_by_name (dynobj, ".got"); - g = mips_elf_section_data (got)->u.got_info; + if (dynobj != NULL) + { + got = mips_elf_got_section (dynobj, FALSE); + g = mips_elf_section_data (got)->u.got_info; - _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local); + if (g->next) + { + struct mips_got_entry e; + struct mips_got_info *gg = g; + + /* Since we're turning what used to be a global symbol into a + local one, bump up the number of local entries of each GOT + that had an entry for it. This will automatically decrease + the number of global entries, since global_gotno is actually + the upper limit of global entries. */ + e.abfd = dynobj; + e.symndx = -1; + e.d.h = h; + + for (g = g->next; g != gg; g = g->next) + if (htab_find (g->got_entries, &e)) + { + BFD_ASSERT (g->global_gotno > 0); + g->local_gotno++; + g->global_gotno--; + } + + /* If this was a global symbol forced into the primary GOT, we + no longer need an entry for it. We can't release the entry + at this point, but we must at least stop counting it as one + of the symbols that required a forced got entry. */ + if (h->root.got.offset == 2) + { + BFD_ASSERT (gg->assigned_gotno > 0); + gg->assigned_gotno--; + } + } + else if (g->global_gotno == 0 && g->global_gotsym == NULL) + /* If we haven't got through GOT allocation yet, just bump up the + number of local entries, as this symbol won't be counted as + global. */ + g->local_gotno++; + else if (h->root.got.offset == 1) + { + /* If we're past non-multi-GOT allocation and this symbol had + been marked for a global got entry, give it a local entry + instead. */ + BFD_ASSERT (g->global_gotno > 0); + g->local_gotno++; + g->global_gotno--; + } + } - /* FIXME: Do we allocate too much GOT space here? */ - g->local_gotno++; - got->_raw_size += MIPS_ELF_GOT_SIZE (dynobj); + _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local); } #define PDR_SIZE 32 @@ -6562,10 +7887,9 @@ _bfd_mips_elf_discard_info (abfd, cookie, info) if (! tdata) return FALSE; - cookie->rels = (MNAME(abfd,_bfd_elf,link_read_relocs) - (abfd, o, (PTR) NULL, - (Elf_Internal_Rela *) NULL, - info->keep_memory)); + cookie->rels = _bfd_elf_link_read_relocs (abfd, o, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + info->keep_memory); if (!cookie->rels) { free (tdata); @@ -7090,7 +8414,7 @@ _bfd_mips_elf_final_link (abfd, info) /* Make sure we didn't grow the global .got region. */ dynobj = elf_hash_table (info)->dynobj; - got = bfd_get_section_by_name (dynobj, ".got"); + got = mips_elf_got_section (dynobj, FALSE); g = mips_elf_section_data (got)->u.got_info; if (g->global_gotsym != NULL) @@ -7849,12 +9173,25 @@ _bfd_mips_elf_merge_private_bfd_data (ibfd, obfd) /* Check if we have the same endianess */ if (! _bfd_generic_verify_endian_match (ibfd, obfd)) - return FALSE; + { + (*_bfd_error_handler) + (_("%s: endianness incompatible with that of the selected emulation"), + bfd_archive_filename (ibfd)); + return FALSE; + } if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour || bfd_get_flavour (obfd) != bfd_target_elf_flavour) return TRUE; + if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0) + { + (*_bfd_error_handler) + (_("%s: ABI is incompatible with that of the selected emulation"), + bfd_archive_filename (ibfd)); + return FALSE; + } + new_flags = elf_elfheader (ibfd)->e_flags; elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER; old_flags = elf_elfheader (obfd)->e_flags; @@ -7882,6 +9219,11 @@ _bfd_mips_elf_merge_private_bfd_data (ibfd, obfd) new_flags &= ~EF_MIPS_NOREORDER; old_flags &= ~EF_MIPS_NOREORDER; + /* Some IRIX 6 BSD-compatibility objects have this bit set. It + doesn't seem to matter. */ + new_flags &= ~EF_MIPS_XGOT; + old_flags &= ~EF_MIPS_XGOT; + if (new_flags == old_flags) return TRUE;