-static void
-mips_elf64_swap_reloca_out (abfd, src, dst)
- bfd *abfd;
- const Elf64_Mips_Internal_Rela *src;
- Elf64_Mips_External_Rela *dst;
-{
- H_PUT_64 (abfd, src->r_offset, dst->r_offset);
- H_PUT_32 (abfd, src->r_sym, dst->r_sym);
- H_PUT_8 (abfd, src->r_ssym, dst->r_ssym);
- H_PUT_8 (abfd, src->r_type3, dst->r_type3);
- H_PUT_8 (abfd, src->r_type2, dst->r_type2);
- H_PUT_8 (abfd, src->r_type, dst->r_type);
- H_PUT_S64 (abfd, src->r_addend, dst->r_addend);
-}
-
-/* Swap in a MIPS 64-bit Rel reloc. */
-
-static void
-mips_elf64_be_swap_reloc_in (abfd, src, dst)
- bfd *abfd;
- const bfd_byte *src;
- Elf_Internal_Rel *dst;
-{
- Elf64_Mips_Internal_Rel mirel;
-
- mips_elf64_swap_reloc_in (abfd,
- (const Elf64_Mips_External_Rel *) src,
- &mirel);
-
- dst[0].r_offset = mirel.r_offset;
- dst[0].r_info = ELF64_R_INFO (mirel.r_sym, mirel.r_type);
- dst[1].r_offset = mirel.r_offset;
- dst[1].r_info = ELF64_R_INFO (mirel.r_ssym, mirel.r_type2);
- dst[2].r_offset = mirel.r_offset;
- dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirel.r_type3);
-}
-
-/* Swap in a MIPS 64-bit Rela reloc. */
-
-static void
-mips_elf64_be_swap_reloca_in (abfd, src, dst)
- bfd *abfd;
- const bfd_byte *src;
- Elf_Internal_Rela *dst;
-{
- Elf64_Mips_Internal_Rela mirela;
-
- mips_elf64_swap_reloca_in (abfd,
- (const Elf64_Mips_External_Rela *) src,
- &mirela);
-
- dst[0].r_offset = mirela.r_offset;
- dst[0].r_info = ELF64_R_INFO (mirela.r_sym, mirela.r_type);
- dst[0].r_addend = mirela.r_addend;
- dst[1].r_offset = mirela.r_offset;
- dst[1].r_info = ELF64_R_INFO (mirela.r_ssym, mirela.r_type2);
- dst[1].r_addend = 0;
- dst[2].r_offset = mirela.r_offset;
- dst[2].r_info = ELF64_R_INFO (STN_UNDEF, mirela.r_type3);
- dst[2].r_addend = 0;
-}
-
-/* Swap out a MIPS 64-bit Rel reloc. */
-
-static void
-mips_elf64_be_swap_reloc_out (abfd, src, dst)
- bfd *abfd;
- const Elf_Internal_Rel *src;
- bfd_byte *dst;
-{
- Elf64_Mips_Internal_Rel mirel;
-
- mirel.r_offset = src[0].r_offset;
- BFD_ASSERT(src[0].r_offset == src[1].r_offset);
- BFD_ASSERT(src[0].r_offset == src[2].r_offset);
-
- mirel.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
- mirel.r_sym = ELF64_R_SYM (src[0].r_info);
- mirel.r_type2 = ELF64_MIPS_R_TYPE2 (src[1].r_info);
- mirel.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
- mirel.r_type3 = ELF64_MIPS_R_TYPE3 (src[2].r_info);
-
- mips_elf64_swap_reloc_out (abfd, &mirel,
- (Elf64_Mips_External_Rel *) dst);
-}
-
-/* Swap out a MIPS 64-bit Rela reloc. */
-
-static void
-mips_elf64_be_swap_reloca_out (abfd, src, dst)
- bfd *abfd;
- const Elf_Internal_Rela *src;
- bfd_byte *dst;
-{
- Elf64_Mips_Internal_Rela mirela;
-
- mirela.r_offset = src[0].r_offset;
- BFD_ASSERT(src[0].r_offset == src[1].r_offset);
- BFD_ASSERT(src[0].r_offset == src[2].r_offset);
-
- mirela.r_type = ELF64_MIPS_R_TYPE (src[0].r_info);
- mirela.r_sym = ELF64_R_SYM (src[0].r_info);
- mirela.r_addend = src[0].r_addend;
- BFD_ASSERT(src[1].r_addend == 0);
- BFD_ASSERT(src[2].r_addend == 0);
-
- mirela.r_type2 = ELF64_MIPS_R_TYPE2 (src[1].r_info);
- mirela.r_ssym = ELF64_MIPS_R_SSYM (src[1].r_info);
- mirela.r_type3 = ELF64_MIPS_R_TYPE3 (src[2].r_info);
-
- mips_elf64_swap_reloca_out (abfd, &mirela,
- (Elf64_Mips_External_Rela *) dst);
-}
-
-/* Calculate the %high function. */
-
-static bfd_vma
-mips_elf64_high (value)
- bfd_vma value;
-{
- return ((value + (bfd_vma) 0x8000) >> 16) & 0xffff;
-}
-
-/* Calculate the %higher function. */
-
-static bfd_vma
-mips_elf64_higher (value)
- bfd_vma value;
-{
- return ((value + (bfd_vma) 0x80008000) >> 32) & 0xffff;
-}
-
-/* Calculate the %highest function. */
-
-static bfd_vma
-mips_elf64_highest (value)
- bfd_vma value;
-{
- return ((value + (bfd_vma) 0x800080008000) >> 48) & 0xffff;
-}
-
-/* Do a R_MIPS_HI16 relocation. */
-
-bfd_reloc_status_type
-mips_elf64_hi16_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
-{
- /* If we're relocating, and this is an external symbol, we don't
- want to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (! reloc_entry->howto->partial_inplace
- || reloc_entry->addend == 0))
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- if (((reloc_entry->addend & 0xffff) + 0x8000) & ~0xffff)
- reloc_entry->addend += 0x8000;
-
- return bfd_reloc_continue;
-}
-
-/* Do a R_MIPS_HIGHER relocation. */
-
-bfd_reloc_status_type
-mips_elf64_higher_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
-{
- /* If we're relocating, and this is an external symbol, we don't
- want to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (! reloc_entry->howto->partial_inplace
- || reloc_entry->addend == 0))
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- if (((reloc_entry->addend & 0xffffffff) + 0x80008000)
- & ~0xffffffff)
- reloc_entry->addend += 0x80008000;
-
- return bfd_reloc_continue;
-}
-
-/* Do a R_MIPS_HIGHEST relocation. */
-
-bfd_reloc_status_type
-mips_elf64_highest_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
-{
- /* If we're relocating, and this is an external symbol, we don't
- want to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (! reloc_entry->howto->partial_inplace
- || reloc_entry->addend == 0))
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- if (((reloc_entry->addend & 0xffffffffffff) + 0x800080008000)
- & ~0xffffffffffff)
- reloc_entry->addend += 0x800080008000;
-
- return bfd_reloc_continue;
-}
-
-/* Do a R_MIPS_GOT16 reloc. This is a reloc against the global offset
- table used for PIC code. If the symbol is an external symbol, the
- instruction is modified to contain the offset of the appropriate
- entry in the global offset table. If the symbol is a section
- symbol, the next reloc is a R_MIPS_LO16 reloc. The two 16 bit
- addends are combined to form the real addend against the section
- symbol; the GOT16 is modified to contain the offset of an entry in
- the global offset table, and the LO16 is modified to offset it
- appropriately. Thus an offset larger than 16 bits requires a
- modified value in the global offset table.
-
- This implementation suffices for the assembler, but the linker does
- not yet know how to create global offset tables. */
-
-bfd_reloc_status_type
-mips_elf64_got16_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- /* If we're relocating, and this an external symbol, we don't want
- to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && reloc_entry->addend == 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- /* If we're relocating, and this is a local symbol, we can handle it
- just like HI16. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) != 0)
- return mips_elf64_hi16_reloc (abfd, reloc_entry, symbol, data,
- input_section, output_bfd, error_message);
-
- abort ();
-}
-
-/* Set the GP value for OUTPUT_BFD. Returns false if this is a
- dangerous relocation. */
-
-static boolean
-mips_elf64_assign_gp (output_bfd, pgp)
- bfd *output_bfd;
- bfd_vma *pgp;
-{
- unsigned int count;
- asymbol **sym;
- unsigned int i;
-
- /* If we've already figured out what GP will be, just return it. */
- *pgp = _bfd_get_gp_value (output_bfd);
- if (*pgp)
- return true;
-
- count = bfd_get_symcount (output_bfd);
- sym = bfd_get_outsymbols (output_bfd);
-
- /* The linker script will have created a symbol named `_gp' with the
- appropriate value. */
- if (sym == (asymbol **) NULL)
- i = count;
- else
- {
- for (i = 0; i < count; i++, sym++)
- {
- register CONST char *name;
-
- name = bfd_asymbol_name (*sym);
- if (*name == '_' && strcmp (name, "_gp") == 0)
- {
- *pgp = bfd_asymbol_value (*sym);
- _bfd_set_gp_value (output_bfd, *pgp);
- break;
- }
- }
- }
-
- if (i >= count)
- {
- /* Only get the error once. */
- *pgp = 4;
- _bfd_set_gp_value (output_bfd, *pgp);
- return false;
- }
-
- return true;
-}
-
-/* We have to figure out the gp value, so that we can adjust the
- symbol value correctly. We look up the symbol _gp in the output
- BFD. If we can't find it, we're stuck. We cache it in the ELF
- target data. We don't need to adjust the symbol value for an
- external symbol if we are producing relocateable output. */
-
-static bfd_reloc_status_type
-mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message, pgp)
- bfd *output_bfd;
- asymbol *symbol;
- boolean relocateable;
- char **error_message;
- bfd_vma *pgp;
-{
- if (bfd_is_und_section (symbol->section)
- && ! relocateable)
- {
- *pgp = 0;
- return bfd_reloc_undefined;
- }
-
- *pgp = _bfd_get_gp_value (output_bfd);
- if (*pgp == 0
- && (! relocateable
- || (symbol->flags & BSF_SECTION_SYM) != 0))
- {
- if (relocateable)
- {
- /* Make up a value. */
- *pgp = symbol->section->output_section->vma + 0x4000;
- _bfd_set_gp_value (output_bfd, *pgp);
- }
- else if (!mips_elf64_assign_gp (output_bfd, pgp))
- {
- *error_message =
- (char *) _("GP relative relocation when _gp not defined");
- return bfd_reloc_dangerous;
- }
- }
-
- return bfd_reloc_ok;
-}
-
-/* Do a R_MIPS_GPREL16 relocation. This is a 16 bit value which must
- become the offset from the gp register. */
-
-bfd_reloc_status_type
-mips_elf64_gprel16_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- boolean relocateable;
- bfd_reloc_status_type ret;
- bfd_vma gp;
-
- /* If we're relocating, and this is an external symbol with no
- addend, we don't want to change anything. We will only have an
- addend if this is a newly created reloc, not read from an ELF
- file. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && reloc_entry->addend == 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- if (output_bfd != (bfd *) NULL)
- relocateable = true;
- else
- {
- relocateable = false;
- output_bfd = symbol->section->output_section->owner;
- }
-
- ret = mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message,
- &gp);
- if (ret != bfd_reloc_ok)
- return ret;
-
- return gprel16_with_gp (abfd, symbol, reloc_entry, input_section,
- relocateable, data, gp);
-}
-
-static bfd_reloc_status_type
-gprel16_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data,
- gp)
- bfd *abfd;
- asymbol *symbol;
- arelent *reloc_entry;
- asection *input_section;
- boolean relocateable;
- PTR data;
- bfd_vma gp;
-{
- bfd_vma relocation;
- unsigned long insn;
- unsigned long val;
-
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
-
- if (reloc_entry->address > input_section->_cooked_size)
- return bfd_reloc_outofrange;
-
- insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
-
- /* Set val to the offset into the section or symbol. */
- if (reloc_entry->howto->src_mask == 0)
- {
- /* This case occurs with the 64-bit MIPS ELF ABI. */
- val = reloc_entry->addend;
- }
- else
- {
- val = ((insn & 0xffff) + reloc_entry->addend) & 0xffff;
- if (val & 0x8000)
- val -= 0x10000;
- }
-
- /* Adjust val for the final section location and GP value. If we
- are producing relocateable output, we don't want to do this for
- an external symbol. */
- if (! relocateable
- || (symbol->flags & BSF_SECTION_SYM) != 0)
- val += relocation - gp;
-
- insn = (insn & ~0xffff) | (val & 0xffff);
- bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
-
- if (relocateable)
- reloc_entry->address += input_section->output_offset;
-
- else if ((long) val >= 0x8000 || (long) val < -0x8000)
- return bfd_reloc_overflow;
-
- return bfd_reloc_ok;
-}
-
-/* Do a R_MIPS_GPREL16 RELA relocation. */
-
-bfd_reloc_status_type
-mips_elf64_gprel16_reloca (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- boolean relocateable;
- bfd_vma gp;
-
- /* This works only for NewABI. */
- BFD_ASSERT (reloc_entry->howto->src_mask == 0);
-
- /* If we're relocating, and this is an external symbol with no
- addend, we don't want to change anything. We will only have an
- addend if this is a newly created reloc, not read from an ELF
- file. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && reloc_entry->addend == 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- if (output_bfd != (bfd *) NULL)
- relocateable = true;
- else
- {
- relocateable = false;
- output_bfd = symbol->section->output_section->owner;
- }
-
- if (prev_reloc_address != reloc_entry->address)
- prev_reloc_address = reloc_entry->address;
- else
- {
- mips_elf64_final_gp (output_bfd, symbol, relocateable, error_message,
- &gp);
- prev_reloc_addend = reloc_entry->addend + reloc_entry->address - gp;
- if (symbol->flags & BSF_LOCAL)
- prev_reloc_addend += _bfd_get_gp_value (abfd);
-/*fprintf(stderr, "Addend: %lx, Next Addend: %lx\n", reloc_entry->addend, prev_reloc_addend);*/
- }
-
- return bfd_reloc_ok;
-}
-
-/* Do a R_MIPS_LITERAL relocation. */
-
-bfd_reloc_status_type
-mips_elf64_literal_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- /* If we're relocating, and this is an external symbol, we don't
- want to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (! reloc_entry->howto->partial_inplace
- || reloc_entry->addend == 0))
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- /* FIXME: The entries in the .lit8 and .lit4 sections should be merged.
- Currently we simply call mips_elf64_gprel16_reloc. */
- return mips_elf64_gprel16_reloc (abfd, reloc_entry, symbol, data,
- input_section, output_bfd, error_message);
-}
-
-/* Do a R_MIPS_GPREL32 relocation. Is this 32 bit value the offset
- from the gp register? XXX */
-
-bfd_reloc_status_type
-mips_elf64_gprel32_reloc (abfd,
- reloc_entry,
- symbol,
- data,
- input_section,
- output_bfd,
- error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- boolean relocateable;
- bfd_reloc_status_type ret;
- bfd_vma gp;
- bfd_vma relocation;
- unsigned long val;
-
- /* If we're relocating, and this is an external symbol with no
- addend, we don't want to change anything. We will only have an
- addend if this is a newly created reloc, not read from an ELF
- file. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && reloc_entry->addend == 0)
- {
- *error_message = (char *)
- _("32bits gp relative relocation occurs for an external symbol");
- return bfd_reloc_outofrange;
- }
-
- if (output_bfd != (bfd *) NULL)
- {
- relocateable = true;
- gp = _bfd_get_gp_value (output_bfd);
- }
- else
- {
- relocateable = false;
- output_bfd = symbol->section->output_section->owner;
-
- ret = mips_elf64_final_gp (output_bfd, symbol, relocateable,
- error_message, &gp);
- if (ret != bfd_reloc_ok)
- return ret;
- }
-
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
-
- if (reloc_entry->address > input_section->_cooked_size)
- return bfd_reloc_outofrange;
-
- if (reloc_entry->howto->src_mask == 0)
- {
- /* This case arises with the 64-bit MIPS ELF ABI. */
- val = 0;
- }
- else
- val = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
-
- /* Set val to the offset into the section or symbol. */
- val += reloc_entry->addend;
-
- /* Adjust val for the final section location and GP value. If we
- are producing relocateable output, we don't want to do this for
- an external symbol. */
- if (! relocateable
- || (symbol->flags & BSF_SECTION_SYM) != 0)
- val += relocation - gp;
-
- bfd_put_32 (abfd, val, (bfd_byte *) data + reloc_entry->address);
-
- if (relocateable)
- reloc_entry->address += input_section->output_offset;
-
- return bfd_reloc_ok;
-}
-
-/* Do a R_MIPS_SHIFT6 relocation. The MSB of the shift is stored at bit 2,
- the rest is at bits 6-10. The bitpos alredy got right by the howto. */
-
-bfd_reloc_status_type
-mips_elf64_shift6_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
-{
- /* If we're relocating, and this is an external symbol, we don't
- want to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (! reloc_entry->howto->partial_inplace
- || reloc_entry->addend == 0))
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- reloc_entry->addend = (reloc_entry->addend & 0x00007c0)
- | (reloc_entry->addend & 0x00000800) >> 9;
-
- return bfd_reloc_continue;
-}
-
-static int
-mips_elf64_additional_program_headers (abfd)
- bfd *abfd;
-{
- int ret = 0;
-
- /* See if we need a PT_MIPS_OPTIONS segment. */
- if (bfd_get_section_by_name (abfd, ".MIPS.options"))
- ++ret;
-
- return ret;
-}
-
-/* Given a BFD reloc type, return a howto structure. */
-
-static reloc_howto_type *
-mips_elf64_reloc_type_lookup (abfd, code)
- bfd *abfd ATTRIBUTE_UNUSED;
- bfd_reloc_code_real_type code;
-{
- /* FIXME: We default to RELA here instead of choosing the right
- relocation variant. */
- reloc_howto_type *howto_table = mips_elf64_howto_table_rela;
-
- switch (code)
- {
- case BFD_RELOC_NONE:
- return &howto_table[R_MIPS_NONE];
- case BFD_RELOC_16:
- return &howto_table[R_MIPS_16];
- case BFD_RELOC_32:
- return &howto_table[R_MIPS_32];
- case BFD_RELOC_64:
- case BFD_RELOC_CTOR:
- /* We need to handle these specially. Select the right
- relocation (R_MIPS_32 or R_MIPS_64) based on the
- size of addresses on this architecture. */
- if (bfd_arch_bits_per_address (abfd) == 32)
- return &howto_table[R_MIPS_32];
- else
- return &howto_table[R_MIPS_64];
-
- case BFD_RELOC_16_PCREL:
- return &howto_table[R_MIPS_PC16];
- case BFD_RELOC_HI16_S:
- return &howto_table[R_MIPS_HI16];
- case BFD_RELOC_LO16:
- return &howto_table[R_MIPS_LO16];
- case BFD_RELOC_GPREL16:
- return &howto_table[R_MIPS_GPREL16];
- case BFD_RELOC_GPREL32:
- return &howto_table[R_MIPS_GPREL32];
- case BFD_RELOC_MIPS_JMP:
- return &howto_table[R_MIPS_26];
- case BFD_RELOC_MIPS_LITERAL:
- return &howto_table[R_MIPS_LITERAL];
- case BFD_RELOC_MIPS_GOT16:
- return &howto_table[R_MIPS_GOT16];
- case BFD_RELOC_MIPS_CALL16:
- return &howto_table[R_MIPS_CALL16];
- case BFD_RELOC_MIPS_SHIFT5:
- return &howto_table[R_MIPS_SHIFT5];
- case BFD_RELOC_MIPS_SHIFT6:
- return &howto_table[R_MIPS_SHIFT6];
- case BFD_RELOC_MIPS_GOT_DISP:
- return &howto_table[R_MIPS_GOT_DISP];
- case BFD_RELOC_MIPS_GOT_PAGE:
- return &howto_table[R_MIPS_GOT_PAGE];
- case BFD_RELOC_MIPS_GOT_OFST:
- return &howto_table[R_MIPS_GOT_OFST];
- case BFD_RELOC_MIPS_GOT_HI16:
- return &howto_table[R_MIPS_GOT_HI16];
- case BFD_RELOC_MIPS_GOT_LO16:
- return &howto_table[R_MIPS_GOT_LO16];
- case BFD_RELOC_MIPS_SUB:
- return &howto_table[R_MIPS_SUB];
- case BFD_RELOC_MIPS_INSERT_A:
- return &howto_table[R_MIPS_INSERT_A];
- case BFD_RELOC_MIPS_INSERT_B:
- return &howto_table[R_MIPS_INSERT_B];
- case BFD_RELOC_MIPS_DELETE:
- return &howto_table[R_MIPS_DELETE];
- case BFD_RELOC_MIPS_HIGHEST:
- return &howto_table[R_MIPS_HIGHEST];
- case BFD_RELOC_MIPS_HIGHER:
- return &howto_table[R_MIPS_HIGHER];
- case BFD_RELOC_MIPS_CALL_HI16:
- return &howto_table[R_MIPS_CALL_HI16];
- case BFD_RELOC_MIPS_CALL_LO16:
- return &howto_table[R_MIPS_CALL_LO16];
- case BFD_RELOC_MIPS_SCN_DISP:
- return &howto_table[R_MIPS_SCN_DISP];
- case BFD_RELOC_MIPS_REL16:
- return &howto_table[R_MIPS_REL16];
- /* Use of R_MIPS_ADD_IMMEDIATE and R_MIPS_PJUMP is deprecated. */
- case BFD_RELOC_MIPS_RELGOT:
- return &howto_table[R_MIPS_RELGOT];
- case BFD_RELOC_MIPS_JALR:
- return &howto_table[R_MIPS_JALR];
-/*
- case BFD_RELOC_MIPS16_JMP:
- return &elf_mips16_jump_howto;
- case BFD_RELOC_MIPS16_GPREL:
- return &elf_mips16_gprel_howto;
- case BFD_RELOC_VTABLE_INHERIT:
- return &elf_mips_gnu_vtinherit_howto;
- case BFD_RELOC_VTABLE_ENTRY:
- return &elf_mips_gnu_vtentry_howto;
- case BFD_RELOC_PCREL_HI16_S:
- return &elf_mips_gnu_rel_hi16;
- case BFD_RELOC_PCREL_LO16:
- return &elf_mips_gnu_rel_lo16;
- case BFD_RELOC_16_PCREL_S2:
- return &elf_mips_gnu_rel16_s2;
- case BFD_RELOC_64_PCREL:
- return &elf_mips_gnu_pcrel64;
- case BFD_RELOC_32_PCREL:
- return &elf_mips_gnu_pcrel32;
-*/
- default:
- bfd_set_error (bfd_error_bad_value);
- return NULL;
- }
-}
-
-/* Prevent relocation handling by bfd for MIPS ELF64. */
-
-static void
-mips_elf64_info_to_howto_rel (abfd, cache_ptr, dst)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *cache_ptr ATTRIBUTE_UNUSED;
- Elf64_Internal_Rel *dst ATTRIBUTE_UNUSED;
-{
- BFD_ASSERT (0);
-}
-
-static void
-mips_elf64_info_to_howto_rela (abfd, cache_ptr, dst)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *cache_ptr ATTRIBUTE_UNUSED;
- Elf64_Internal_Rela *dst ATTRIBUTE_UNUSED;
-{
- BFD_ASSERT (0);
-}
-
-/* Since each entry in an SHT_REL or SHT_RELA section can represent up
- to three relocs, we must tell the user to allocate more space. */
-
-static long
-mips_elf64_get_reloc_upper_bound (abfd, sec)
- bfd *abfd ATTRIBUTE_UNUSED;
- asection *sec;
-{
- return (sec->reloc_count * 3 + 1) * sizeof (arelent *);
-}
-
-/* Read the relocations from one reloc section. */
-
-static boolean
-mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, rel_hdr)
- bfd *abfd;
- asection *asect;
- asymbol **symbols;
- const Elf_Internal_Shdr *rel_hdr;
-{
- PTR allocated = NULL;
- bfd_byte *native_relocs;
- arelent *relents;
- arelent *relent;
- bfd_vma count;
- bfd_vma i;
- int entsize;
- reloc_howto_type *howto_table;
-
- allocated = (PTR) bfd_malloc (rel_hdr->sh_size);
- if (allocated == NULL)
- return false;
-
- if (bfd_seek (abfd, rel_hdr->sh_offset, SEEK_SET) != 0
- || (bfd_bread (allocated, rel_hdr->sh_size, abfd) != rel_hdr->sh_size))
- goto error_return;
-
- native_relocs = (bfd_byte *) allocated;
-
- relents = asect->relocation + asect->reloc_count;
-
- entsize = rel_hdr->sh_entsize;
- BFD_ASSERT (entsize == sizeof (Elf64_Mips_External_Rel)
- || entsize == sizeof (Elf64_Mips_External_Rela));
-
- count = rel_hdr->sh_size / entsize;
-
- if (entsize == sizeof (Elf64_Mips_External_Rel))
- howto_table = mips_elf64_howto_table_rel;
- else
- howto_table = mips_elf64_howto_table_rela;
-
- relent = relents;
- for (i = 0; i < count; i++, native_relocs += entsize)
- {
- Elf64_Mips_Internal_Rela rela;
- boolean used_sym, used_ssym;
- int ir;
-
- if (entsize == sizeof (Elf64_Mips_External_Rela))
- mips_elf64_swap_reloca_in (abfd,
- (Elf64_Mips_External_Rela *) native_relocs,
- &rela);
- else
- {
- Elf64_Mips_Internal_Rel rel;
-
- mips_elf64_swap_reloc_in (abfd,
- (Elf64_Mips_External_Rel *) native_relocs,
- &rel);
- rela.r_offset = rel.r_offset;
- rela.r_sym = rel.r_sym;
- rela.r_ssym = rel.r_ssym;
- rela.r_type3 = rel.r_type3;
- rela.r_type2 = rel.r_type2;
- rela.r_type = rel.r_type;
- rela.r_addend = 0;
- }
-
- /* Each entry represents up to three actual relocations. */
-
- used_sym = false;
- used_ssym = false;
- for (ir = 0; ir < 3; ir++)
- {
- enum elf_mips_reloc_type type;
-
- switch (ir)
- {
- default:
- abort ();
- case 0:
- type = (enum elf_mips_reloc_type) rela.r_type;
- break;
- case 1:
- type = (enum elf_mips_reloc_type) rela.r_type2;
- break;
- case 2:
- type = (enum elf_mips_reloc_type) rela.r_type3;
- break;
- }
-
- if (type == R_MIPS_NONE)
- {
- /* There are no more relocations in this entry. If this
- is the first entry, we need to generate a dummy
- relocation so that the generic linker knows that
- there has been a break in the sequence of relocations
- applying to a particular address. */
- if (ir == 0)
- {
- relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
- if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
- relent->address = rela.r_offset;
- else
- relent->address = rela.r_offset - asect->vma;
- relent->addend = 0;
- relent->howto = &howto_table[(int) R_MIPS_NONE];
- ++relent;
- }
- break;
- }
-
- /* Some types require symbols, whereas some do not. */
- switch (type)
- {
- case R_MIPS_NONE:
- case R_MIPS_LITERAL:
- case R_MIPS_INSERT_A:
- case R_MIPS_INSERT_B:
- case R_MIPS_DELETE:
- relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
- break;
-
- default:
- if (! used_sym)
- {
- if (rela.r_sym == 0)
- relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
- else
- {
- asymbol **ps, *s;
-
- ps = symbols + rela.r_sym - 1;
- s = *ps;
- if ((s->flags & BSF_SECTION_SYM) == 0)
- relent->sym_ptr_ptr = ps;
- else
- relent->sym_ptr_ptr = s->section->symbol_ptr_ptr;
- }
-
- used_sym = true;
- }
- else if (! used_ssym)
- {
- switch (rela.r_ssym)
- {
- case RSS_UNDEF:
- relent->sym_ptr_ptr =
- bfd_abs_section_ptr->symbol_ptr_ptr;
- break;
-
- case RSS_GP:
- case RSS_GP0:
- case RSS_LOC:
- /* FIXME: I think these need to be handled using
- special howto structures. */
- BFD_ASSERT (0);
- break;
-
- default:
- BFD_ASSERT (0);
- break;
- }
-
- used_ssym = true;
- }
- else
- relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
-
- break;
- }
-
- /* The address of an ELF reloc is section relative for an
- object file, and absolute for an executable file or
- shared library. The address of a BFD reloc is always
- section relative. */
- if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
- relent->address = rela.r_offset;
- else
- relent->address = rela.r_offset - asect->vma;
-
- relent->addend = rela.r_addend;
-
- relent->howto = &howto_table[(int) type];
-
- ++relent;
- }
- }
-
- asect->reloc_count += relent - relents;
-
- if (allocated != NULL)
- free (allocated);
-
- return true;
-
- error_return:
- if (allocated != NULL)
- free (allocated);
- return false;
-}
-
-/* Read the relocations. On Irix 6, there can be two reloc sections
- associated with a single data section. */
-
-static boolean
-mips_elf64_slurp_reloc_table (abfd, asect, symbols, dynamic)
- bfd *abfd;
- asection *asect;
- asymbol **symbols;
- boolean dynamic;
-{
- bfd_size_type amt;
- struct bfd_elf_section_data * const d = elf_section_data (asect);
-
- if (dynamic)
- {
- bfd_set_error (bfd_error_invalid_operation);
- return false;
- }
-
- if (asect->relocation != NULL
- || (asect->flags & SEC_RELOC) == 0
- || asect->reloc_count == 0)
- return true;
-
- /* Allocate space for 3 arelent structures for each Rel structure. */
- amt = asect->reloc_count;
- amt *= 3 * sizeof (arelent);
- asect->relocation = (arelent *) bfd_alloc (abfd, amt);
- if (asect->relocation == NULL)
- return false;
-
- /* The slurp_one_reloc_table routine increments reloc_count. */
- asect->reloc_count = 0;
-
- if (! mips_elf64_slurp_one_reloc_table (abfd, asect, symbols, &d->rel_hdr))
- return false;
- if (d->rel_hdr2 != NULL)
- {
- if (! mips_elf64_slurp_one_reloc_table (abfd, asect, symbols,
- d->rel_hdr2))
- return false;
- }
-
- return true;
-}
-
-/* Write out the relocations. */
-
-static void
-mips_elf64_write_relocs (abfd, sec, data)
- bfd *abfd;
- asection *sec;
- PTR data;
-{
- boolean *failedp = (boolean *) data;
- int count;
- Elf_Internal_Shdr *rel_hdr;
- unsigned int idx;
-
- /* If we have already failed, don't do anything. */
- if (*failedp)
- return;
-
- if ((sec->flags & SEC_RELOC) == 0)
- return;
-
- /* The linker backend writes the relocs out itself, and sets the
- reloc_count field to zero to inhibit writing them here. Also,
- sometimes the SEC_RELOC flag gets set even when there aren't any
- relocs. */
- if (sec->reloc_count == 0)
- return;
-
- /* We can combine up to three relocs that refer to the same address
- if the latter relocs have no associated symbol. */
- count = 0;
- for (idx = 0; idx < sec->reloc_count; idx++)
- {
- bfd_vma addr;
- unsigned int i;
-
- ++count;
-
- addr = sec->orelocation[idx]->address;
- for (i = 0; i < 2; i++)
- {
- arelent *r;
-
- if (idx + 1 >= sec->reloc_count)
- break;
- r = sec->orelocation[idx + 1];
- if (r->address != addr
- || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
- || (*r->sym_ptr_ptr)->value != 0)
- break;
-
- /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
-
- ++idx;
- }
- }
-
- rel_hdr = &elf_section_data (sec)->rel_hdr;
-
- /* Do the actual relocation. */
-
- if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rel))
- mips_elf64_write_rel (abfd, sec, rel_hdr, &count, data);
- else if (rel_hdr->sh_entsize == sizeof(Elf64_Mips_External_Rela))
- mips_elf64_write_rela (abfd, sec, rel_hdr, &count, data);
- else
- BFD_ASSERT (0);
-}
-
-static void
-mips_elf64_write_rel (abfd, sec, rel_hdr, count, data)
- bfd *abfd;
- asection *sec;
- Elf_Internal_Shdr *rel_hdr;
- int *count;
- PTR data;
-{
- boolean *failedp = (boolean *) data;
- Elf64_Mips_External_Rel *ext_rel;
- unsigned int idx;
- asymbol *last_sym = 0;
- int last_sym_idx = 0;
-
- rel_hdr->sh_size = (bfd_vma)(rel_hdr->sh_entsize * *count);
- rel_hdr->contents = (PTR) bfd_alloc (abfd, rel_hdr->sh_size);
- if (rel_hdr->contents == NULL)
- {
- *failedp = true;
- return;
- }
-
- ext_rel = (Elf64_Mips_External_Rel *) rel_hdr->contents;
- for (idx = 0; idx < sec->reloc_count; idx++, ext_rel++)
- {
- arelent *ptr;
- Elf64_Mips_Internal_Rel int_rel;
- asymbol *sym;
- int n;
- unsigned int i;
-
- ptr = sec->orelocation[idx];
-
- /* The address of an ELF reloc is section relative for an object
- file, and absolute for an executable file or shared library.
- The address of a BFD reloc is always section relative. */
- if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
- int_rel.r_offset = ptr->address;
- else
- int_rel.r_offset = ptr->address + sec->vma;
-
- sym = *ptr->sym_ptr_ptr;
- if (sym == last_sym)
- n = last_sym_idx;
- else
- {
- last_sym = sym;
- n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
- if (n < 0)
- {
- *failedp = true;
- return;
- }
- last_sym_idx = n;
- }
-
- int_rel.r_sym = n;
- int_rel.r_ssym = RSS_UNDEF;
-
- if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
- && ! _bfd_elf_validate_reloc (abfd, ptr))
- {
- *failedp = true;
- return;
- }
-
- int_rel.r_type = ptr->howto->type;
- int_rel.r_type2 = (int) R_MIPS_NONE;
- int_rel.r_type3 = (int) R_MIPS_NONE;
-
- for (i = 0; i < 2; i++)
- {
- arelent *r;
-
- if (idx + 1 >= sec->reloc_count)
- break;
- r = sec->orelocation[idx + 1];
- if (r->address != ptr->address
- || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
- || (*r->sym_ptr_ptr)->value != 0)
- break;
-
- /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
-
- if (i == 0)
- int_rel.r_type2 = r->howto->type;
- else
- int_rel.r_type3 = r->howto->type;
-
- ++idx;
- }
-
- mips_elf64_swap_reloc_out (abfd, &int_rel, ext_rel);
- }
-
- BFD_ASSERT (ext_rel - (Elf64_Mips_External_Rel *) rel_hdr->contents
- == *count);
-}
-
-static void
-mips_elf64_write_rela (abfd, sec, rela_hdr, count, data)
- bfd *abfd;
- asection *sec;
- Elf_Internal_Shdr *rela_hdr;
- int *count;
- PTR data;
-{
- boolean *failedp = (boolean *) data;
- Elf64_Mips_External_Rela *ext_rela;
- unsigned int idx;
- asymbol *last_sym = 0;
- int last_sym_idx = 0;
-
- rela_hdr->sh_size = (bfd_vma)(rela_hdr->sh_entsize * *count);
- rela_hdr->contents = (PTR) bfd_alloc (abfd, rela_hdr->sh_size);
- if (rela_hdr->contents == NULL)
- {
- *failedp = true;
- return;
- }
-
- ext_rela = (Elf64_Mips_External_Rela *) rela_hdr->contents;
- for (idx = 0; idx < sec->reloc_count; idx++, ext_rela++)
- {
- arelent *ptr;
- Elf64_Mips_Internal_Rela int_rela;
- asymbol *sym;
- int n;
- unsigned int i;
-
- ptr = sec->orelocation[idx];
-
- /* The address of an ELF reloc is section relative for an object
- file, and absolute for an executable file or shared library.
- The address of a BFD reloc is always section relative. */
- if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
- int_rela.r_offset = ptr->address;
- else
- int_rela.r_offset = ptr->address + sec->vma;
-
- sym = *ptr->sym_ptr_ptr;
- if (sym == last_sym)
- n = last_sym_idx;
- else
- {
- last_sym = sym;
- n = _bfd_elf_symbol_from_bfd_symbol (abfd, &sym);
- if (n < 0)
- {
- *failedp = true;
- return;
- }
- last_sym_idx = n;
- }
-
- int_rela.r_sym = n;
- int_rela.r_addend = ptr->addend;
- int_rela.r_ssym = RSS_UNDEF;
-
- if ((*ptr->sym_ptr_ptr)->the_bfd->xvec != abfd->xvec
- && ! _bfd_elf_validate_reloc (abfd, ptr))
- {
- *failedp = true;
- return;
- }
-
- int_rela.r_type = ptr->howto->type;
- int_rela.r_type2 = (int) R_MIPS_NONE;
- int_rela.r_type3 = (int) R_MIPS_NONE;
-
- for (i = 0; i < 2; i++)
- {
- arelent *r;
-
- if (idx + 1 >= sec->reloc_count)
- break;
- r = sec->orelocation[idx + 1];
- if (r->address != ptr->address
- || ! bfd_is_abs_section ((*r->sym_ptr_ptr)->section)
- || (*r->sym_ptr_ptr)->value != 0)
- break;
-
- /* We can merge the reloc at IDX + 1 with the reloc at IDX. */
-
- if (i == 0)
- int_rela.r_type2 = r->howto->type;
- else
- int_rela.r_type3 = r->howto->type;
-
- ++idx;
- }
-
- mips_elf64_swap_reloca_out (abfd, &int_rela, ext_rela);
- }
-
- BFD_ASSERT (ext_rela - (Elf64_Mips_External_Rela *) rela_hdr->contents
- == *count);
-}
-\f
-/* This structure is used to hold .got information when linking. It
- is stored in the tdata field of the bfd_elf_section_data structure. */
-
-struct mips_elf64_got_info
-{
- /* The global symbol in the GOT with the lowest index in the dynamic
- symbol table. */
- struct elf_link_hash_entry *global_gotsym;
- /* The number of global .got entries. */
- unsigned int global_gotno;
- /* The number of local .got entries. */
- unsigned int local_gotno;
- /* The number of local .got entries we have used. */
- unsigned int assigned_gotno;
-};
-
-/* The MIPS ELF64 linker needs additional information for each symbol in
- the global hash table. */
-
-struct mips_elf64_link_hash_entry
-{
- struct elf_link_hash_entry root;
-
- /* External symbol information. */
- EXTR esym;
-
- /* Number of R_MIPS_32, R_MIPS_REL32, or R_MIPS_64 relocs against
- this symbol. */
- unsigned int possibly_dynamic_relocs;
-
- /* If the R_MIPS_32, R_MIPS_REL32, or R_MIPS_64 reloc is against
- a readonly section. */
- boolean readonly_reloc;
-
- /* The index of the first dynamic relocation (in the .rel.dyn
- section) against this symbol. */
- unsigned int min_dyn_reloc_index;
-
- /* We must not create a stub for a symbol that has relocations
- related to taking the function's address, i.e. any but
- R_MIPS_CALL*16 ones -- see "MIPS ABI Supplement, 3rd Edition",
- p. 4-20. */
- boolean no_fn_stub;
-
- /* If there is a stub that 32 bit functions should use to call this
- 16 bit function, this points to the section containing the stub. */
- asection *fn_stub;
-
- /* Whether we need the fn_stub; this is set if this symbol appears
- in any relocs other than a 16 bit call. */
- boolean need_fn_stub;
-
- /* If there is a stub that 16 bit functions should use to call this
- 32 bit function, this points to the section containing the stub. */
- asection *call_stub;
-
- /* This is like the call_stub field, but it is used if the function
- being called returns a floating point value. */
- asection *call_fp_stub;
-};
-\f
- /* The mips16 compiler uses a couple of special sections to handle
- floating point arguments.
-
- Section names that look like .mips16.fn.FNNAME contain stubs that
- copy floating point arguments from the fp regs to the gp regs and
- then jump to FNNAME. If any 32 bit function calls FNNAME, the
- call should be redirected to the stub instead. If no 32 bit
- function calls FNNAME, the stub should be discarded. We need to
- consider any reference to the function, not just a call, because
- if the address of the function is taken we will need the stub,
- since the address might be passed to a 32 bit function.
-
- Section names that look like .mips16.call.FNNAME contain stubs
- that copy floating point arguments from the gp regs to the fp
- regs and then jump to FNNAME. If FNNAME is a 32 bit function,
- then any 16 bit function that calls FNNAME should be redirected
- to the stub instead. If FNNAME is not a 32 bit function, the
- stub should be discarded.
-
- .mips16.call.fp.FNNAME sections are similar, but contain stubs
- which call FNNAME and then copy the return value from the fp regs
- to the gp regs. These stubs store the return value in $18 while
- calling FNNAME; any function which might call one of these stubs
- must arrange to save $18 around the call. (This case is not
- needed for 32 bit functions that call 16 bit functions, because
- 16 bit functions always return floating point values in both
- $f0/$f1 and $2/$3.)
-
- Note that in all cases FNNAME might be defined statically.
- Therefore, FNNAME is not used literally. Instead, the relocation
- information will indicate which symbol the section is for.
-
- We record any stubs that we find in the symbol table. */
-
-#define FN_STUB ".mips16.fn."
-#define CALL_STUB ".mips16.call."
-#define CALL_FP_STUB ".mips16.call.fp."
-
-/* MIPS ELF64 linker hash table. */
-
-struct mips_elf64_link_hash_table
-{
- struct elf_link_hash_table root;
- /* This is set if we see any mips16 stub sections. */
- boolean mips16_stubs_seen;
-};
-
-/* Look up an entry in a MIPS ELF64 linker hash table. */
-
-#define mips_elf64_link_hash_lookup(table, string, create, copy, follow) \
- ((struct mips_elf64_link_hash_entry *) \
- elf_link_hash_lookup (&(table)->root, (string), (create), \
- (copy), (follow)))
-
-/* Traverse a MIPS ELF linker hash table. */
-
-#define mips_elf64_link_hash_traverse(table, func, info) \
- (elf_link_hash_traverse \
- (&(table)->root, \
- (boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
- (info)))
-
-/* Get the MIPS ELF64 linker hash table from a link_info structure. */
-
-#define mips_elf64_hash_table(p) \
- ((struct mips_elf64_link_hash_table *) ((p)->hash))
-
-/* Create an entry in a MIPS ELF64 linker hash table. */
-
-static struct bfd_hash_entry *
-mips_elf64_link_hash_newfunc (entry, table, string)
- struct bfd_hash_entry *entry;
- struct bfd_hash_table *table;
- const char *string;
-{
- struct mips_elf64_link_hash_entry *ret =
- (struct mips_elf64_link_hash_entry *) entry;
-
- /* Allocate the structure if it has not already been allocated by a
- subclass. */
- if (ret == (struct mips_elf64_link_hash_entry *) NULL)
- ret = ((struct mips_elf64_link_hash_entry *)
- bfd_hash_allocate (table,
- sizeof (struct mips_elf64_link_hash_entry)));
- if (ret == (struct mips_elf64_link_hash_entry *) NULL)
- return (struct bfd_hash_entry *) ret;
-
- /* Call the allocation method of the superclass. */
- ret = ((struct mips_elf64_link_hash_entry *)
- _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
- table, string));
- if (ret != (struct mips_elf64_link_hash_entry *) NULL)
- {
- /* Set local fields. */
- memset (&ret->esym, 0, sizeof (EXTR));
- /* We use -2 as a marker to indicate that the information has
- not been set. -1 means there is no associated ifd. */
- ret->esym.ifd = -2;
- ret->possibly_dynamic_relocs = 0;
- ret->readonly_reloc = false;
- ret->min_dyn_reloc_index = 0;
- ret->no_fn_stub = false;
- ret->fn_stub = NULL;
- ret->need_fn_stub = false;
- ret->call_stub = NULL;
- ret->call_fp_stub = NULL;
- }
-
- return (struct bfd_hash_entry *) ret;
-}
-
-/* Create a MIPS ELF64 linker hash table. */
-
-struct bfd_link_hash_table *
-mips_elf64_link_hash_table_create (abfd)
- bfd *abfd;
-{
- struct mips_elf64_link_hash_table *ret;
-
- ret = ((struct mips_elf64_link_hash_table *)
- bfd_alloc (abfd, sizeof (struct mips_elf64_link_hash_table)));
- if (ret == (struct mips_elf64_link_hash_table *) NULL)
- return NULL;
-
- if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
- mips_elf64_link_hash_newfunc))
- {
- bfd_release (abfd, ret);
- return NULL;
- }
-
- ret->mips16_stubs_seen = false;
-
- return &ret->root.root;
-}
-\f
-/* Returns the offset for the entry at the INDEXth position
- in the GOT. */
-
-static bfd_vma
-mips_elf64_got_offset_from_index (dynobj, output_bfd, index)
- bfd *dynobj;
- bfd *output_bfd;
- bfd_vma index;
-{
- asection *sgot;
- bfd_vma gp;
-
- sgot = bfd_get_section_by_name (dynobj, ".got");
- gp = _bfd_get_gp_value (output_bfd);
- return (sgot->output_section->vma + sgot->output_offset + index -
- gp);
-}
-
-/* Returns the GOT information associated with the link indicated by
- INFO. If SGOTP is non-NULL, it is filled in with the GOT
- section. */
-
-static struct mips_elf64_got_info *
-_mips_elf64_got_info (abfd, sgotp)
- bfd *abfd;
- asection **sgotp;
-{
- asection *sgot;
- struct mips_elf64_got_info *g;
-
- sgot = bfd_get_section_by_name (abfd, ".got");
- BFD_ASSERT (sgot != NULL);
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_elf64_got_info *) elf_section_data (sgot)->tdata;
- BFD_ASSERT (g != NULL);
-
- if (sgotp)
- *sgotp = sgot;
- return g;
-}
-
-/* Sign-extend VALUE, which has the indicated number of BITS. */
-
-static bfd_vma
-mips_elf64_sign_extend (value, bits)
- bfd_vma value;
- int bits;
-{
- if (value & ((bfd_vma)1 << (bits - 1)))
- /* VALUE is negative. */
- value |= ((bfd_vma) - 1) << bits;
-
- return value;
-}
-
-/* Return non-zero if the indicated VALUE has overflowed the maximum
- range expressable by a signed number with the indicated number of
- BITS. */
-
-static boolean
-mips_elf64_overflow_p (value, bits)
- bfd_vma value;
- int bits;
-{
- bfd_signed_vma svalue = (bfd_signed_vma) value;
-
- if (svalue > (1 << (bits - 1)) - 1)
- /* The value is too big. */
- return true;
- else if (svalue < -(1 << (bits - 1)))
- /* The value is too small. */
- return true;
-
- /* All is well. */
- return false;
-}
-\f
-/* Returns the GOT index for the global symbol indicated by H. */
-
-static bfd_vma
-mips_elf64_global_got_index (abfd, h)
- bfd *abfd;
- struct elf_link_hash_entry *h;
-{
- bfd_vma index;
- asection *sgot;
- struct mips_elf64_got_info *g;
-
- g = _mips_elf64_got_info (abfd, &sgot);
-
- /* Once we determine the global GOT entry with the lowest dynamic
- symbol table index, we must put all dynamic symbols with greater
- indices into the GOT. That makes it easy to calculate the GOT
- offset. */
- BFD_ASSERT (h->dynindx >= g->global_gotsym->dynindx);
- index = ((h->dynindx - g->global_gotsym->dynindx + g->local_gotno)
- * (get_elf_backend_data (abfd)->s->arch_size / 8));
- BFD_ASSERT (index < sgot->_raw_size);
-
- return index;
-}
-
-struct mips_elf64_hash_sort_data
-{
- /* The symbol in the global GOT with the lowest dynamic symbol table
- index. */
- struct elf_link_hash_entry *low;
- /* The least dynamic symbol table index corresponding to a symbol
- with a GOT entry. */
- long min_got_dynindx;
- /* The greatest dynamic symbol table index not corresponding to a
- symbol without a GOT entry. */
- long max_non_got_dynindx;
-};
-
-/* If H needs a GOT entry, assign it the highest available dynamic
- index. Otherwise, assign it the lowest available dynamic
- index. */
-
-static boolean
-mips_elf64_sort_hash_table_f (h, data)
- struct mips_elf64_link_hash_entry *h;
- PTR data;
-{
- struct mips_elf64_hash_sort_data *hsd
- = (struct mips_elf64_hash_sort_data *) data;
-
- /* Symbols without dynamic symbol table entries aren't interesting
- at all. */
- if (h->root.dynindx == -1)
- return true;
-
- if (h->root.got.offset != 1)
- h->root.dynindx = hsd->max_non_got_dynindx++;
- else
- {
- h->root.dynindx = --hsd->min_got_dynindx;
- hsd->low = (struct elf_link_hash_entry *) h;
- }
-
- return true;
-}
-
-/* Sort the dynamic symbol table so that symbols that need GOT entries
- appear towards the end. This reduces the amount of GOT space
- required. MAX_LOCAL is used to set the number of local symbols
- known to be in the dynamic symbol table. During
- mips_elf64_size_dynamic_sections, this value is 1. Afterward, the
- section symbols are added and the count is higher. */
-
-static boolean
-mips_elf64_sort_hash_table (info, max_local)
- struct bfd_link_info *info;
- unsigned long max_local;
-{
- struct mips_elf64_hash_sort_data hsd;
- struct mips_elf64_got_info *g;
- bfd *dynobj;
-
- dynobj = elf_hash_table (info)->dynobj;
-
- hsd.low = NULL;
- hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount;
- hsd.max_non_got_dynindx = max_local;
- mips_elf64_link_hash_traverse (((struct mips_elf64_link_hash_table *)
- elf_hash_table (info)),
- mips_elf64_sort_hash_table_f,
- &hsd);
-
- /* There shoud have been enough room in the symbol table to
- accomodate both the GOT and non-GOT symbols. */
- BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
-
- /* Now we know which dynamic symbol has the lowest dynamic symbol
- table index in the GOT. */
- g = _mips_elf64_got_info (dynobj, NULL);
- g->global_gotsym = hsd.low;
-
- return true;
-}
-\f
-#if 0
-/* Swap in an MSYM entry. */
-
-static void
-mips_elf64_swap_msym_in (abfd, ex, in)
- bfd *abfd;
- const Elf32_External_Msym *ex;
- Elf32_Internal_Msym *in;
-{
- in->ms_hash_value = H_GET_32 (abfd, ex->ms_hash_value);
- in->ms_info = H_GET_32 (abfd, ex->ms_info);
-}
-#endif
-/* Swap out an MSYM entry. */
-
-static void
-mips_elf64_swap_msym_out (abfd, in, ex)
- bfd *abfd;
- const Elf32_Internal_Msym *in;
- Elf32_External_Msym *ex;
-{
- H_PUT_32 (abfd, in->ms_hash_value, ex->ms_hash_value);
- H_PUT_32 (abfd, in->ms_info, ex->ms_info);
-}
-\f
-/* Create a local GOT entry for VALUE. Return the index of the entry,
- or -1 if it could not be created. */
-
-static bfd_vma
-mips_elf64_create_local_got_entry (abfd, g, sgot, value)
- bfd *abfd;
- struct mips_elf64_got_info *g;
- asection *sgot;
- bfd_vma value;
-{
- CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
-
- if (g->assigned_gotno >= g->local_gotno)
- {
- /* We didn't allocate enough space in the GOT. */
- (*_bfd_error_handler)
- (_("not enough GOT space for local GOT entries"));
- bfd_set_error (bfd_error_bad_value);
- return (bfd_vma) -1;
- }
-
- bfd_put_64 (abfd, value, (sgot->contents + got_size * g->assigned_gotno));
- return got_size * g->assigned_gotno++;
-}
-
-/* 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_elf64_local_got_index (abfd, info, value)
- bfd *abfd;
- struct bfd_link_info *info;
- bfd_vma value;
-{
- CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
- asection *sgot;
- struct mips_elf64_got_info *g;
- bfd_byte *entry;
-
- g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
-
- /* Look to see if we already have an appropriate entry. */
- for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
- entry != sgot->contents + got_size * g->assigned_gotno;
- entry += got_size)
- {
- bfd_vma address = bfd_get_64 (abfd, entry);
- if (address == value)
- return entry - sgot->contents;
- }
-
- return mips_elf64_create_local_got_entry (abfd, g, sgot, value);
-}
-
-/* Find a GOT entry that is within 32KB of the VALUE. These entries
- are supposed to be placed at small offsets in the GOT, i.e.,
- within 32KB of GP. Return the index into the GOT for this page,
- and store the offset from this entry to the desired address in
- OFFSETP, if it is non-NULL. */
-
-static bfd_vma
-mips_elf64_got_page (abfd, info, value, offsetp)
- bfd *abfd;
- struct bfd_link_info *info;
- bfd_vma value;
- bfd_vma *offsetp;
-{
- CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
- asection *sgot;
- struct mips_elf64_got_info *g;
- bfd_byte *entry;
- bfd_byte *last_entry;
- bfd_vma index = 0;
- bfd_vma address;
-
- g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
-
- /* Look to see if we aleady have an appropriate entry. */
- last_entry = sgot->contents + got_size * g->assigned_gotno;
- for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
- entry != last_entry;
- entry += got_size)
- {
- address = bfd_get_64 (abfd, entry);
-
- if (!mips_elf64_overflow_p (value - address, 16))
- {
- /* This entry will serve as the page pointer. We can add a
- 16-bit number to it to get the actual address. */
- index = entry - sgot->contents;
- break;
- }
- }
-
- /* If we didn't have an appropriate entry, we create one now. */
- if (entry == last_entry)
- index = mips_elf64_create_local_got_entry (abfd, g, sgot, value);
-
- if (offsetp)
- {
- address = bfd_get_64 (abfd, entry);
- *offsetp = value - address;
- }
-
- return index;
-}
-
-/* Find a GOT entry whose higher-order 16 bits are the same as those
- for value. Return the index into the GOT for this entry. */
-
-static bfd_vma
-mips_elf64_got16_entry (abfd, info, value, external)
- bfd *abfd;
- struct bfd_link_info *info;
- bfd_vma value;
- boolean external;
-{
- CONST bfd_vma got_size = get_elf_backend_data (abfd)->s->arch_size / 8;
- asection *sgot;
- struct mips_elf64_got_info *g;
- bfd_byte *entry;
- bfd_byte *last_entry;
- bfd_vma index = 0;
- bfd_vma address;
-
- if (! external)
- {
- /* Although the ABI says that it is "the high-order 16 bits" that we
- want, it is really the %high value. The complete value is
- calculated with a `addiu' of a LO16 relocation, just as with a
- HI16/LO16 pair. */
- value = mips_elf64_high (value) << 16;
- }
-
- g = _mips_elf64_got_info (elf_hash_table (info)->dynobj, &sgot);
-
- /* Look to see if we already have an appropriate entry. */
- last_entry = sgot->contents + got_size * g->assigned_gotno;
- for (entry = (sgot->contents + got_size * MIPS_RESERVED_GOTNO);
- entry != last_entry;
- entry += got_size)
- {
- address = bfd_get_64 (abfd, entry);
- if (address == value)
- {
- /* This entry has the right high-order 16 bits, and the low-order
- 16 bits are set to zero. */
- index = entry - sgot->contents;
- break;
- }
- }
-
- /* If we didn't have an appropriate entry, we create one now. */
- if (entry == last_entry)
- index = mips_elf64_create_local_got_entry (abfd, g, sgot, value);
-
- return index;
-}
-\f
-/* Return whether a relocation is against a local symbol. */
-
-static boolean
-mips_elf64_local_relocation_p (input_bfd, relocation, local_sections,
- check_forced)
- bfd *input_bfd;
- const Elf_Internal_Rela *relocation;
- asection **local_sections;
- boolean check_forced;
-{
- unsigned long r_symndx;
- Elf_Internal_Shdr *symtab_hdr;
- struct mips_elf64_link_hash_entry* h;
- size_t extsymoff;
-
- r_symndx = ELF64_R_SYM (relocation->r_info);
- symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
- extsymoff = (elf_bad_symtab (input_bfd)) ? 0 : symtab_hdr->sh_info;
-
- if (r_symndx < extsymoff)
- return true;
- if (elf_bad_symtab (input_bfd) && local_sections[r_symndx] != NULL)
- return true;
-
- if (check_forced)
- {
- /* Look up the hash table to check whether the symbol
- was forced local. */
- h = (struct mips_elf64_link_hash_entry *)
- elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
- /* Find the real hash-table entry for this symbol. */
- while (h->root.root.type == bfd_link_hash_indirect
- || h->root.root.type == bfd_link_hash_warning)
- h = (struct mips_elf64_link_hash_entry *) h->root.root.u.i.link;
- if ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
- return true;
- }
-
- return false;
-}
-\f
-/* Returns the first relocation of type r_type found, beginning with
- RELOCATION. RELEND is one-past-the-end of the relocation table. */
-
-static const Elf_Internal_Rela *
-mips_elf64_next_relocation (r_type, relocation, relend)
- unsigned int r_type;
- const Elf_Internal_Rela *relocation;
- const Elf_Internal_Rela *relend;
-{
- /* According to the MIPS ELF ABI, the R_MIPS_LO16 relocation must be
- immediately following. However, for the IRIX6 ABI, the next
- relocation may be a composed relocation consisting of several
- relocations for the same address. In that case, the R_MIPS_LO16
- relocation may occur as one of these. We permit a similar
- extension in general, as that is useful for GCC. */
- while (relocation < relend)
- {
- if (ELF64_MIPS_R_TYPE (relocation->r_info) == r_type)
- return relocation;
-
- ++relocation;
- }
-
- /* We didn't find it. */
- bfd_set_error (bfd_error_bad_value);
- return NULL;
-}
-
-/* Create a rel.dyn relocation for the dynamic linker to resolve. REL
- is the original relocation, which is now being transformed into a
- dynamic relocation. The ADDENDP is adjusted if necessary; the
- caller should store the result in place of the original addend. */
-
-static boolean
-mips_elf64_create_dynamic_relocation (output_bfd, info, rel, h, sec,
- symbol, addendp, input_section)
- bfd *output_bfd;
- struct bfd_link_info *info;
- const Elf_Internal_Rela *rel;
- struct mips_elf64_link_hash_entry *h;
- asection *sec;
- bfd_vma symbol;
- bfd_vma *addendp;
- asection *input_section;
-{
- Elf_Internal_Rel outrel[3];
- boolean skip;
- asection *sreloc;
- bfd *dynobj;
- int r_type;
-
- r_type = ELF64_MIPS_R_TYPE (rel->r_info);
- dynobj = elf_hash_table (info)->dynobj;
- sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn");
- BFD_ASSERT (sreloc != NULL);
- BFD_ASSERT (sreloc->contents != NULL);
- BFD_ASSERT ((sreloc->reloc_count
- * get_elf_backend_data (output_bfd)->s->sizeof_rel)
- < sreloc->_raw_size);
-
- skip = false;
- outrel[0].r_offset = _bfd_elf_section_offset (output_bfd, info,
- input_section,
- rel[0].r_offset);
-
- /* We begin by assuming that the offset for the dynamic relocation
- is the same as for the original relocation. We'll adjust this
- later to reflect the correct output offsets. */
- if (elf_section_data (input_section)->sec_info_type != ELF_INFO_TYPE_STABS)
- {
- outrel[1].r_offset = rel[1].r_offset;
- outrel[2].r_offset = rel[2].r_offset;
- }
- else
- {
- /* Except that in a stab section things are more complex.
- Because we compress stab information, the offset given in the
- relocation may not be the one we want; we must let the stabs
- machinery tell us the offset. */
- outrel[1].r_offset = outrel[0].r_offset;
- outrel[2].r_offset = outrel[0].r_offset;
- /* If we didn't need the relocation at all, this value will be
- -1. */
- if (outrel[0].r_offset == (bfd_vma) -1)
- skip = true;
- }