Fix alpha-elf relaxation

ld/
	* emultempl/alphaelf.em (alpha_after_parse): Enable 2 relax passes.
bfd/
	* elf64-alpha.c (elf64_alpha_size_got_sections): New may_merge
	parameter; honor it and disable got merging when false.
	(elf64_alpha_relax_got_load): Do not relax to GPREL relocs during
	the first pass of relaxation.
	(elf64_alpha_relax_with_lituse): Likewise.  Move relaxed relocs to
	the end of the LITERAL+LITUSE chain.
	(elf64_alpha_relax_section): Only process LITERAL relocs during the
	second pass of relaxation.
This commit is contained in:
Richard Henderson 2014-04-21 08:14:18 -07:00
parent cc75d373fd
commit d1c109de72
4 changed files with 156 additions and 93 deletions

View File

@ -1,5 +1,14 @@
2014-04-21 Richard Henderson <rth@redhat.com>
* elf64-alpha.c (elf64_alpha_size_got_sections): New may_merge
parameter; honor it and disable got merging when false.
(elf64_alpha_relax_got_load): Do not relax to GPREL relocs during
the first pass of relaxation.
(elf64_alpha_relax_with_lituse): Likewise. Move relaxed relocs to
the end of the LITERAL+LITUSE chain.
(elf64_alpha_relax_section): Only process LITERAL relocs during the
second pass of relaxation.
* configure.ac (use_secureplt): Enable by default.
* configure: Rebuild.

View File

