summaryrefslogtreecommitdiff
path: root/bfd/elfxx-mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r--bfd/elfxx-mips.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 2b2f615934a..c8c9870fdd1 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -5451,6 +5451,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 = (MNAME(abfd,_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 <reg>, turn it into bgezal $zero, <target>. */
+ if ((instruction & 0xfc1fffff) == 0x0000f809)
+ instruction = 0x04110000;
+ /* If it was jr <reg>, turn it into b <target>. */
+ 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