bfd/ChangeLog

* elf32-avr.c: Add DIFF relocations for AVR.
	(avr_final_link_relocate): Handle the DIFF relocs.
	(bfd_elf_avr_diff_reloc): New.
	(elf32_avr_is_diff_reloc): New.
	(elf32_avr_adjust_diff_reloc_value): Reduce difference value.
	(elf32_avr_relax_delete_bytes): Recompute difference after deleting
	bytes.

	* reloc.c: Add BFD_RELOC_AVR_DIFF8/16/32 relocations

gas/ChangeLog

	* config/tc-avr.c: Add new flag mlink-relax.
	(md_show_usage): Add flag and help text.
	(md_parse_option): Record whether link relax is turned on.
	(relaxable_section): New.
	(avr_validate_fix_sub): New.
	(avr_force_relocation): New.
	(md_apply_fix): Generate DIFF reloc.
	(avr_allow_local_subtract): New.

	* config/tc-avr.h (TC_LINKRELAX_FIXUP): Define to 0.
	(TC_FORCE_RELOCATION): Define.
	(TC_FORCE_RELOCATION_SUB_SAME): Define.
	(TC_VALIDATE_FIX_SUB): Define.
	(avr_force_relocation): Declare.
	(avr_validate_fix_sub): Declare.
	(md_allow_local_subtract): Define.
	(avr_allow_local_subtract): Declare.

gas/testsuite/ChangeLog

	* gas/avr/diffreloc_withrelax.d: New testcase.
	* gas/avr/noreloc_withoutrelax.d: Likewise.
	* gas/avr/relax.s: Likewise.

include/ChangeLog

	* elf/avr.h: Add new DIFF relocs.

ld/testsuite/ChangeLog

	* ld-avr/norelax_diff.d: New testcase.
	* ld-avr/relax_diff.d: Likewise.
	* ld-avr/relax.s: Likewise.
This commit is contained in:
Denis Chertykov
2014-04-10 19:50:33 +04:00
parent 9d497a19ea
commit e4ef1b6c3f
17 changed files with 503 additions and 4 deletions

View File

