diff --git a/bfd/ChangeLog b/bfd/ChangeLog index a29b48ace47..b9718b0b904 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,9 @@ +2003-03-25 Alexandre Oliva + + * elfxx-mips.c (_bfd_mips_relax_section): New function. + * elfxx-mips.h (_bfd_mips_relax_section): Declare. + * elfn32-mips.c, elf64-mips.c: Use it. + 2003-03-25 Stan Cox Nick Clifton diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c index 46d9a92cb5b..39f21b3046d 100644 --- a/bfd/elf64-mips.c +++ b/bfd/elf64-mips.c @@ -2887,6 +2887,7 @@ const struct elf_size_info mips_elf64_size_info = #define bfd_elf64_canonicalize_reloc mips_elf64_canonicalize_reloc #define bfd_elf64_get_dynamic_reloc_upper_bound mips_elf64_get_dynamic_reloc_upper_bound #define bfd_elf64_canonicalize_dynamic_reloc mips_elf64_canonicalize_dynamic_reloc +#define bfd_elf64_bfd_relax_section _bfd_mips_relax_section /* MIPS ELF64 archive functions. */ #define bfd_elf64_archive_functions diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c index 00a0e88c1ce..9105d185bb8 100644 --- a/bfd/elfn32-mips.c +++ b/bfd/elfn32-mips.c @@ -2211,6 +2211,7 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = { #define bfd_elf32_bfd_set_private_flags _bfd_mips_elf_set_private_flags #define bfd_elf32_bfd_print_private_bfd_data \ _bfd_mips_elf_print_private_bfd_data +#define bfd_elf32_bfd_relax_section _bfd_mips_relax_section /* Support for SGI-ish mips targets using n32 ABI. */ 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 , 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 diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h index 3f7794361b6..3064c39d137 100644 --- a/bfd/elfxx-mips.h +++ b/bfd/elfxx-mips.h @@ -108,3 +108,6 @@ extern bfd_reloc_status_type _bfd_mips_elf32_gprel16_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); extern unsigned long _bfd_elf_mips_mach PARAMS ((flagword)); +extern bfd_boolean _bfd_mips_relax_section (bfd *, asection *, + struct bfd_link_info *, + bfd_boolean *);