diff options
-rw-r--r-- | bfd/ChangeLog | 19 | ||||
-rw-r--r-- | bfd/elf32-avr.c | 321 | ||||
-rw-r--r-- | ld/testsuite/ChangeLog | 11 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-1.d | 10 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-1.s | 7 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-2.d | 12 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-2.s | 13 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-3.d | 10 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-3.s | 8 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-4.d | 10 | ||||
-rw-r--r-- | ld/testsuite/ld-avr/avr-prop-4.s | 8 |
11 files changed, 428 insertions, 1 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2af87a1..5eabbd7 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,24 @@ 2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com> + * elf32-avr.c (struct elf_avr_section_data): New structure. + (struct avr_relax_info): New structure. + (elf_avr_new_section_hook): New function. + (struct elf_avr_section_data): Add relax_info. + (get_avr_relax_info): New function. + (init_avr_relax_info): New function. + (elf32_avr_relax_delete_bytes): Find next property record before + deleting bytes. When deleting don't move bytes beyond the next + property record. + (avr_elf32_assign_records_to_section): New function. + (avr_property_record_compare): New function. + (avr_load_all_property_sections): New function. + (elf32_avr_relax_section): Load property data. After relaxing the + section, move any .align directives that have enough deleted bytes + before them. + (bfd_elf32_new_section_hook): Define. + +2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com> + * elf32-avr.h (struct avr_property_header): New strucure. (avr_elf32_load_property_records): Declare. (avr_elf32_property_record_name): Declare. diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c index b780f8d..8651f49 100644 --- a/bfd/elf32-avr.c +++ b/bfd/elf32-avr.c @@ -707,6 +707,83 @@ static bfd_vma avr_pc_wrap_around = 0x10000000; instruction. This option could be switched off by a linker switch. */ static int avr_replace_call_ret_sequences = 1; + +/* Per-section relaxation related information for avr. */ + +struct avr_relax_info +{ + /* Track the avr property records that apply to this section. */ + + struct + { + /* Number of records in the list. */ + unsigned count; + + /* How many records worth of space have we allocated. */ + unsigned allocated; + + /* The records, only COUNT records are initialised. */ + struct avr_property_record *items; + } records; +}; + +/* Per section data, specialised for avr. */ + +struct elf_avr_section_data +{ + /* The standard data must appear first. */ + struct bfd_elf_section_data elf; + + /* Relaxation related information. */ + struct avr_relax_info relax_info; +}; + +/* Possibly initialise avr specific data for new section SEC from ABFD. */ + +static bfd_boolean +elf_avr_new_section_hook (bfd *abfd, asection *sec) +{ + if (!sec->used_by_bfd) + { + struct elf_avr_section_data *sdata; + bfd_size_type amt = sizeof (*sdata); + + sdata = bfd_zalloc (abfd, amt); + if (sdata == NULL) + return FALSE; + sec->used_by_bfd = sdata; + } + + return _bfd_elf_new_section_hook (abfd, sec); +} + +/* Return a pointer to the relaxation information for SEC. */ + +static struct avr_relax_info * +get_avr_relax_info (asection *sec) +{ + struct elf_avr_section_data *section_data; + + /* No info available if no section or if it is an output section. */ + if (!sec || sec == sec->output_section) + return NULL; + + section_data = (struct elf_avr_section_data *) elf_section_data (sec); + return §ion_data->relax_info; +} + +/* Initialise the per section relaxation information for SEC. */ + +static void +init_avr_relax_info (asection *sec) +{ + struct avr_relax_info *relax_info = get_avr_relax_info (sec); + + relax_info->records.count = 0; + relax_info->records.allocated = 0; + relax_info->records.items = NULL; +} + /* Initialize an entry in the stub hash table. */ static struct bfd_hash_entry * @@ -1737,13 +1814,38 @@ elf32_avr_relax_delete_bytes (bfd *abfd, struct elf_link_hash_entry **sym_hashes; struct elf_link_hash_entry **end_hashes; unsigned int symcount; + struct avr_relax_info *relax_info; + struct avr_property_record *prop_record = NULL; 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; + relax_info = get_avr_relax_info (sec); toaddr = sec->size; + if (relax_info->records.count > 0) + { + /* There should be no property record within the range of deleted + bytes, however, there might be a property record for ADDR, this is + how we handle alignment directives. + Find the next (if any) property record after the deleted bytes. */ + unsigned int i; + + for (i = 0; i < relax_info->records.count; ++i) + { + bfd_vma offset = relax_info->records.items [i].offset; + + BFD_ASSERT (offset <= addr || offset >= (addr + count)); + if (offset >= (addr + count)) + { + prop_record = &relax_info->records.items [i]; + toaddr = offset; + break; + } + } + } + irel = elf_section_data (sec)->relocs; irelend = irel + sec->reloc_count; @@ -1751,7 +1853,32 @@ elf32_avr_relax_delete_bytes (bfd *abfd, if (toaddr - addr - count > 0) memmove (contents + addr, contents + addr + count, (size_t) (toaddr - addr - count)); - sec->size -= count; + if (prop_record == NULL) + sec->size -= count; + else + { + /* Use the property record to fill in the bytes we've opened up. */ + int fill = 0; + switch (prop_record->type) + { + case RECORD_ORG_AND_FILL: + fill = prop_record->data.org.fill; + /* Fall through. */ + case RECORD_ORG: + break; + case RECORD_ALIGN_AND_FILL: + fill = prop_record->data.align.fill; + /* Fall through. */ + case RECORD_ALIGN: + prop_record->data.align.preceding_deleted += count; + break; + }; + memset (contents + toaddr - count, fill, count); + + /* Adjust the TOADDR to avoid moving symbols located at the address + of the property record, which has not moved. */ + toaddr -= count; + } /* Adjust all the reloc addresses. */ for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) @@ -2048,6 +2175,127 @@ get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx) return offset; } +/* Iterate over the property records in R_LIST, and copy each record into + the list of records within the relaxation information for the section to + which the record applies. */ + +static void +avr_elf32_assign_records_to_sections (struct avr_property_record_list *r_list) +{ + unsigned int i; + + for (i = 0; i < r_list->record_count; ++i) + { + struct avr_relax_info *relax_info; + + relax_info = get_avr_relax_info (r_list->records [i].section); + BFD_ASSERT (relax_info != NULL); + + if (relax_info->records.count + == relax_info->records.allocated) + { + /* Allocate more space. */ + bfd_size_type size; + + relax_info->records.allocated += 10; + size = (sizeof (struct avr_property_record) + * relax_info->records.allocated); + relax_info->records.items + = bfd_realloc (relax_info->records.items, size); + } + + memcpy (&relax_info->records.items [relax_info->records.count], + &r_list->records [i], + sizeof (struct avr_property_record)); + relax_info->records.count++; + } +} + +/* Compare two STRUCT AVR_PROPERTY_RECORD in AP and BP, used as the + ordering callback from QSORT. */ + +static int +avr_property_record_compare (const void *ap, const void *bp) +{ + const struct avr_property_record *a + = (struct avr_property_record *) ap; + const struct avr_property_record *b + = (struct avr_property_record *) bp; + + if (a->offset != b->offset) + return (a->offset - b->offset); + + if (a->section != b->section) + return (bfd_get_section_vma (a->section->owner, a->section) + - bfd_get_section_vma (b->section->owner, b->section)); + + return (a->type - b->type); +} + +/* Load all of the avr property sections from all of the bfd objects + referenced from LINK_INFO. All of the records within each property + section are assigned to the STRUCT AVR_RELAX_INFO within the section + specific data of the appropriate section. */ + +static void +avr_load_all_property_sections (struct bfd_link_info *link_info) +{ + bfd *abfd; + asection *sec; + + /* Initialize the per-section relaxation info. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + init_avr_relax_info (sec); + } + + /* Load the descriptor tables from .avr.prop sections. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) + { + struct avr_property_record_list *r_list; + + r_list = avr_elf32_load_property_records (abfd); + if (r_list != NULL) + avr_elf32_assign_records_to_sections (r_list); + + free (r_list); + } + + /* Now, for every section, ensure that the descriptor list in the + relaxation data is sorted by ascending offset within the section. */ + for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next) + for (sec = abfd->sections; sec != NULL; sec = sec->next) + { + struct avr_relax_info *relax_info = get_avr_relax_info (sec); + if (relax_info && relax_info->records.count > 0) + { + unsigned int i; + + qsort (relax_info->records.items, + relax_info->records.count, + sizeof (struct avr_property_record), + avr_property_record_compare); + + /* For debug purposes, list all the descriptors. */ + for (i = 0; i < relax_info->records.count; ++i) + { + switch (relax_info->records.items [i].type) + { + case RECORD_ORG: + break; + case RECORD_ORG_AND_FILL: + break; + case RECORD_ALIGN: + break; + case RECORD_ALIGN_AND_FILL: + break; + }; + } + } + } +} + /* This function handles relaxing for the avr. Many important relaxing opportunities within functions are already realized by the compiler itself. @@ -2091,6 +2339,15 @@ elf32_avr_relax_section (bfd *abfd, bfd_byte *contents = NULL; Elf_Internal_Sym *isymbuf = NULL; struct elf32_avr_link_hash_table *htab; + static bfd_boolean relaxation_initialised = FALSE; + + if (!relaxation_initialised) + { + relaxation_initialised = TRUE; + + /* Load entries from the .avr.prop sections. */ + avr_load_all_property_sections (link_info); + } /* If 'shrinkable' is FALSE, do not shrink by deleting bytes while relaxing. Such shrinking can cause issues for the sections such @@ -2694,6 +2951,67 @@ elf32_avr_relax_section (bfd *abfd, } } + if (!*again) + { + /* Look through all the property records in this section to see if + there's any alignment records that can be moved. */ + struct avr_relax_info *relax_info; + + relax_info = get_avr_relax_info (sec); + if (relax_info->records.count > 0) + { + unsigned int i; + + for (i = 0; i < relax_info->records.count; ++i) + { + switch (relax_info->records.items [i].type) + { + case RECORD_ORG: + case RECORD_ORG_AND_FILL: + break; + case RECORD_ALIGN: + case RECORD_ALIGN_AND_FILL: + { + struct avr_property_record *record; + unsigned long bytes_to_align; + int count = 0; + + /* Look for alignment directives that have had enough + bytes deleted before them, such that the directive + can be moved backwards and still maintain the + required alignment. */ + record = &relax_info->records.items [i]; + bytes_to_align + = (unsigned long) (1 << record->data.align.bytes); + while (record->data.align.preceding_deleted >= + bytes_to_align) + { + record->data.align.preceding_deleted + -= bytes_to_align; + count += bytes_to_align; + } + + if (count > 0) + { + bfd_vma addr = record->offset; + + /* We can delete COUNT bytes and this alignment + directive will still be correctly aligned. + First move the alignment directive, then delete + the bytes. */ + record->offset -= count; + elf32_avr_relax_delete_bytes (abfd, sec, + addr - count, + count); + *again = TRUE; + } + } + break; + } + } + } + } + if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) { @@ -3809,5 +4127,6 @@ avr_elf32_property_record_name (struct avr_property_record *rec) #define bfd_elf32_bfd_relax_section elf32_avr_relax_section #define bfd_elf32_bfd_get_relocated_section_contents \ elf32_avr_get_relocated_section_contents +#define bfd_elf32_new_section_hook elf_avr_new_section_hook #include "elf32-target.h" diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index 9313705..3d7fcba 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2015-02-25 Andrew Burgess <andrew.burgess@embecosm.com> + + * ld-avr/avr-prop-1.d: New file. + * ld-avr/avr-prop-1.s: New file. + * ld-avr/avr-prop-2.d: New file. + * ld-avr/avr-prop-2.s: New file. + * ld-avr/avr-prop-3.d: New file. + * ld-avr/avr-prop-3.s: New file. + * ld-avr/avr-prop-4.d: New file. + * ld-avr/avr-prop-4.s: New file. + 2015-02-25 Oleg Endo <olegendo@gcc.gnu.org> * ld-sh/arch/arch_expected.txt: Regenerate. diff --git a/ld/testsuite/ld-avr/avr-prop-1.d b/ld/testsuite/ld-avr/avr-prop-1.d new file mode 100644 index 0000000..0c4ddc4 --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-1.d @@ -0,0 +1,10 @@ +#name: AVR .avr.prop, single .org test. +#as: -mmcu=avrxmega2 -mlink-relax +#ld: -mavrxmega2 --relax +#source: avr-prop-1.s +#nm: -n +#target: avr-*-* + +#... +00000020 T dest +#... diff --git a/ld/testsuite/ld-avr/avr-prop-1.s b/ld/testsuite/ld-avr/avr-prop-1.s new file mode 100644 index 0000000..a287d3e --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-1.s @@ -0,0 +1,7 @@ + .text + .global _start, dest +_start: + CALL dest + .org 0x20 +dest: + nop diff --git a/ld/testsuite/ld-avr/avr-prop-2.d b/ld/testsuite/ld-avr/avr-prop-2.d new file mode 100644 index 0000000..a45443e --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-2.d @@ -0,0 +1,12 @@ +#name: AVR .avr.prop, multiple .org test. +#as: -mmcu=avrxmega2 -mlink-relax +#ld: -mavrxmega2 --relax +#source: avr-prop-2.s +#nm: -n +#target: avr-*-* + +#... +00000010 T label1 +00000020 T label2 +00000030 T dest +#... diff --git a/ld/testsuite/ld-avr/avr-prop-2.s b/ld/testsuite/ld-avr/avr-prop-2.s new file mode 100644 index 0000000..a36eb18 --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-2.s @@ -0,0 +1,13 @@ + .text + .global _start, label1, label2, dest +_start: + CALL dest + .org 0x10 +label1: + CALL dest + .org 0x20 +label2: + CALL dest + .org 0x30 +dest: + nop diff --git a/ld/testsuite/ld-avr/avr-prop-3.d b/ld/testsuite/ld-avr/avr-prop-3.d new file mode 100644 index 0000000..0e1e271 --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-3.d @@ -0,0 +1,10 @@ +#name: AVR .avr.prop, single .align test. +#as: -mmcu=avrxmega2 -mlink-relax +#ld: -mavrxmega2 --relax +#source: avr-prop-3.s +#nm: -n +#target: avr-*-* + +#... +00000008 T dest +#... diff --git a/ld/testsuite/ld-avr/avr-prop-3.s b/ld/testsuite/ld-avr/avr-prop-3.s new file mode 100644 index 0000000..bf66a45 --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-3.s @@ -0,0 +1,8 @@ + .text + .global _start, dest +_start: + CALL dest + CALL dest + .align 3 +dest: + NOP diff --git a/ld/testsuite/ld-avr/avr-prop-4.d b/ld/testsuite/ld-avr/avr-prop-4.d new file mode 100644 index 0000000..42a42c1 --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-4.d @@ -0,0 +1,10 @@ +#name: AVR .avr.prop, realign .align test. +#as: -mmcu=avrxmega2 -mlink-relax +#ld: -mavrxmega2 --relax +#source: avr-prop-4.s +#nm: -n +#target: avr-*-* + +#... +00000004 T dest +#... diff --git a/ld/testsuite/ld-avr/avr-prop-4.s b/ld/testsuite/ld-avr/avr-prop-4.s new file mode 100644 index 0000000..b1d532f --- /dev/null +++ b/ld/testsuite/ld-avr/avr-prop-4.s @@ -0,0 +1,8 @@ + .text + .global _start, dest +_start: + CALL dest + CALL dest + .align 2 +dest: + NOP |