aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-sparc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-sparc.c')
-rw-r--r--bfd/elf32-sparc.c114
1 files changed, 113 insertions, 1 deletions
diff --git a/bfd/elf32-sparc.c b/bfd/elf32-sparc.c
index 1c8d0ba..70d65a5 100644
--- a/bfd/elf32-sparc.c
+++ b/bfd/elf32-sparc.c
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/sparc.h"
+#include "opcode/sparc.h"
static reloc_howto_type *elf32_sparc_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
@@ -36,6 +37,8 @@ static boolean elf32_sparc_adjust_dynamic_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
static boolean elf32_sparc_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
+static boolean elf32_sparc_relax_section
+ PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *));
static boolean elf32_sparc_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -1059,6 +1062,23 @@ elf32_sparc_size_dynamic_sections (output_bfd, info)
return true;
}
+
+#define SET_SEC_DO_RELAX(section) do { elf_section_data(section)->tdata = (void *)1; } while (0)
+#define SEC_DO_RELAX(section) (elf_section_data(section)->tdata == (void *)1)
+
+/*ARGSUSED*/
+static boolean
+elf32_sparc_relax_section (abfd, section, link_info, again)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ asection *section ATTRIBUTE_UNUSED;
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED;
+ boolean *again;
+{
+ *again = false;
+ SET_SEC_DO_RELAX (section);
+ return true;
+}
+
/* Relocate a SPARC ELF section. */
static boolean
@@ -1518,6 +1538,7 @@ elf32_sparc_relocate_section (output_bfd, info, input_bfd, input_section,
break;
}
+ r = bfd_reloc_continue;
if (r_type == R_SPARC_WDISP16)
{
bfd_vma x;
@@ -1549,7 +1570,97 @@ elf32_sparc_relocate_section (output_bfd, info, input_bfd, input_section,
bfd_putl32 (/*input_bfd,*/ x, contents + rel->r_offset);
r = bfd_reloc_ok;
}
- else
+ else if ((r_type == R_SPARC_WDISP30 || r_type == R_SPARC_WPLT30)
+ && SEC_DO_RELAX (input_section)
+ && rel->r_offset + 4 < input_section->_raw_size)
+ {
+#define G0 0
+#define O7 15
+#define XCC (2 << 20)
+#define COND(x) (((x)&0xf)<<25)
+#define CONDA COND(0x8)
+#define INSN_BPA (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA (F2(0,2) | CONDA)
+#define INSN_OR F3(2, 0x2, 0)
+#define INSN_NOP F2(0,4)
+
+ bfd_vma x, y;
+
+ /* If the instruction is a call with either:
+ restore
+ arithmetic instruction with rd == %o7
+ where rs1 != %o7 and rs2 if it is register != %o7
+ then we can optimize if the call destination is near
+ by changing the call into a branch always. */
+ x = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ y = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ if ((x & OP(~0)) == OP(1) && (y & OP(~0)) == OP(2))
+ {
+ if (((y & OP3(~0)) == OP3(0x3d) /* restore */
+ || ((y & OP3(0x28)) == 0 /* arithmetic */
+ && (y & RD(~0)) == RD(O7)))
+ && (y & RS1(~0)) != RS1(O7)
+ && ((y & F3I(~0))
+ || (y & RS2(~0)) != RS2(O7)))
+ {
+ bfd_vma reloc;
+
+ reloc = relocation + rel->r_addend - rel->r_offset;
+ reloc -= (input_section->output_section->vma
+ + input_section->output_offset);
+
+ /* Ensure the reloc fits into simm22. */
+ if ((reloc & 3) == 0
+ && ((reloc & ~(bfd_vma)0x7fffff) == 0
+ || ((reloc | 0x7fffff) == ~(bfd_vma)0)))
+ {
+ reloc >>= 2;
+
+ /* Check whether it fits into simm19 on v9. */
+ if (((reloc & 0x3c0000) == 0
+ || (reloc & 0x3c0000) == 0x3c0000)
+ && (elf_elfheader (output_bfd)->e_flags & EF_SPARC_32PLUS))
+ x = INSN_BPA | (reloc & 0x7ffff); /* ba,pt %xcc */
+ else
+ x = INSN_BA | (reloc & 0x3fffff); /* ba */
+ bfd_put_32 (input_bfd, x, contents + rel->r_offset);
+ r = bfd_reloc_ok;
+ if (rel->r_offset >= 4
+ && (y & (0xffffffff ^ RS1(~0)))
+ == (INSN_OR | RD(O7) | RS2(G0)))
+ {
+ bfd_vma z;
+ unsigned int reg;
+
+ z = bfd_get_32 (input_bfd,
+ contents + rel->r_offset - 4);
+ if ((z & (0xffffffff ^ RD(~0)))
+ != (INSN_OR | RS1(O7) | RS2(G0)))
+ break;
+
+ /* The sequence was
+ or %o7, %g0, %rN
+ call foo
+ or %rN, %g0, %o7
+
+ If call foo was replaced with ba, replace
+ or %rN, %g0, %o7 with nop. */
+
+ reg = (y & RS1(~0)) >> 14;
+ if (reg != ((z & RD(~0)) >> 25)
+ || reg == G0 || reg == O7)
+ break;
+
+ bfd_put_32 (input_bfd, INSN_NOP,
+ contents + rel->r_offset + 4);
+ }
+
+ }
+ }
+ }
+ }
+
+ if (r == bfd_reloc_continue)
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
@@ -1967,6 +2078,7 @@ elf32_sparc_final_write_processing (abfd, linker)
#define ELF_MAXPAGESIZE 0x10000
#define bfd_elf32_bfd_reloc_type_lookup elf32_sparc_reloc_type_lookup
+#define bfd_elf32_bfd_relax_section elf32_sparc_relax_section
#define elf_info_to_howto elf32_sparc_info_to_howto
#define elf_backend_create_dynamic_sections \
_bfd_elf_create_dynamic_sections