@ -2466,7 +2466,8 @@ elf64_alpha_calc_got_offsets (struct bfd_link_info *info)
/* Constructs the gots. */
static bfd_boolean
elf64_alpha_size_got_sections (struct bfd_link_info *info)
elf64_alpha_size_got_sections (struct bfd_link_info *info,
bfd_boolean may_merge)
{
bfd *i, *got_list, *cur_got_obj = NULL;
struct alpha_elf_link_hash_table * htab;
@ -2521,21 +2522,24 @@ elf64_alpha_size_got_sections (struct bfd_link_info *info)
if (cur_got_obj == NULL)
return FALSE;
i = alpha_elf_tdata(cur_got_obj)->got_link_next;
while (i != NULL)
if (may_merge)
{
if (elf64_alpha_can_merge_gots (cur_got_obj, i))
i = alpha_elf_tdata(cur_got_obj)->got_link_next;
while (i != NULL)
{
elf64_alpha_merge_gots (cur_got_obj, i);
if (elf64_alpha_can_merge_gots (cur_got_obj, i))
{
elf64_alpha_merge_gots (cur_got_obj, i);
alpha_elf_tdata(i)->got->size = 0;
i = alpha_elf_tdata(i)->got_link_next;
alpha_elf_tdata(cur_got_obj)->got_link_next = i;
}
else
{
cur_got_obj = i;
i = alpha_elf_tdata(i)->got_link_next;
alpha_elf_tdata(i)->got->size = 0;
i = alpha_elf_tdata(i)->got_link_next;
alpha_elf_tdata(cur_got_obj)->got_link_next = i;
}
else
{
cur_got_obj = i;
i = alpha_elf_tdata(i)->got_link_next;
}
}
}
@ -2638,7 +2642,7 @@ elf64_alpha_always_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
if (htab == NULL)
return FALSE;
if (!elf64_alpha_size_got_sections (info))
if (!elf64_alpha_size_got_sections (info, TRUE))
return FALSE;
/* Allocate space for all of the .got subsections. */
@ -3074,6 +3078,10 @@ elf64_alpha_relax_got_load (struct alpha_relax_info *info, bfd_vma symval,
}
else
{
/* We may only create GPREL relocs during the second pass. */
if (info->link_info->relax_pass == 0)
return TRUE;
disp = symval - info->gp;
insn = (OP_LDA << 26) | (insn & 0x03ff0000);
r_type = R_ALPHA_GPREL16;
@ -3214,21 +3222,27 @@ static bfd_boolean
elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
bfd_vma symval, Elf_Internal_Rela *irel)
{
Elf_Internal_Rela *urel, *irelend = info->relend;
int flags, count, i;
Elf_Internal_Rela *urel, *erel, *irelend = info->relend;
int flags;
bfd_signed_vma disp;
bfd_boolean fits16;
bfd_boolean fits32;
bfd_boolean lit_reused = FALSE;
bfd_boolean all_optimized = TRUE;
bfd_boolean changed_contents;
bfd_boolean changed_relocs;
bfd_byte *contents = info->contents;
bfd *abfd = info->abfd;
bfd_vma sec_output_vma;
unsigned int lit_insn;
int relax_pass;
lit_insn = bfd_get_32 (info->abfd, info->contents + irel->r_offset);
lit_insn = bfd_get_32 (abfd, contents + irel->r_offset);
if (lit_insn >> 26 != OP_LDQ)
{
((*_bfd_error_handler)
("%B: %A+0x%lx: warning: LITERAL relocation against unexpected insn",
info->abfd, info->sec,
abfd, info->sec,
(unsigned long) irel->r_offset));
return TRUE;
}
@ -3237,25 +3251,32 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
return TRUE;
changed_contents = info->changed_contents;
changed_relocs = info->changed_relocs;
sec_output_vma = info->sec->output_section->vma + info->sec->output_offset;
relax_pass = info->link_info->relax_pass;
/* Summarize how this particular LITERAL is used. */
for (urel = irel+1, flags = count = 0; urel < irelend; ++urel, ++count)
for (erel = irel+1, flags = 0; erel < irelend; ++erel)
{
if (ELF64_R_TYPE (urel->r_info) != R_ALPHA_LITUSE)
if (ELF64_R_TYPE (erel->r_info) != R_ALPHA_LITUSE)
break;
if (urel->r_addend <= 6)
flags |= 1 << urel->r_addend;
if (erel->r_addend <= 6)
flags |= 1 << erel->r_addend;
}
/* A little preparation for the loop... */
disp = symval - info->gp;
for (urel = irel+1, i = 0; i < count; ++i, ++urel)
for (urel = irel+1; urel < erel; ++urel)
{
bfd_vma urel_r_offset = urel->r_offset;
unsigned int insn;
int insn_disp;
bfd_signed_vma xdisp;
Elf_Internal_Rela nrel;
insn = bfd_get_32 (info->abfd, info->contents + urel->r_offset);
insn = bfd_get_32 (abfd, contents + urel_r_offset);
switch (urel->r_addend)
{
@ -3267,6 +3288,13 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
break;
case LITUSE_ALPHA_BASE:
/* We may only create GPREL relocs during the second pass. */
if (relax_pass == 0)
{
all_optimized = FALSE;
break;
}
/* We can always optimize 16-bit displacements. */
/* Extract the displacement from the instruction, sign-extending
@ -3284,14 +3312,20 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
/* Take the op code and dest from this insn, take the base
register from the literal insn. Leave the offset alone. */
insn = (insn & 0xffe0ffff) | (lit_insn & 0x001f0000);
urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_GPREL16);
urel->r_addend = irel->r_addend;
info->changed_relocs = TRUE;
bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
changed_contents = TRUE;
bfd_put_32 (info->abfd, (bfd_vma) insn,
info->contents + urel->r_offset);
info->changed_contents = TRUE;
nrel = *urel;
nrel.r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_GPREL16);
nrel.r_addend = irel->r_addend;
/* As we adjust, move the reloc to the end so that we don't
break the LITERAL+LITUSE chain. */
if (urel < --erel)
*urel-- = *erel;
*erel = nrel;
changed_relocs = TRUE;
}
/* If all mem+byte, we can optimize 32-bit mem displacements. */
@ -3302,15 +3336,16 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_GPRELHIGH);
lit_insn = (OP_LDAH << 26) | (lit_insn & 0x03ff0000);
bfd_put_32 (info->abfd, (bfd_vma) lit_insn,
info->contents + irel->r_offset);
bfd_put_32 (abfd, (bfd_vma) lit_insn, contents + irel->r_offset);
lit_reused = TRUE;
info->changed_contents = TRUE;
changed_contents = TRUE;
/* Since all relocs must be optimized, don't bother swapping
this relocation to the end. */
urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_GPRELLOW);
urel->r_addend = irel->r_addend;
info->changed_relocs = TRUE;
changed_relocs = TRUE;
}
else
all_optimized = FALSE;
@ -3324,14 +3359,19 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
insn &= ~ (unsigned) 0x001ff000;
insn |= ((symval & 7) << 13) | 0x1000;
bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
changed_contents = TRUE;
urel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
urel->r_addend = 0;
info->changed_relocs = TRUE;
nrel = *urel;
nrel.r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
nrel.r_addend = 0;
bfd_put_32 (info->abfd, (bfd_vma) insn,
info->contents + urel->r_offset);
info->changed_contents = TRUE;
/* As we adjust, move the reloc to the end so that we don't
break the LITERAL+LITUSE chain. */
if (urel < --erel)
*urel-- = *erel;
*erel = nrel;
changed_relocs = TRUE;
break;
case LITUSE_ALPHA_JSR:
@ -3348,18 +3388,15 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
if (info->h && info->h->root.root.type == bfd_link_hash_undefweak)
{
insn |= 31 << 16;
bfd_put_32 (info->abfd, (bfd_vma) insn,
info->contents + urel->r_offset);
bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
info->changed_contents = TRUE;
changed_contents = TRUE;
break;
}
/* If not zero, place to jump without needing pv. */
optdest = elf64_alpha_relax_opt_call (info, symval);
org = (info->sec->output_section->vma
+ info->sec->output_offset
+ urel->r_offset + 4);
org = sec_output_vma + urel_r_offset + 4;
odisp = (optdest ? optdest : symval) - org;
if (odisp >= -0x400000 && odisp < 0x400000)
@ -3371,27 +3408,32 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
insn = (OP_BSR << 26) | (insn & 0x03e00000);
else
insn = (OP_BR << 26) | (insn & 0x03e00000);
bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
changed_contents = TRUE;
urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_BRADDR);
urel->r_addend = irel->r_addend;
nrel = *urel;
nrel.r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
R_ALPHA_BRADDR);
nrel.r_addend = irel->r_addend;
if (optdest)
urel->r_addend += optdest - symval;
nrel.r_addend += optdest - symval;
else
all_optimized = FALSE;
bfd_put_32 (info->abfd, (bfd_vma) insn,
info->contents + urel->r_offset);
/* Kill any HINT reloc that might exist for this insn. */
xrel = (elf64_alpha_find_reloc_at_ofs
(info->relocs, info->relend, urel->r_offset,
(info->relocs, info->relend, urel_r_offset,
R_ALPHA_HINT));
if (xrel)
xrel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
info->changed_contents = TRUE;
/* As we adjust, move the reloc to the end so that we don't
break the LITERAL+LITUSE chain. */
if (urel < --erel)
*urel-- = *erel;
*erel = nrel;
info->changed_relocs = TRUE;
}
else
@ -3403,14 +3445,14 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
{
Elf_Internal_Rela *gpdisp
= (elf64_alpha_find_reloc_at_ofs
(info->relocs, irelend, urel->r_offset + 4,
(info->relocs, irelend, urel_r_offset + 4,
R_ALPHA_GPDISP));
if (gpdisp)
{
bfd_byte *p_ldah = info->contents + gpdisp->r_offset;
bfd_byte *p_ldah = contents + gpdisp->r_offset;
bfd_byte *p_lda = p_ldah + gpdisp->r_addend;
unsigned int ldah = bfd_get_32 (info->abfd, p_ldah);
unsigned int lda = bfd_get_32 (info->abfd, p_lda);
unsigned int ldah = bfd_get_32 (abfd, p_ldah);
unsigned int lda = bfd_get_32 (abfd, p_lda);
/* Verify that the instruction is "ldah $29,0($26)".
Consider a function that ends in a noreturn call,
@ -3419,12 +3461,12 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
In that case the insn would use $27 as the base. */
if (ldah == 0x27ba0000 && lda == 0x23bd0000)
{
bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, p_ldah);
bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, p_lda);
bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, p_ldah);
bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, p_lda);
gpdisp->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
info->changed_contents = TRUE;
info->changed_relocs = TRUE;
changed_contents = TRUE;
changed_relocs = TRUE;
}
}
}
@ -3433,6 +3475,9 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
}
}
/* If we reused the literal instruction, we must have optimized all. */
BFD_ASSERT(!lit_reused || all_optimized);
/* If all cases were optimized, we can reduce the use count on this
got entry by one, possibly eliminating it. */
if (all_optimized)
@ -3452,17 +3497,19 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
if (!lit_reused)
{
irel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
info->changed_relocs = TRUE;
changed_relocs = TRUE;
bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP,
info->contents + irel->r_offset);
info->changed_contents = TRUE;
bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, contents + irel->r_offset);
changed_contents = TRUE;
}
return TRUE;
}
else
return elf64_alpha_relax_got_load (info, symval, irel, R_ALPHA_LITERAL);
info->changed_contents = changed_contents;
info->changed_relocs = changed_relocs;
if (all_optimized || relax_pass == 0)
return TRUE;
return elf64_alpha_relax_got_load (info, symval, irel, R_ALPHA_LITERAL);
}
static bfd_boolean
@ -3724,6 +3771,7 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
struct alpha_elf_got_entry **local_got_entries;
struct alpha_relax_info info;
struct alpha_elf_link_hash_table * htab;
int relax_pass;
htab = alpha_elf_hash_table (link_info);
if (htab == NULL)
@ -3739,15 +3787,19 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
return TRUE;
BFD_ASSERT (is_alpha_elf (abfd));
relax_pass = link_info->relax_pass;
/* Make sure our GOT and PLT tables are up-to-date. */
if (htab->relax_trip != link_info->relax_trip)
{
htab->relax_trip = link_info->relax_trip;
/* This should never fail after the initial round, since the only
error is GOT overflow, and relaxation only shrinks the table. */
if (!elf64_alpha_size_got_sections (link_info))
/* This should never fail after the initial round, since the only error
is GOT overflow, and relaxation only shrinks the table. However, we
may only merge got sections during the first pass. If we merge
sections after we've created GPREL relocs, the GP for the merged
section backs up which may put the relocs out of range. */
if (!elf64_alpha_size_got_sections (link_info, relax_pass == 0))
abort ();
if (elf_hash_table (link_info)->dynamic_sections_created)
{
@ -3802,24 +3854,21 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
unsigned long r_symndx = ELF64_R_SYM (irel->r_info);
/* Early exit for unhandled or unrelaxable relocations. */
switch (r_type)
{
case R_ALPHA_LITERAL:
case R_ALPHA_GPRELHIGH:
case R_ALPHA_GPRELLOW:
case R_ALPHA_GOTDTPREL:
case R_ALPHA_GOTTPREL:
case R_ALPHA_TLSGD:
break;
case R_ALPHA_TLSLDM:
/* The symbol for a TLSLDM reloc is ignored. Collapse the
reloc to the STN_UNDEF (0) symbol so that they all match. */
r_symndx = STN_UNDEF;
break;
default:
continue;
if (r_type != R_ALPHA_LITERAL)
{
/* We complete everything except LITERAL in the first pass. */
if (relax_pass != 0)
continue;
if (r_type == R_ALPHA_TLSLDM)
{
/* The symbol for a TLSLDM reloc is ignored. Collapse the
reloc to the STN_UNDEF (0) symbol so that they all match. */
r_symndx = STN_UNDEF;
}
else if (r_type != R_ALPHA_GOTDTPREL
&& r_type != R_ALPHA_GOTTPREL
&& r_type != R_ALPHA_TLSGD)
continue;
}
/* Get the value of the symbol referred to by the reloc. */

View File

@ -1,3 +1,7 @@
2014-04-21 Richard Henderson <rth@redhat.com>
* emultempl/alphaelf.em (alpha_after_parse): Enable 2 relax passes.
2014-04-16 Steve Ellcey <sellcey@mips.com>
* emultempl/elf32.em: Include safe-ctype.h.

View File

@ -72,6 +72,7 @@ alpha_after_open (void)
static void
alpha_after_parse (void)
{
link_info.relax_pass = 2;
if (limit_32bit && !link_info.shared && !link_info.relocatable)
lang_section_start (".interp",
exp_binop ('+',