Index: include/elf/avr.h =================================================================== RCS file: /cvs/src/src/include/elf/avr.h,v retrieving revision 1.6 diff -U20 -r1.6 avr.h --- include/elf/avr.h 10 May 2005 10:21:10 -0000 1.6 +++ include/elf/avr.h 15 Oct 2005 14:08:39 -0000 @@ -9,53 +9,59 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ELF_AVR_H #define _ELF_AVR_H #include "elf/reloc-macros.h" /* Processor specific flags for the ELF header e_flags field. */ #define EF_AVR_MACH 0xf +/* If bit #7 is set, it is assumed that the elf file uses local symbols + as reference for the relocations so that linker relaxation is possible. */ +#define EF_AVR_LINKRELAX_PREPARED 0x80 + #define E_AVR_MACH_AVR1 1 #define E_AVR_MACH_AVR2 2 #define E_AVR_MACH_AVR3 3 #define E_AVR_MACH_AVR4 4 #define E_AVR_MACH_AVR5 5 /* Relocations. */ START_RELOC_NUMBERS (elf_avr_reloc_type) RELOC_NUMBER (R_AVR_NONE, 0) RELOC_NUMBER (R_AVR_32, 1) RELOC_NUMBER (R_AVR_7_PCREL, 2) RELOC_NUMBER (R_AVR_13_PCREL, 3) RELOC_NUMBER (R_AVR_16, 4) RELOC_NUMBER (R_AVR_16_PM, 5) RELOC_NUMBER (R_AVR_LO8_LDI, 6) RELOC_NUMBER (R_AVR_HI8_LDI, 7) RELOC_NUMBER (R_AVR_HH8_LDI, 8) RELOC_NUMBER (R_AVR_LO8_LDI_NEG, 9) RELOC_NUMBER (R_AVR_HI8_LDI_NEG, 10) RELOC_NUMBER (R_AVR_HH8_LDI_NEG, 11) RELOC_NUMBER (R_AVR_LO8_LDI_PM, 12) RELOC_NUMBER (R_AVR_HI8_LDI_PM, 13) RELOC_NUMBER (R_AVR_HH8_LDI_PM, 14) RELOC_NUMBER (R_AVR_LO8_LDI_PM_NEG, 15) RELOC_NUMBER (R_AVR_HI8_LDI_PM_NEG, 16) RELOC_NUMBER (R_AVR_HH8_LDI_PM_NEG, 17) RELOC_NUMBER (R_AVR_CALL, 18) RELOC_NUMBER (R_AVR_LDI, 19) RELOC_NUMBER (R_AVR_6, 20) RELOC_NUMBER (R_AVR_6_ADIW, 21) + RELOC_NUMBER (R_AVR_MS8_LDI, 22) + RELOC_NUMBER (R_AVR_MS8_LDI_NEG, 23) END_RELOC_NUMBERS (R_AVR_max) #endif /* _ELF_AVR_H */ Index: bfd/elf32-avr.c =================================================================== RCS file: /cvs/src/src/bfd/elf32-avr.c,v retrieving revision 1.22 diff -U20 -r1.22 elf32-avr.c --- bfd/elf32-avr.c 20 Jun 2005 18:12:06 -0000 1.22 +++ bfd/elf32-avr.c 15 Oct 2005 14:08:42 -0000 @@ -1,69 +1,79 @@ /* AVR-specific support for 32-bit ELF Copyright 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Denis Chertykov This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + Foundation, Inc., 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ #include "bfd.h" #include "sysdep.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf/avr.h" static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); static void avr_info_to_howto_rela PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); static asection *elf32_avr_gc_mark_hook PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, struct elf_link_hash_entry *, Elf_Internal_Sym *)); static bfd_boolean elf32_avr_gc_sweep_hook PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *)); static bfd_boolean elf32_avr_check_relocs PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *)); static bfd_reloc_status_type avr_final_link_relocate PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, bfd_vma)); static bfd_boolean elf32_avr_relocate_section PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); static void bfd_elf_avr_final_write_processing PARAMS ((bfd *, bfd_boolean)); static bfd_boolean elf32_avr_object_p PARAMS ((bfd *)); +/* Relaxing stuff */ +static bfd_boolean elf32_avr_relax_section + PARAMS((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); +static bfd_boolean elf32_avr_relax_delete_bytes + PARAMS((bfd *, asection *, bfd_vma, int)); +static bfd_byte *elf32_avr_get_relocated_section_contents + PARAMS((bfd *, struct bfd_link_info *, struct bfd_link_order *, + bfd_byte *, bfd_boolean, asymbol **)); + static reloc_howto_type elf_avr_howto_table[] = { HOWTO (R_AVR_NONE, /* 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_generic_reloc, /* special_function */ "R_AVR_NONE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_AVR_32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ @@ -150,41 +160,42 @@ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A high 8 bit absolute relocation of 16 bit address. For LDI command. */ HOWTO (R_AVR_HI8_LDI, /* type */ 8, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AVR_HI8_LDI", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A high 6 bit absolute relocation of 22 bit address. - For LDI command. */ + For LDI command. As well second most significant 8 bit value of + a 32 bit link-time constant. */ HOWTO (R_AVR_HH8_LDI, /* type */ 16, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AVR_HH8_LDI", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A negative low 8 bit absolute relocation of 16 bit address. For LDI command. */ HOWTO (R_AVR_LO8_LDI_NEG, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ @@ -194,41 +205,41 @@ "R_AVR_LO8_LDI_NEG", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A hegative high 8 bit absolute relocation of 16 bit address. For LDI command. */ HOWTO (R_AVR_HI8_LDI_NEG, /* type */ 8, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AVR_HI8_LDI_NEG", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A hegative high 6 bit absolute relocation of 22 bit address. + /* A negative high 6 bit absolute relocation of 22 bit address. For LDI command. */ HOWTO (R_AVR_HH8_LDI_NEG, /* type */ 16, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AVR_HH8_LDI_NEG", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A low 8 bit absolute relocation of 24 bit program memory address. For LDI command. */ HOWTO (R_AVR_LO8_LDI_PM, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ @@ -357,77 +368,130 @@ bfd_elf_generic_reloc, /* special_function */ "R_AVR_6", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A 6 bit absolute relocation of 6 bit offset. For sbiw/adiw command. */ HOWTO (R_AVR_6_ADIW, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 6, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AVR_6_ADIW", /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ - FALSE) /* pcrel_offset */ + FALSE), /* pcrel_offset */ + /* Most significant 8 bit value of a 32 bit link-time constant. */ + HOWTO (R_AVR_MS8_LDI, /* type */ + 24, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_AVR_MS8_LDI", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Negative most significant 8 bit value of a 32 bit link-time constant. */ + HOWTO (R_AVR_MS8_LDI_NEG, /* type */ + 24, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_AVR_MS8_LDI_NEG", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE) /* pcrel_offset */ }; /* Map BFD reloc types to AVR ELF reloc types. */ struct avr_reloc_map { bfd_reloc_code_real_type bfd_reloc_val; unsigned int elf_reloc_val; }; static const struct avr_reloc_map avr_reloc_map[] = { { BFD_RELOC_NONE, R_AVR_NONE }, { BFD_RELOC_32, R_AVR_32 }, { BFD_RELOC_AVR_7_PCREL, R_AVR_7_PCREL }, { BFD_RELOC_AVR_13_PCREL, R_AVR_13_PCREL }, { BFD_RELOC_16, R_AVR_16 }, { BFD_RELOC_AVR_16_PM, R_AVR_16_PM }, { BFD_RELOC_AVR_LO8_LDI, R_AVR_LO8_LDI}, { BFD_RELOC_AVR_HI8_LDI, R_AVR_HI8_LDI }, { BFD_RELOC_AVR_HH8_LDI, R_AVR_HH8_LDI }, + { BFD_RELOC_AVR_MS8_LDI, R_AVR_MS8_LDI }, { BFD_RELOC_AVR_LO8_LDI_NEG, R_AVR_LO8_LDI_NEG }, { BFD_RELOC_AVR_HI8_LDI_NEG, R_AVR_HI8_LDI_NEG }, { BFD_RELOC_AVR_HH8_LDI_NEG, R_AVR_HH8_LDI_NEG }, + { BFD_RELOC_AVR_MS8_LDI_NEG, R_AVR_MS8_LDI_NEG }, { BFD_RELOC_AVR_LO8_LDI_PM, R_AVR_LO8_LDI_PM }, { BFD_RELOC_AVR_HI8_LDI_PM, R_AVR_HI8_LDI_PM }, { BFD_RELOC_AVR_HH8_LDI_PM, R_AVR_HH8_LDI_PM }, { BFD_RELOC_AVR_LO8_LDI_PM_NEG, R_AVR_LO8_LDI_PM_NEG }, { BFD_RELOC_AVR_HI8_LDI_PM_NEG, R_AVR_HI8_LDI_PM_NEG }, { BFD_RELOC_AVR_HH8_LDI_PM_NEG, R_AVR_HH8_LDI_PM_NEG }, { BFD_RELOC_AVR_CALL, R_AVR_CALL }, { BFD_RELOC_AVR_LDI, R_AVR_LDI }, { BFD_RELOC_AVR_6, R_AVR_6 }, { BFD_RELOC_AVR_6_ADIW, R_AVR_6_ADIW } }; +/* Meant to be filled one day with the wrap around address for the + specific device. I.e. it is meant to be filled one day with + 0x4000 for 16k devices, 0x8000 for 32k devices and so on. + We initialize it here with a value of 0x1000000 resulting in the + fact that we will never detect a wrap-around. The logic of the + source code later on assumes that in avr_pc_wrap_around one single + bit is set. */ +unsigned int avr_pc_wrap_around = 0x1000000; + +/* Calculates the effective distance of a pc relative jump/call. */ +static int +avr_relative_distance_considering_wrap_around (unsigned int distance) +{ + unsigned int wrap_around_mask = avr_pc_wrap_around - 1; + + int dist_with_wrap_around = distance & wrap_around_mask; + + if (dist_with_wrap_around > ((int) (avr_pc_wrap_around >> 1)) ) + dist_with_wrap_around -= avr_pc_wrap_around; + + return dist_with_wrap_around; +} + static reloc_howto_type * bfd_elf32_bfd_reloc_type_lookup (abfd, code) bfd *abfd ATTRIBUTE_UNUSED; bfd_reloc_code_real_type code; { unsigned int i; for (i = 0; i < sizeof (avr_reloc_map) / sizeof (struct avr_reloc_map); i++) { if (avr_reloc_map[i].bfd_reloc_val == code) return &elf_avr_howto_table[avr_reloc_map[i].elf_reloc_val]; } return NULL; } /* Set the howto pointer for an AVR ELF reloc. */ @@ -566,77 +630,83 @@ return bfd_reloc_outofrange; if (srel > ((1 << 7) - 1) || (srel < - (1 << 7))) return bfd_reloc_overflow; x = bfd_get_16 (input_bfd, contents); x = (x & 0xfc07) | (((srel >> 1) << 3) & 0x3f8); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_13_PCREL: contents += rel->r_offset; srel = (bfd_signed_vma) relocation; srel += rel->r_addend; srel -= rel->r_offset; srel -= 2; /* Branch instructions add 2 to the PC... */ srel -= (input_section->output_section->vma + input_section->output_offset); if (srel & 1) return bfd_reloc_outofrange; + srel = avr_relative_distance_considering_wrap_around (srel); + /* AVR addresses commands as words. */ srel >>= 1; /* Check for overflow. */ if (srel < -2048 || srel > 2047) { + printf ("Relative distance too large: %i \n", (int) srel); + /* Apply WRAPAROUND if possible. */ switch (bfd_get_mach (input_bfd)) { case bfd_mach_avr2: case bfd_mach_avr4: break; default: return bfd_reloc_overflow; } } x = bfd_get_16 (input_bfd, contents); x = (x & 0xf000) | (srel & 0xfff); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_LO8_LDI: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_LDI: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; - if ((srel & 0xffff) > 255) + if ( ((srel > 0) && (srel & 0xffff) > 255) + || ((srel < 0) && ( (-srel) & 0xffff) > 128)) /* Remove offset for data/eeprom section. */ return bfd_reloc_overflow; + x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_6: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; if (((srel & 0xffff) > 63) || (srel < 0)) /* Remove offset for data/eeprom section. */ return bfd_reloc_overflow; x = bfd_get_16 (input_bfd, contents); x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) | ((srel & (1 << 5)) << 8)); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_6_ADIW: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; if (((srel & 0xffff) > 63) || (srel < 0)) @@ -648,69 +718,88 @@ break; case R_AVR_HI8_LDI: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; srel = (srel >> 8) & 0xff; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_HH8_LDI: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; srel = (srel >> 16) & 0xff; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; + case R_AVR_MS8_LDI: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation + rel->r_addend; + srel = (srel >> 24) & 0xff; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); + bfd_put_16 (input_bfd, x, contents); + break; + case R_AVR_LO8_LDI_NEG: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; srel = -srel; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_HI8_LDI_NEG: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; srel = -srel; srel = (srel >> 8) & 0xff; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_HH8_LDI_NEG: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; srel = -srel; srel = (srel >> 16) & 0xff; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; + case R_AVR_MS8_LDI_NEG: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation + rel->r_addend; + srel = -srel; + srel = (srel >> 24) & 0xff; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); + bfd_put_16 (input_bfd, x, contents); + break; + case R_AVR_LO8_LDI_PM: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; if (srel & 1) return bfd_reloc_outofrange; srel = srel >> 1; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); bfd_put_16 (input_bfd, x, contents); break; case R_AVR_HI8_LDI_PM: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; if (srel & 1) return bfd_reloc_outofrange; srel = srel >> 1; srel = (srel >> 8) & 0xff; x = bfd_get_16 (input_bfd, contents); x = (x & 0xf0f0) | (srel & 0xf) | ((srel << 4) & 0xf00); @@ -926,82 +1015,673 @@ case bfd_mach_avr1: val = E_AVR_MACH_AVR1; break; case bfd_mach_avr3: val = E_AVR_MACH_AVR3; break; case bfd_mach_avr4: val = E_AVR_MACH_AVR4; break; case bfd_mach_avr5: val = E_AVR_MACH_AVR5; break; } elf_elfheader (abfd)->e_machine = EM_AVR; elf_elfheader (abfd)->e_flags &= ~ EF_AVR_MACH; elf_elfheader (abfd)->e_flags |= val; + elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED; } /* Set the right machine number. */ static bfd_boolean elf32_avr_object_p (abfd) bfd *abfd; { unsigned int e_set = bfd_mach_avr2; if (elf_elfheader (abfd)->e_machine == EM_AVR || elf_elfheader (abfd)->e_machine == EM_AVR_OLD) { int e_mach = elf_elfheader (abfd)->e_flags & EF_AVR_MACH; + switch (e_mach) { default: case E_AVR_MACH_AVR2: e_set = bfd_mach_avr2; break; case E_AVR_MACH_AVR1: e_set = bfd_mach_avr1; break; case E_AVR_MACH_AVR3: e_set = bfd_mach_avr3; break; case E_AVR_MACH_AVR4: e_set = bfd_mach_avr4; break; case E_AVR_MACH_AVR5: e_set = bfd_mach_avr5; break; } } return bfd_default_set_arch_mach (abfd, bfd_arch_avr, e_set); } + +/* Enable debugging printout at stdout with a value of 1. */ +#define DEBUG_RELAX 0 + +/* This function handles relaxing for the avr. + Many important relaxing opportunities within functions are already + realized by the compiler itself. + Here we only try to replace call (4 bytes) -> rcall (2 bytes) + and jump -> rjmp (safes also 2 bytes). + We refrain from relaxing within sections ".vectors" and + ".jumptables" in order to maintain the position of the instructions. + There, however, we substitute jmp/call by a sequence rjmp,nop/rcall,nop + if possible. (In future one could possibly use the space of the nop + for the first instruction of the irq service function. + + The .jumptables sections is meant to be used for a future tablejump variant + for the devices with 3-byte program counter where the table itself + contains 4-byte jump instructions whose relative offset must not + be changed. */ + +static bfd_boolean +elf32_avr_relax_section (bfd *abfd, asection *sec, + struct bfd_link_info *link_info, + bfd_boolean *again) +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Rela *internal_relocs; + Elf_Internal_Rela *irel, *irelend; + bfd_byte *contents = NULL; + Elf_Internal_Sym *isymbuf = NULL; + static asection *last_input_section = NULL; + static Elf_Internal_Rela *last_reloc = NULL; + + /* Assume nothing changes. */ + *again = FALSE; + + /* We don't have to do anything for a relocatable link, if + this section does not have relocs, or if this is not a + code section. */ + if (link_info->relocatable + || (sec->flags & SEC_RELOC) == 0 + || sec->reloc_count == 0 + || (sec->flags & SEC_CODE) == 0) + return TRUE; + + /* Check if the object file to relax uses internal symbols so that we + could fix up the relocations. */ + + if (!(elf_elfheader (abfd)->e_flags & EF_AVR_LINKRELAX_PREPARED)) + return TRUE; + + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Get a copy of the native relocations. */ + internal_relocs = (_bfd_elf_link_read_relocs + (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, + link_info->keep_memory)); + if (internal_relocs == NULL) + goto error_return; + + if (sec != last_input_section) + last_reloc = NULL; + + last_input_section = sec; + + /* Walk through the relocs looking for relaxing opportunities. */ + irelend = internal_relocs + sec->reloc_count; + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma symval; + + if (ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL + && ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL + && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL) + 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 + { + /* Go get them off disk. */ + if (!bfd_malloc_and_get_section (abfd, sec, &contents)) + goto error_return; + } + } + + /* Read this BFD's local 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 error_return; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value; + /* If the reloc is absolute, it will not have + a symbol or section associated with it. */ + if (sym_sec) + symval += sym_sec->output_section->vma + + sym_sec->output_offset; + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + BFD_ASSERT (h != NULL); + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + { + /* This appears to be a reference to an undefined + symbol. Just ignore it--it will be caught by the + regular reloc processing. */ + continue; + } + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + switch (ELF32_R_TYPE (irel->r_info)) + { + /* Try to turn a 22-bit absolute call/jump into an 13-bit + pc-relative rcall/rjmp. */ + case R_AVR_CALL: + { + bfd_vma value = symval + irel->r_addend; + bfd_vma dot, gap; + int distance_short_enough = 0; + + /* Get the address of this instruction. */ + dot = (sec->output_section->vma + + sec->output_offset + irel->r_offset); + + /* Compute the distance from this insn to the branch target. */ + gap = value - dot; + + /* If the distance is within -4094..+4098 inclusive, then we can + relax this jump/call. +4098 because the call/jump target + will be closer after the relaxation. */ + if ((int) gap >= -4094 && (int) gap <= 4098) + distance_short_enough = 1; + + /* Here we handle the wrap-around case. E.g. for a 16k device + we could use a rjmp to jump from address 0x100 to 0x3d00! + In order to make this work properly, we need to fill the + vaiable avr_pc_wrap_around to the appropriate value. + I.e. 0x4000 for a 16k device. */ + { + /* Shrinking the code size makes the gaps larger in the + case of wrap-arounds. So we use a heuristical safety + margin to avoid that during relax the distance gets + again too large for the short jumps. Let's assume + a typical code-size reduction due to relax for a + 16k device of 600 bytes. So let's use twice the + typical value as safety margin. */ + + int rgap; + + int assumed_shrink = 600; + if (avr_pc_wrap_around > 0x4000) + assumed_shrink = 900; + + int safety_margin = assumed_shrink; + + rgap = avr_relative_distance_considering_wrap_around (gap); + + if (rgap >= (-4092 + safety_margin) + && rgap <= (4094 - safety_margin)) + distance_short_enough = 1; + } + + if (distance_short_enough) + { + if (DEBUG_RELAX) + printf ("shrinking jump/call instruction at address 0x%x" + " in section %s\n\n", + (int) dot, sec->name); + + unsigned char code_msb; + unsigned char code_lsb; + + /* Note that we've changed the relocs, section contents, + etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Get the instruction code for relaxing. */ + code_lsb = bfd_get_8 (abfd, contents + irel->r_offset); + code_msb = bfd_get_8 (abfd, contents + irel->r_offset + 1); + + /* Mask out the relocation bits. */ + code_msb &= 0x94; + code_lsb &= 0x0E; + if (code_msb == 0x94 && code_lsb == 0x0E) + { + /* we are changeing call -> rcall . */ + bfd_put_8 (abfd, 0x00, contents + irel->r_offset); + bfd_put_8 (abfd, 0xD0, contents + irel->r_offset + 1); + } + else if (code_msb == 0x94 && code_lsb == 0x0C) + { + /* we are changeing jump -> rjmp. */ + bfd_put_8 (abfd, 0x00, contents + irel->r_offset); + bfd_put_8 (abfd, 0xC0, contents + irel->r_offset + 1); + } + else + abort (); + + /* Fix the relocation's type. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_AVR_13_PCREL); + + /* Check for the vector section. There we don't want to + modify the ordering! */ + + if (!strcmp (sec->name,".vectors") + || !strcmp (sec->name,".jumptables")) + { + /* Let's insert a nop. */ + bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 2); + bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 3); + } + else + { + /* Delete two bytes of data. */ + if (!elf32_avr_relax_delete_bytes (abfd, sec, + irel->r_offset + 2, 2)) + goto error_return; + + /* That will change things, so, we should relax again. + Note that this is not required, and it may be slow. */ + *again = TRUE; + } + } + break; + } + + default: + break; + } + } + + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + { + if (! link_info->keep_memory) + free (contents); + else + { + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + return TRUE; + + error_return: + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + free (contents); + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + return FALSE; +} + +/* Delete some bytes from a section while changing the size of an instruction. + The parameter "addr" denotes the section-relative offset pointing just + behind the shrinked instruction. "addr+count" point at the first + byte just behind the original unshrinked instruction. */ +static bfd_boolean +elf32_avr_relax_delete_bytes (bfd *abfd, asection *sec, + bfd_vma addr, int count) +{ + Elf_Internal_Shdr *symtab_hdr; + unsigned int sec_shndx; + bfd_byte *contents; + Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Rela *irelalign; + Elf_Internal_Sym *isym; + Elf_Internal_Sym *isymbuf = NULL; + Elf_Internal_Sym *isymend; + bfd_vma toaddr; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + contents = elf_section_data (sec)->this_hdr.contents; + + + + /* The deletion must stop at the next ALIGN reloc for an aligment + power larger than the number of bytes we are deleting. */ + + irelalign = NULL; + toaddr = sec->size; + + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + + /* Actually delete the bytes. */ + memmove (contents + addr, contents + addr + count, + (size_t) (toaddr - addr - count)); + sec->size -= count; + + /* Adjust all the relocs. */ + for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + { + bfd_vma symval; + bfd_vma old_reloc_address; + bfd_vma shrinked_insn_address; + + old_reloc_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset); + shrinked_insn_address = (sec->output_section->vma + + sec->output_offset + addr - count); + + /* Get the new reloc address. */ + if ((irel->r_offset > addr + && irel->r_offset < toaddr)) + { + if (DEBUG_RELAX) + printf ("Relocation at address 0x%x needs to be moved.\n" + "Old section offset: 0x%x, New section offset: 0x%x \n", + (unsigned int) old_reloc_address, + (unsigned int) irel->r_offset, + (unsigned int) ((irel->r_offset) - count)); + + irel->r_offset -= count; + } + + /* The reloc's own addresses are now ok. However, we need to readjust + the reloc's addend if two conditions are met: + 1.) the reloc is relative to a symbol in this section that + is located in front of the shrinked instruction + 2.) symbol plus addend end up behind the shrinked instruction. + + This should happen only for local symbols that are progmem related. */ + + /* Read this BFD's local 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) + return FALSE; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value; + /* If the reloc is absolute, it will not have + a symbol or section associated with it. */ + if (sym_sec) + { + symval += sym_sec->output_section->vma + + sym_sec->output_offset; + + if (DEBUG_RELAX) + printf ("Checking if the relocation's " + "addend needs corrections.\n" + "Address of anchor symbol: 0x%x \n" + "Address of relocation target: 0x%x \n" + "Address of relaxed insn: 0x%x \n", + (unsigned int) symval, + (unsigned int) (symval + irel->r_addend), + (unsigned int) shrinked_insn_address); + + if ( symval <= shrinked_insn_address + && (symval + irel->r_addend) > shrinked_insn_address) + { + irel->r_addend -= count; + + if (DEBUG_RELAX) + printf ("Anchor symbol and relocation target bracket " + "shrinked insn address.\n" + "Need for new addend : 0x%x\n", + (unsigned int) irel->r_addend); + } + } + else + { + /* Reference symbol is absolute. No adjustment needed. */ + } + } + else + { + /* Reference symbol is extern. No need for adjusting the addend. */ + } + } + + /* Adjust the local symbols defined in this section. */ + isym = (Elf_Internal_Sym *) symtab_hdr->contents; + isymend = isym + symtab_hdr->sh_info; + for (; isym < isymend; isym++) + { + if (isym->st_shndx == sec_shndx + && isym->st_value > addr + && isym->st_value < toaddr) + isym->st_value -= count; + } + + /* Now adjust the global symbols defined in this section. */ + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + sym_hashes = elf_sym_hashes (abfd); + end_hashes = sym_hashes + symcount; + for (; sym_hashes < end_hashes; sym_hashes++) + { + struct elf_link_hash_entry *sym_hash = *sym_hashes; + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec + && sym_hash->root.u.def.value > addr + && sym_hash->root.u.def.value < toaddr) + { + sym_hash->root.u.def.value -= count; + } + } + + return TRUE; +} + +/* This is a version of bfd_generic_get_relocated_section_contents + which uses elf32_h8_relocate_section. + + For avr it's essentially a cut and paste taken from the H8300 port. + The author of the relaxation support patch for avr had absolutely no + clue what is happening here but found out that this part of the code + seems to be important. */ + +static bfd_byte * +elf32_avr_get_relocated_section_contents (bfd *output_bfd, + struct bfd_link_info *link_info, + struct bfd_link_order *link_order, + bfd_byte *data, + bfd_boolean relocatable, + asymbol **symbols) +{ + Elf_Internal_Shdr *symtab_hdr; + asection *input_section = link_order->u.indirect.section; + bfd *input_bfd = input_section->owner; + asection **sections = NULL; + Elf_Internal_Rela *internal_relocs = NULL; + Elf_Internal_Sym *isymbuf = NULL; + + /* We only need to handle the case of relaxing, or of having a + particular set of section contents, specially. */ + if (relocatable + || elf_section_data (input_section)->this_hdr.contents == NULL) + return bfd_generic_get_relocated_section_contents (output_bfd, link_info, + link_order, data, + relocatable, + symbols); + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + + memcpy (data, elf_section_data (input_section)->this_hdr.contents, + (size_t) input_section->size); + + if ((input_section->flags & SEC_RELOC) != 0 + && input_section->reloc_count > 0) + { + asection **secpp; + Elf_Internal_Sym *isym, *isymend; + bfd_size_type amt; + + internal_relocs = (_bfd_elf_link_read_relocs + (input_bfd, input_section, (PTR) NULL, + (Elf_Internal_Rela *) NULL, FALSE)); + if (internal_relocs == NULL) + goto error_return; + + if (symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + goto error_return; + } + + amt = symtab_hdr->sh_info; + amt *= sizeof (asection *); + sections = (asection **) bfd_malloc (amt); + if (sections == NULL && amt != 0) + goto error_return; + + isymend = isymbuf + symtab_hdr->sh_info; + for (isym = isymbuf, secpp = sections; isym < isymend; ++isym, ++secpp) + { + asection *isec; + + if (isym->st_shndx == SHN_UNDEF) + isec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + isec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + isec = bfd_com_section_ptr; + else + isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx); + + *secpp = isec; + } + + if (! elf32_avr_relocate_section (output_bfd, link_info, input_bfd, + input_section, data, internal_relocs, + isymbuf, sections)) + goto error_return; + + if (sections != NULL) + free (sections); + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (elf_section_data (input_section)->relocs != internal_relocs) + free (internal_relocs); + } + + return data; + + error_return: + if (sections != NULL) + free (sections); + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (internal_relocs != NULL + && elf_section_data (input_section)->relocs != internal_relocs) + free (internal_relocs); + return NULL; +} + + #define ELF_ARCH bfd_arch_avr #define ELF_MACHINE_CODE EM_AVR #define ELF_MACHINE_ALT1 EM_AVR_OLD #define ELF_MAXPAGESIZE 1 #define TARGET_LITTLE_SYM bfd_elf32_avr_vec #define TARGET_LITTLE_NAME "elf32-avr" #define elf_info_to_howto avr_info_to_howto_rela #define elf_info_to_howto_rel NULL #define elf_backend_relocate_section elf32_avr_relocate_section #define elf_backend_gc_mark_hook elf32_avr_gc_mark_hook #define elf_backend_gc_sweep_hook elf32_avr_gc_sweep_hook #define elf_backend_check_relocs elf32_avr_check_relocs #define elf_backend_can_gc_sections 1 #define elf_backend_rela_normal 1 #define elf_backend_final_write_processing \ bfd_elf_avr_final_write_processing #define elf_backend_object_p elf32_avr_object_p +#define bfd_elf32_bfd_relax_section elf32_avr_relax_section +#define bfd_elf32_bfd_get_relocated_section_contents \ + elf32_avr_get_relocated_section_contents + #include "elf32-target.h" Index: bfd/bfd-in2.h =================================================================== RCS file: /cvs/src/src/bfd/bfd-in2.h,v retrieving revision 1.367 diff -U20 -r1.367 bfd-in2.h --- bfd/bfd-in2.h 30 Sep 2005 15:36:35 -0000 1.367 +++ bfd/bfd-in2.h 15 Oct 2005 14:08:53 -0000 @@ -3429,54 +3429,63 @@ /* This is a 16 bit reloc for the AVR that stores 13 bit pc relative short offset into 12 bits. */ BFD_RELOC_AVR_13_PCREL, /* This is a 16 bit reloc for the AVR that stores 17 bit value (usually program memory address) into 16 bits. */ BFD_RELOC_AVR_16_PM, /* This is a 16 bit reloc for the AVR that stores 8 bit value (usually data memory address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_LO8_LDI, /* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit of data memory address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_HI8_LDI, /* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit of program memory address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_HH8_LDI, +/* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit +of 32bit value) into 8 bit immediate value of LDI insn. */ + BFD_RELOC_AVR_MS8_LDI, + /* This is a 16 bit reloc for the AVR that stores negated 8 bit value (usually data memory address) into 8 bit immediate value of SUBI insn. */ BFD_RELOC_AVR_LO8_LDI_NEG, /* This is a 16 bit reloc for the AVR that stores negated 8 bit value (high 8 bit of data memory address) into 8 bit immediate value of SUBI insn. */ BFD_RELOC_AVR_HI8_LDI_NEG, /* This is a 16 bit reloc for the AVR that stores negated 8 bit value (most high 8 bit of program memory address) into 8 bit immediate value of LDI or SUBI insn. */ BFD_RELOC_AVR_HH8_LDI_NEG, +/* This is a 16 bit reloc for the AVR that stores negated 8 bit value +(most significant 8 bit of 32 bit value) into 8 bit immediate value +of LDI or SUBI insn. */ + BFD_RELOC_AVR_MS8_LDI_NEG, + /* This is a 16 bit reloc for the AVR that stores 8 bit value (usually command address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_LO8_LDI_PM, /* This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit of command address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_HI8_LDI_PM, /* This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit of command address) into 8 bit immediate value of LDI insn. */ BFD_RELOC_AVR_HH8_LDI_PM, /* This is a 16 bit reloc for the AVR that stores negated 8 bit value (usually command address) into 8 bit immediate value of SUBI insn. */ BFD_RELOC_AVR_LO8_LDI_PM_NEG, /* This is a 16 bit reloc for the AVR that stores negated 8 bit value (high 8 bit of 16 bit command address) into 8 bit immediate value of SUBI insn. */ BFD_RELOC_AVR_HI8_LDI_PM_NEG, Index: bfd/libbfd.h =================================================================== RCS file: /cvs/src/src/bfd/libbfd.h,v retrieving revision 1.158 diff -U20 -r1.158 libbfd.h --- bfd/libbfd.h 30 Sep 2005 15:36:40 -0000 1.158 +++ bfd/libbfd.h 15 Oct 2005 14:08:56 -0000 @@ -1456,43 +1456,45 @@ "BFD_RELOC_MMIX_PUSHJ_1", "BFD_RELOC_MMIX_PUSHJ_2", "BFD_RELOC_MMIX_PUSHJ_3", "BFD_RELOC_MMIX_PUSHJ_STUBBABLE", "BFD_RELOC_MMIX_JMP", "BFD_RELOC_MMIX_JMP_1", "BFD_RELOC_MMIX_JMP_2", "BFD_RELOC_MMIX_JMP_3", "BFD_RELOC_MMIX_ADDR19", "BFD_RELOC_MMIX_ADDR27", "BFD_RELOC_MMIX_REG_OR_BYTE", "BFD_RELOC_MMIX_REG", "BFD_RELOC_MMIX_BASE_PLUS_OFFSET", "BFD_RELOC_MMIX_LOCAL", "BFD_RELOC_AVR_7_PCREL", "BFD_RELOC_AVR_13_PCREL", "BFD_RELOC_AVR_16_PM", "BFD_RELOC_AVR_LO8_LDI", "BFD_RELOC_AVR_HI8_LDI", "BFD_RELOC_AVR_HH8_LDI", + "BFD_RELOC_AVR_MS8_LDI", "BFD_RELOC_AVR_LO8_LDI_NEG", "BFD_RELOC_AVR_HI8_LDI_NEG", "BFD_RELOC_AVR_HH8_LDI_NEG", + "BFD_RELOC_AVR_MS8_LDI_NEG", "BFD_RELOC_AVR_LO8_LDI_PM", "BFD_RELOC_AVR_HI8_LDI_PM", "BFD_RELOC_AVR_HH8_LDI_PM", "BFD_RELOC_AVR_LO8_LDI_PM_NEG", "BFD_RELOC_AVR_HI8_LDI_PM_NEG", "BFD_RELOC_AVR_HH8_LDI_PM_NEG", "BFD_RELOC_AVR_CALL", "BFD_RELOC_AVR_LDI", "BFD_RELOC_AVR_6", "BFD_RELOC_AVR_6_ADIW", "BFD_RELOC_390_12", "BFD_RELOC_390_GOT12", "BFD_RELOC_390_PLT32", "BFD_RELOC_390_COPY", "BFD_RELOC_390_GLOB_DAT", "BFD_RELOC_390_JMP_SLOT", "BFD_RELOC_390_RELATIVE", "BFD_RELOC_390_GOTPC", "BFD_RELOC_390_GOT16", "BFD_RELOC_390_PC16DBL", Index: bfd/reloc.c =================================================================== RCS file: /cvs/src/src/bfd/reloc.c,v retrieving revision 1.140 diff -U20 -r1.140 reloc.c --- bfd/reloc.c 5 Oct 2005 21:24:23 -0000 1.140 +++ bfd/reloc.c 15 Oct 2005 14:09:03 -0000 @@ -3574,57 +3574,67 @@ BFD_RELOC_AVR_16_PM ENUMDOC This is a 16 bit reloc for the AVR that stores 17 bit value (usually program memory address) into 16 bits. ENUM BFD_RELOC_AVR_LO8_LDI ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (usually data memory address) into 8 bit immediate value of LDI insn. ENUM BFD_RELOC_AVR_HI8_LDI ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit of data memory address) into 8 bit immediate value of LDI insn. ENUM BFD_RELOC_AVR_HH8_LDI ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit of program memory address) into 8 bit immediate value of LDI insn. ENUM + BFD_RELOC_AVR_MS8_LDI +ENUMDOC + This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit + of 32 bit value) into 8 bit immediate value of LDI insn. +ENUM BFD_RELOC_AVR_LO8_LDI_NEG ENUMDOC This is a 16 bit reloc for the AVR that stores negated 8 bit value (usually data memory address) into 8 bit immediate value of SUBI insn. ENUM BFD_RELOC_AVR_HI8_LDI_NEG ENUMDOC This is a 16 bit reloc for the AVR that stores negated 8 bit value (high 8 bit of data memory address) into 8 bit immediate value of SUBI insn. ENUM BFD_RELOC_AVR_HH8_LDI_NEG ENUMDOC This is a 16 bit reloc for the AVR that stores negated 8 bit value (most high 8 bit of program memory address) into 8 bit immediate value of LDI or SUBI insn. ENUM + BFD_RELOC_AVR_MS8_LDI_NEG +ENUMDOC + This is a 16 bit reloc for the AVR that stores negated 8 bit value (msb + of 32 bit value) into 8 bit immediate value of LDI insn. +ENUM BFD_RELOC_AVR_LO8_LDI_PM ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (usually command address) into 8 bit immediate value of LDI insn. ENUM BFD_RELOC_AVR_HI8_LDI_PM ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (high 8 bit of command address) into 8 bit immediate value of LDI insn. ENUM BFD_RELOC_AVR_HH8_LDI_PM ENUMDOC This is a 16 bit reloc for the AVR that stores 8 bit value (most high 8 bit of command address) into 8 bit immediate value of LDI insn. ENUM BFD_RELOC_AVR_LO8_LDI_PM_NEG ENUMDOC This is a 16 bit reloc for the AVR that stores negated 8 bit value (usually command address) into 8 bit immediate value of SUBI insn. ENUM Index: gas/config/tc-avr.h =================================================================== RCS file: /cvs/src/src/gas/config/tc-avr.h,v retrieving revision 1.10 diff -U20 -r1.10 tc-avr.h --- gas/config/tc-avr.h 11 Aug 2005 01:25:25 -0000 1.10 +++ gas/config/tc-avr.h 15 Oct 2005 14:09:04 -0000 @@ -44,40 +44,41 @@ /* If you define this macro, GAS will warn about the use of nonstandard escape sequences in a string. */ #define ONLY_STANDARD_ESCAPES /* GAS will call this function for any expression that can not be recognized. When the function is called, `input_line_pointer' will point to the start of the expression. */ #define md_operand(x) /* You may define this macro to parse an expression used in a data allocation pseudo-op such as `.word'. You can use this to recognize relocation directives that may appear in such directives. */ #define TC_PARSE_CONS_EXPRESSION(EXPR,N) avr_parse_cons_expression (EXPR,N) void avr_parse_cons_expression (expressionS *exp, int nbytes); /* You may define this macro to generate a fixup for a data allocation pseudo-op. */ #define TC_CONS_FIX_NEW(FRAG,WHERE,N,EXP) avr_cons_fix_new(FRAG,WHERE,N,EXP) void avr_cons_fix_new(fragS *frag,int where, int nbytes, expressionS *exp); + /* This should just call either `number_to_chars_bigendian' or `number_to_chars_littleendian', whichever is appropriate. On targets like the MIPS which support options to change the endianness, which function to call is a runtime decision. On other targets, `md_number_to_chars' can be a simple macro. */ #define md_number_to_chars number_to_chars_littleendian /* `md_short_jump_size' `md_long_jump_size' `md_create_short_jump' `md_create_long_jump' If `WORKING_DOT_WORD' is defined, GAS will not do broken word processing (*note Broken words::.). Otherwise, you should set `md_short_jump_size' to the size of a short jump (a jump that is just long enough to jump around a long jmp) and `md_long_jump_size' to the size of a long jump (a jump that can go anywhere in the function), You should define `md_create_short_jump' to create a short jump around a long jump, and define `md_create_long_jump' to create a long jump. */ #define WORKING_DOT_WORD @@ -103,20 +104,43 @@ #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC) extern long md_pcrel_from_section PARAMS ((struct fix *, segT)); /* The number of bytes to put into a word in a listing. This affects the way the bytes are clumped together in the listing. For example, a value of 2 might print `1234 5678' where a value of 1 would print `12 34 56 78'. The default value is 4. */ #define LISTING_WORD_SIZE 2 /* AVR port uses `$' as a logical line separator */ #define LEX_DOLLAR 0 /* An `.lcomm' directive with no explicit alignment parameter will use this macro to set P2VAR to the alignment that a request for SIZE bytes will have. The alignment is expressed as a power of two. If no alignment should take place, the macro definition should do nothing. Some targets define a `.bss' directive that is also affected by this macro. The default definition will set P2VAR to the truncated power of two of sizes up to eight bytes. */ #define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0 + +/* We don't want gas to fixup anything because auf relaxing. */ +#define TC_LINKRELAX_FIXUP(seg) 1 + +/* We don't want gas to fixup the following program memory related relocations. + We will need them in case that we want to do linker relaxation. + We could in principle keep these fixups in gas when not relaxing. + However, there is no serious performance penilty when making the linker + make the fixup work. */ +#define TC_VALIDATE_FIX(FIXP,SEG,SKIP) \ +if (FIXP->fx_r_type == BFD_RELOC_AVR_7_PCREL \ + || FIXP->fx_r_type == BFD_RELOC_AVR_13_PCREL \ + || FIXP->fx_r_type == BFD_RELOC_AVR_LO8_LDI_PM \ + || FIXP->fx_r_type == BFD_RELOC_AVR_HI8_LDI_PM \ + || FIXP->fx_r_type == BFD_RELOC_AVR_HH8_LDI_PM \ + || FIXP->fx_r_type == BFD_RELOC_AVR_16_PM) \ + { \ + goto SKIP; \ + } + +/* We do not want to adjust any relocations to make implementation of + linker relaxations easier. */ +#define tc_fix_adjustable(FIXP) 0 Index: gas/config/tc-avr.c =================================================================== RCS file: /cvs/src/src/gas/config/tc-avr.c,v retrieving revision 1.28 diff -U20 -r1.28 tc-avr.c --- gas/config/tc-avr.c 11 Aug 2005 01:25:25 -0000 1.28 +++ gas/config/tc-avr.c 15 Oct 2005 14:09:05 -0000 @@ -8,40 +8,41 @@ GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "as.h" #include "safe-ctype.h" #include "subsegs.h" +#include struct avr_opcodes_s { char *name; char *constraints; int insn_size; /* In words. */ int isa; unsigned int bin_opcode; }; #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \ {#NAME, CONSTR, SIZE, ISA, BIN}, struct avr_opcodes_s avr_opcodes[] = { #include "opcode/avr.h" {NULL, NULL, 0, 0, 0} }; const char comment_chars[] = ";"; @@ -161,42 +162,42 @@ #define EXP_MOD_RELOC(i) exp_mod[i].reloc #define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc #define HAVE_PM_P(i) exp_mod[i].have_pm struct exp_mod_s { char *name; bfd_reloc_code_real_type reloc; bfd_reloc_code_real_type neg_reloc; int have_pm; }; static struct exp_mod_s exp_mod[] = { {"hh8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 1}, {"pm_hh8", BFD_RELOC_AVR_HH8_LDI_PM, BFD_RELOC_AVR_HH8_LDI_PM_NEG, 0}, {"hi8", BFD_RELOC_AVR_HI8_LDI, BFD_RELOC_AVR_HI8_LDI_NEG, 1}, {"pm_hi8", BFD_RELOC_AVR_HI8_LDI_PM, BFD_RELOC_AVR_HI8_LDI_PM_NEG, 0}, {"lo8", BFD_RELOC_AVR_LO8_LDI, BFD_RELOC_AVR_LO8_LDI_NEG, 1}, {"pm_lo8", BFD_RELOC_AVR_LO8_LDI_PM, BFD_RELOC_AVR_LO8_LDI_PM_NEG, 0}, - {"hlo8", -BFD_RELOC_AVR_LO8_LDI, -BFD_RELOC_AVR_LO8_LDI_NEG, 0}, - {"hhi8", -BFD_RELOC_AVR_HI8_LDI, -BFD_RELOC_AVR_HI8_LDI_NEG, 0}, + {"hlo8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 0}, + {"hhi8", BFD_RELOC_AVR_MS8_LDI, BFD_RELOC_AVR_MS8_LDI_NEG, 0}, }; /* Opcode hash table. */ static struct hash_control *avr_hash; /* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */ static struct hash_control *avr_mod_hash; #define OPTION_MMCU 'm' #define OPTION_ALL_OPCODES (OPTION_MD_BASE + 1) #define OPTION_NO_SKIP_BUG (OPTION_MD_BASE + 2) #define OPTION_NO_WRAP (OPTION_MD_BASE + 3) struct option md_longopts[] = { { "mmcu", required_argument, NULL, OPTION_MMCU }, { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES }, { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG }, { "mno-wrap", no_argument, NULL, OPTION_NO_WRAP }, { NULL, no_argument, NULL, 0 } @@ -426,43 +427,46 @@ { abort (); } void md_begin () { unsigned int i; struct avr_opcodes_s *opcode; avr_hash = hash_new (); /* Insert unique names into hash table. This hash table then provides a quick index to the first opcode with a particular name in the opcode table. */ for (opcode = avr_opcodes; opcode->name; opcode++) hash_insert (avr_hash, opcode->name, (char *) opcode); avr_mod_hash = hash_new (); for (i = 0; i < sizeof (exp_mod) / sizeof (exp_mod[0]); ++i) - hash_insert (avr_mod_hash, EXP_MOD_NAME (i), (void *) (i + 10)); + hash_insert (avr_mod_hash, EXP_MOD_NAME (i), + (void *) ((intptr_t) (i + 10))); bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach); + + linkrelax = 1; } /* Resolve STR as a constant expression and return the result. If result greater than MAX then error. */ static unsigned int avr_get_constant (str, max) char *str; int max; { expressionS ex; str = skip_space (str); input_line_pointer = str; expression (&ex); if (ex.X_op != O_constant) as_bad (_("constant value required")); if (ex.X_add_number > max || ex.X_add_number < 0) as_bad (_("number must be less than %d"), max + 1); @@ -969,69 +973,61 @@ break; case BFD_RELOC_AVR_6: if ((value > 63) || (value < 0)) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7) | ((value & (1 << 5)) << 8)), where); break; case BFD_RELOC_AVR_6_ADIW: if ((value > 63) || (value < 0)) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); bfd_putl16 ((bfd_vma) insn | (value & 0xf) | ((value & 0x30) << 2), where); break; case BFD_RELOC_AVR_LO8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where); break; - case -BFD_RELOC_AVR_LO8_LDI: - bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where); - break; - case BFD_RELOC_AVR_HI8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 8), where); break; - case -BFD_RELOC_AVR_HI8_LDI: + case BFD_RELOC_AVR_MS8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 24), where); break; case BFD_RELOC_AVR_HH8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where); break; case BFD_RELOC_AVR_LO8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value), where); break; - case -BFD_RELOC_AVR_LO8_LDI_NEG: - bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where); - break; - case BFD_RELOC_AVR_HI8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 8), where); break; - case -BFD_RELOC_AVR_HI8_LDI_NEG: + case BFD_RELOC_AVR_MS8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 24), where); break; case BFD_RELOC_AVR_HH8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where); break; case BFD_RELOC_AVR_LO8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 1), where); break; case BFD_RELOC_AVR_HI8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 9), where); break; case BFD_RELOC_AVR_HH8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 17), where); break; case BFD_RELOC_AVR_LO8_LDI_PM_NEG: @@ -1183,41 +1179,41 @@ xx8 (address) xx8 (-address) pm_xx8 (address) pm_xx8 (-address) where xx is: hh, hi, lo. */ static bfd_reloc_code_real_type avr_ldi_expression (exp) expressionS *exp; { char *str = input_line_pointer; char *tmp; char op[8]; int mod; tmp = str; str = extract_word (str, op, sizeof (op)); if (op[0]) { - mod = (int) hash_find (avr_mod_hash, op); + mod = (intptr_t) hash_find (avr_mod_hash, op); if (mod) { int closes = 0; mod -= 10; str = skip_space (str); if (*str == '(') { int neg_p = 0; ++str; if (strncmp ("pm(", str, 3) == 0 || strncmp ("-(pm(", str, 5) == 0) { if (HAVE_PM_P (mod)) { ++mod; Index: ld/scripttempl/avr.sc =================================================================== RCS file: /cvs/src/src/ld/scripttempl/avr.sc,v retrieving revision 1.3 diff -U20 -r1.3 avr.sc --- ld/scripttempl/avr.sc 8 May 2004 21:52:56 -0000 1.3 +++ ld/scripttempl/avr.sc 15 Oct 2005 14:09:06 -0000 @@ -58,75 +58,107 @@ { *(.rela.data) ${RELOCATING+*(.rela.data.*)} ${RELOCATING+*(.rela.gnu.linkonce.d*)} } .rel.ctors ${RELOCATING-0} : { *(.rel.ctors) } .rela.ctors ${RELOCATING-0} : { *(.rela.ctors) } .rel.dtors ${RELOCATING-0} : { *(.rel.dtors) } .rela.dtors ${RELOCATING-0} : { *(.rela.dtors) } .rel.got ${RELOCATING-0} : { *(.rel.got) } .rela.got ${RELOCATING-0} : { *(.rela.got) } .rel.bss ${RELOCATING-0} : { *(.rel.bss) } .rela.bss ${RELOCATING-0} : { *(.rela.bss) } .rel.plt ${RELOCATING-0} : { *(.rel.plt) } .rela.plt ${RELOCATING-0} : { *(.rela.plt) } /* Internal text space or external memory */ .text : { *(.vectors) + KEEP(*(.vectors)) ${CONSTRUCTING+ __ctors_start = . ; } ${CONSTRUCTING+ *(.ctors) } ${CONSTRUCTING+ __ctors_end = . ; } ${CONSTRUCTING+ __dtors_start = . ; } ${CONSTRUCTING+ *(.dtors) } ${CONSTRUCTING+ __dtors_end = . ; } + KEEP(SORT(*)(.ctors)) + KEEP(SORT(*)(.dtors)) + /* For data that needs to reside in the lower 64k of progmem */ *(.progmem.gcc*) *(.progmem*) ${RELOCATING+. = ALIGN(2);} + + /* for future tablejump instruction arrays for 3 byte pc devices */ + *(.jumptables) + *(.jumptables*) + /* for code that needs to reside in the lower 128k progmem */ + *(.lowtext) + *(.lowtext*) + *(.init0) /* Start here after reset. */ + KEEP (*(.init0)) *(.init1) + KEEP (*(.init1)) *(.init2) /* Clear __zero_reg__, set up stack pointer. */ + KEEP (*(.init2)) *(.init3) + KEEP (*(.init3)) *(.init4) /* Initialize data and BSS. */ + KEEP (*(.init4)) *(.init5) + KEEP (*(.init5)) *(.init6) /* C++ constructors. */ + KEEP (*(.init6)) *(.init7) + KEEP (*(.init7)) *(.init8) + KEEP (*(.init8)) *(.init9) /* Call main(). */ + KEEP (*(.init9)) *(.text) ${RELOCATING+. = ALIGN(2);} *(.text.*) ${RELOCATING+. = ALIGN(2);} *(.fini9) /* _exit() starts here. */ + KEEP (*(.fini9)) *(.fini8) + KEEP (*(.fini8)) *(.fini7) + KEEP (*(.fini7)) *(.fini6) /* C++ destructors. */ + KEEP (*(.fini6)) *(.fini5) + KEEP (*(.fini5)) *(.fini4) + KEEP (*(.fini4)) *(.fini3) + KEEP (*(.fini3)) *(.fini2) + KEEP (*(.fini2)) *(.fini1) + KEEP (*(.fini1)) *(.fini0) /* Infinite loop after program termination. */ + KEEP (*(.fini0)) ${RELOCATING+ _etext = . ; } } ${RELOCATING+ > text} .data ${RELOCATING-0} : ${RELOCATING+AT (ADDR (.text) + SIZEOF (.text))} { ${RELOCATING+ PROVIDE (__data_start = .) ; } *(.data) *(.gnu.linkonce.d*) ${RELOCATING+. = ALIGN(2);} ${RELOCATING+ _edata = . ; } ${RELOCATING+ PROVIDE (__data_end = .) ; } } ${RELOCATING+ > data} .bss ${RELOCATING+ SIZEOF(.data) + ADDR(.data)} : { ${RELOCATING+ PROVIDE (__bss_start = .) ; } *(.bss) *(COMMON) ${RELOCATING+ PROVIDE (__bss_end = .) ; } } ${RELOCATING+ > data}