-/* We use this instead of bfd_elf_generic_reloc because the latter
- gets the handling of zero addends wrong. */
-static bfd_reloc_status_type
-mips_elf_generic_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
- && (symbol->flags & BSF_LOCAL) != 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- /* Just go on, nothing to see here. */
- return bfd_reloc_continue;
-}
-
-/* Do a R_MIPS_HI16 relocation. This has to be done in combination
- with a R_MIPS_LO16 reloc, because there is a carry from the LO16 to
- the HI16. Here we just save the information we need; we do the
- actual relocation when we see the LO16.
-
- MIPS ELF requires that the LO16 immediately follow the HI16. As a
- GNU extension, for non-pc-relative relocations, we permit an
- arbitrary number of HI16 relocs to be associated with a single LO16
- reloc. This extension permits gcc to output the HI and LO relocs
- itself.
-
- This cannot be done for PC-relative relocations because both the HI16
- and LO16 parts of the relocations must be done relative to the LO16
- part, and there can be carry to or borrow from the HI16 part. */
-
-struct mips_hi16
-{
- struct mips_hi16 *next;
- bfd_byte *addr;
- bfd_vma addend;
-};
-
-/* FIXME: This should not be a static variable. */
-
-static struct mips_hi16 *mips_hi16_list;
-
-static bfd_reloc_status_type
-mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message;
-{
- bfd_reloc_status_type ret;
- bfd_vma relocation;
- struct mips_hi16 *n;
-
- /* 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
- && (symbol->flags & BSF_LOCAL) != 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- ret = bfd_reloc_ok;
-
- if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
- {
- bfd_boolean relocatable;
- bfd_vma gp;
-
- if (ret == bfd_reloc_undefined)
- abort ();
-
- if (output_bfd != NULL)
- relocatable = TRUE;
- else
- {
- relocatable = FALSE;
- output_bfd = symbol->section->output_section->owner;
- }
-
- ret = mips_elf_final_gp (output_bfd, symbol, relocatable,
- error_message, &gp);
- if (ret != bfd_reloc_ok)
- return ret;
-
- relocation = gp - reloc_entry->address;
- }
- else
- {
- if (bfd_is_und_section (symbol->section)
- && output_bfd == (bfd *) NULL)
- ret = bfd_reloc_undefined;
-
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
- }
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
- relocation += reloc_entry->addend;
-
- if (reloc_entry->address > input_section->_cooked_size)
- return bfd_reloc_outofrange;
-
- /* Save the information, and let LO16 do the actual relocation. */
- n = (struct mips_hi16 *) bfd_malloc ((bfd_size_type) sizeof *n);
- if (n == NULL)
- return bfd_reloc_outofrange;
- n->addr = (bfd_byte *) data + reloc_entry->address;
- n->addend = relocation;
- n->next = mips_hi16_list;
- mips_hi16_list = n;
-
- if (output_bfd != (bfd *) NULL)
- reloc_entry->address += input_section->output_offset;
-
- return ret;
-}
-
-/* Do a R_MIPS_LO16 relocation. This is a straightforward 16 bit
- inplace relocation; this function exists in order to do the
- R_MIPS_HI16 relocation described above. */
-
-static bfd_reloc_status_type
-mips_elf_lo16_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;
-{
- arelent gp_disp_relent;
-
- if (mips_hi16_list != NULL)
- {
- struct mips_hi16 *l;
-
- l = mips_hi16_list;
- while (l != NULL)
- {
- unsigned long insn;
- unsigned long val;
- unsigned long vallo;
- struct mips_hi16 *next;
-
- /* Do the HI16 relocation. Note that we actually don't need
- to know anything about the LO16 itself, except where to
- find the low 16 bits of the addend needed by the LO16. */
- insn = bfd_get_32 (abfd, l->addr);
- vallo = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
-
- /* The low order 16 bits are always treated as a signed
- value. */
- vallo = ((vallo & 0xffff) ^ 0x8000) - 0x8000;
- val = ((insn & 0xffff) << 16) + vallo;
- val += l->addend;
-
- /* If PC-relative, we need to subtract out the address of the LO
- half of the HI/LO. (The actual relocation is relative
- to that instruction.) */
- if (reloc_entry->howto->pc_relative)
- val -= reloc_entry->address;
-
- /* At this point, "val" has the value of the combined HI/LO
- pair. If the low order 16 bits (which will be used for
- the LO16 insn) are negative, then we will need an
- adjustment for the high order 16 bits. */
- val += 0x8000;
- val = (val >> 16) & 0xffff;
-
- insn &= ~ (bfd_vma) 0xffff;
- insn |= val;
- bfd_put_32 (abfd, (bfd_vma) insn, l->addr);
-
- if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
- {
- gp_disp_relent = *reloc_entry;
- reloc_entry = &gp_disp_relent;
- reloc_entry->addend = l->addend;
- }
-
- next = l->next;
- free (l);
- l = next;
- }
-
- mips_hi16_list = NULL;
- }
- else if (strcmp (bfd_asymbol_name (symbol), "_gp_disp") == 0)
- {
- bfd_reloc_status_type ret;
- bfd_vma gp, relocation;
-
- /* FIXME: Does this case ever occur? */
-
- ret = mips_elf_final_gp (output_bfd, symbol, TRUE, error_message, &gp);
- if (ret != bfd_reloc_ok)
- return ret;
-
- relocation = gp - reloc_entry->address;
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
- relocation += reloc_entry->addend;
-
- if (reloc_entry->address > input_section->_cooked_size)
- return bfd_reloc_outofrange;
-
- gp_disp_relent = *reloc_entry;
- reloc_entry = &gp_disp_relent;
- reloc_entry->addend = relocation - 4;
- }
-
- /* Now do the LO16 reloc in the usual way. */
- return mips_elf_generic_reloc (abfd, reloc_entry, symbol, data,
- input_section, output_bfd, error_message);
-}
-
-/* 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. */
-
-static bfd_reloc_status_type
-mips_elf_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 is an external symbol, we don't want
- to change anything. */
- if (output_bfd != (bfd *) NULL
- && (symbol->flags & BSF_SECTION_SYM) == 0
- && (symbol->flags & BSF_LOCAL) != 0)
- {
- reloc_entry->address += input_section->output_offset;
- return bfd_reloc_ok;
- }
-
- return mips_elf_hi16_reloc (abfd, reloc_entry, symbol, data,
- input_section, output_bfd, error_message);
-}
-