mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-27 01:28:46 +00:00
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:
182
bfd/elf32-avr.c
182
bfd/elf32-avr.c
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user