@@ -32,6 +32,15 @@ static bfd_boolean debug_relax = FALSE;
/* Enable debugging printout at stdout with this variable. */
static bfd_boolean debug_stubs = FALSE;
static bfd_reloc_status_type
bfd_elf_avr_diff_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void *data,
asection *input_section,
bfd *output_bfd,
char **error_message);
/* Hash table initialization and handling. Code is taken from the hppa port
and adapted to the needs of AVR. */
@@ -557,6 +566,45 @@ static reloc_howto_type elf_avr_howto_table[] =
0xffffff, /* src_mask */
0xffffff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_AVR_DIFF8, /* type */
0, /* rightshift */
0, /* size (0 = byte, 1 = short, 2 = long) */
8, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_avr_diff_reloc, /* special_function */
"R_AVR_DIFF8", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_AVR_DIFF16, /* type */
0, /* rightshift */
1, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_avr_diff_reloc, /* special_function */
"R_AVR_DIFF16", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_AVR_DIFF32, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */
bfd_elf_avr_diff_reloc, /* special_function */
"R_AVR_DIFF32", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffffffff, /* dst_mask */
FALSE) /* pcrel_offset */
};
/* Map BFD reloc types to AVR ELF reloc types. */
@@ -598,7 +646,10 @@ static const struct avr_reloc_map avr_reloc_map[] =
{ BFD_RELOC_8, R_AVR_8 },
{ BFD_RELOC_AVR_8_LO, R_AVR_8_LO8 },
{ BFD_RELOC_AVR_8_HI, R_AVR_8_HI8 },
{ BFD_RELOC_AVR_8_HLO, R_AVR_8_HLO8 }
{ BFD_RELOC_AVR_8_HLO, R_AVR_8_HLO8 },
{ BFD_RELOC_AVR_DIFF8, R_AVR_DIFF8 },
{ BFD_RELOC_AVR_DIFF16, R_AVR_DIFF16 },
{ BFD_RELOC_AVR_DIFF32, R_AVR_DIFF32 }
};
/* Meant to be filled one day with the wrap around address for the
@@ -797,6 +848,22 @@ avr_get_stub_addr (bfd_vma srel,
return 0x020000;
}
/* Perform a diff relocation. Nothing to do, as the difference value is already
written into the section's contents. */
static bfd_reloc_status_type
bfd_elf_avr_diff_reloc (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry ATTRIBUTE_UNUSED,
asymbol *symbol ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED,
asection *input_section ATTRIBUTE_UNUSED,
bfd *output_bfd ATTRIBUTE_UNUSED,
char **error_message ATTRIBUTE_UNUSED)
{
return bfd_reloc_ok;
}
/* Perform a single relocation. By default we use the standard BFD
routines, but a few relocs, we have to do them ourselves. */
@@ -1149,6 +1216,13 @@ avr_final_link_relocate (reloc_howto_type * howto,
bfd_put_16 (input_bfd, (bfd_vma) srel &0x00ffff, contents);
break;
case R_AVR_DIFF8:
case R_AVR_DIFF16:
case R_AVR_DIFF32:
/* Nothing to do here, as contents already contains the diff value. */
r = bfd_reloc_ok;
break;
default:
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
@@ -1457,6 +1531,104 @@ elf32_avr_object_p (bfd *abfd)
e_set);
}
/* Returns whether the relocation type passed is a diff reloc. */
static bfd_boolean
elf32_avr_is_diff_reloc (Elf_Internal_Rela *irel)
{
return (ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF8
||ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF16
|| ELF32_R_TYPE (irel->r_info) == R_AVR_DIFF32);
}
/* Reduce the diff value written in the section by count if the shrinked
insn address happens to fall between the two symbols for which this
diff reloc was emitted. */
static void
elf32_avr_adjust_diff_reloc_value (bfd *abfd,
struct bfd_section *isec,
Elf_Internal_Rela *irel,
bfd_vma symval,
bfd_vma shrinked_insn_address,
int count)
{
unsigned char *reloc_contents = NULL;
unsigned char *isec_contents = elf_section_data (isec)->this_hdr.contents;
if (isec_contents == NULL)
{
if (! bfd_malloc_and_get_section (abfd, isec, &isec_contents))
return;
elf_section_data (isec)->this_hdr.contents = isec_contents;
}
reloc_contents = isec_contents + irel->r_offset;
/* Read value written in object file. */
bfd_vma x = 0;
switch (ELF32_R_TYPE (irel->r_info))
{
case R_AVR_DIFF8:
{
x = *reloc_contents;
break;
}
case R_AVR_DIFF16:
{
x = bfd_get_16 (abfd, reloc_contents);
break;
}
case R_AVR_DIFF32:
{
x = bfd_get_32 (abfd, reloc_contents);
break;
}
default:
{
BFD_FAIL();
}
}
/* For a diff reloc sym1 - sym2 the diff at assembly time (x) is written
into the object file at the reloc offset. sym2's logical value is
symval (<start_of_section>) + reloc addend. Compute the start and end
addresses and check if the shrinked insn falls between sym1 and sym2. */
bfd_vma end_address = symval + irel->r_addend;
bfd_vma start_address = end_address - x;
/* Reduce the diff value by count bytes and write it back into section
contents. */
if (shrinked_insn_address >= start_address &&
shrinked_insn_address <= end_address)
{
switch (ELF32_R_TYPE (irel->r_info))
{
case R_AVR_DIFF8:
{
*reloc_contents = (x - count);
break;
}
case R_AVR_DIFF16:
{
bfd_put_16 (abfd, (x - count) & 0xFFFF, reloc_contents);
break;
}
case R_AVR_DIFF32:
{
bfd_put_32 (abfd, (x - count) & 0xFFFFFFFF, reloc_contents);
break;
}
default:
{
BFD_FAIL();
}
}
}
}
/* Delete some bytes from a section while changing the size of an instruction.
The parameter "addr" denotes the section-relative offset pointing just
@@ -1595,6 +1767,14 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
if (symval <= shrinked_insn_address
&& (symval + irel->r_addend) > shrinked_insn_address)
{
if (elf32_avr_is_diff_reloc (irel))
{
elf32_avr_adjust_diff_reloc_value (abfd, isec, irel,
symval,
shrinked_insn_address,
count);
}
irel->r_addend -= count;
if (debug_relax)