diff --git bfd/elf32-avr.c bfd/elf32-avr.c index a2d4401..e957024 100644 --- bfd/elf32-avr.c +++ bfd/elf32-avr.c @@ -617,6 +617,11 @@ static bfd_vma avr_pc_wrap_around = 0x10000000; machine will try to optimize call/ret sequences by a single jump instruction. This option could be switched off by a linker switch. */ static int avr_replace_call_ret_sequences = 1; + +/* This variable decides whether the linker will attempt to reduce + the size of the interrupt vector table. Needs to be turned on by + a linker switch (--shrink-ivt). */ +static bfd_boolean avr_shrink_ivt = FALSE; /* Initialize an entry in the stub hash table. */ @@ -1649,6 +1654,212 @@ elf32_avr_relax_delete_bytes (bfd *abfd, return TRUE; } +static const char *vector_prefix = "__vector_"; +static int extract_vector_number (const char *symbol_name) +{ + const char *start = symbol_name + strlen (vector_prefix); + int vector_number = 0; + + while (*start >= '0' && *start <= '9') + { + vector_number = vector_number * 10 + (*start - '0'); + start++; + } + + return vector_number; +} + + +/* Given symbol indices for symbols representing the max available and + max defined vectors, this function returns addresses for the corresponding + entries in the vector table, using the relocation offset for those symbol + references */ +static void get_vector_entry_addresses (bfd *abfd, + asection *sec, + int last_vector_sym_index, + int last_defined_vector_sym_index, + bfd_vma *last_vector_entry_address, + bfd_vma *last_defined_vector_entry_address, + unsigned int *vector_table_entry_size) +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + Elf_Internal_Rela *irel = _bfd_elf_link_read_relocs + (abfd, sec, NULL, NULL, TRUE); + + Elf_Internal_Rela *irelend = irel + sec->reloc_count; + + for (; irel < irelend; irel++) + { + int indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + if (indx < 0) + continue; + + if (indx == last_defined_vector_sym_index) + { + *last_defined_vector_entry_address = irel->r_offset; + } + if (indx == last_vector_sym_index) + { + *last_vector_entry_address = irel->r_offset; + *vector_table_entry_size = bfd_get_reloc_size( + elf_avr_howto_table + + ELF32_R_TYPE (irel->r_info)); + } + } +} + +/* Filters the relocation array to only include entries that point into the + section AFTER deleting the extra bytes i.e. having offsets less than the + section size after deletion. */ +static void remove_extra_vector_entry_relocations (bfd *abfd, + asection *sec, + unsigned int section_size_after_deletion) +{ + Elf_Internal_Rela *irel = _bfd_elf_link_read_relocs + (abfd, sec, NULL, NULL, TRUE); + Elf_Internal_Rela *irelend = irel + sec->reloc_count; + + Elf_Internal_Rela *updated_irel = irel; + unsigned int valid_reloc_count = 0; + + for (; irel < irelend; irel++) + { + if (irel->r_offset < section_size_after_deletion) + { + *updated_irel++ = *irel; + valid_reloc_count++; + } + } + + sec->reloc_count = valid_reloc_count; +} + + +static void delete_unused_vector_table_bytes (bfd *abfd, + asection *sec, + int last_vector_sym_index, + int last_defined_vector_sym_index) +{ + bfd_vma last_vector_entry_address = 0; + bfd_vma last_defined_vector_entry_address = 0; + unsigned int vector_table_entry_size = 0; + + get_vector_entry_addresses (abfd, sec, + last_vector_sym_index, + last_defined_vector_sym_index, + &last_vector_entry_address, + &last_defined_vector_entry_address, + &vector_table_entry_size); + + if (debug_relax) + printf ("Last defined vector address : %0x, Last vector address : %0x", + (int)last_defined_vector_entry_address, + (int)last_vector_entry_address); + + if (last_defined_vector_entry_address < last_vector_entry_address) + { + if (debug_relax) + printf ("Deleting from %0x to %0x\n", + (unsigned int)last_defined_vector_entry_address + + vector_table_entry_size, + (unsigned int)(last_vector_entry_address + - last_defined_vector_entry_address)); + + /* Initialize contents pointer and cache contents, relax_delete_bytes expects + it to be populated */ + if (elf_section_data (sec)->this_hdr.contents == NULL) + { + bfd_byte *contents = NULL; + if (! bfd_malloc_and_get_section (abfd, sec, &contents)) + return; + } + + unsigned int bytes_to_delete = last_vector_entry_address - + last_defined_vector_entry_address; + + /* Do this before calling avr_relax_delete_bytes, as that function + adjusts relocation offsets by bytes_to_delete, and having + relocations pointing past the section confuses it. */ + remove_extra_vector_entry_relocations (abfd, sec, + sec->size - bytes_to_delete); + + /* The vector entry address points to the start of the entry, so add the size + of an entry and then delete from there */ + elf32_avr_relax_delete_bytes (abfd, sec, + last_defined_vector_entry_address + vector_table_entry_size, + bytes_to_delete); + } +} + + +/* Walks through the global symbols in the BFD, finds those that + represent interrupt handlers (i.e. those that start with __vector_*), + and finds the max available and max defined ones. This assumes the + avr-libc convention of defining vector table entries as jumps to + __vector_, with __vector_ weakly defined by default. */ +static void find_vector_symbols (bfd *abfd, + int *last_vector_sym_index, + int *last_defined_vector_sym_index) +{ + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); + int symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + struct elf_link_hash_entry **end_hashes = sym_hashes + symcount; + + int last_vector_number = 0; + int last_defined_vector_number = 0; + int sym_index = 0; + + for (; sym_hashes < end_hashes; sym_hashes++,sym_index++) + { + struct elf_link_hash_entry *sym_hash = *sym_hashes; + const char *symbol_name = sym_hash->root.root.string; + + /* If the symbol's name begins with __vector_ */ + if (strstr (symbol_name, vector_prefix) == symbol_name) + { + int vector_number = extract_vector_number (symbol_name); + + if (vector_number < 0) + continue; + + if (vector_number > last_vector_number) + { + last_vector_number = vector_number; + *last_vector_sym_index = sym_index; + } + + if (sym_hash->root.type == bfd_link_hash_defined) + { + if (vector_number > last_defined_vector_number) + { + last_defined_vector_number = vector_number; + *last_defined_vector_sym_index = sym_index; + } + } + } + } +} + +static void shrink_vector_table (bfd *abfd, + asection *sec) +{ + int last_vector_sym_index = -1; + int last_defined_vector_sym_index = -1; + + find_vector_symbols (abfd, &last_vector_sym_index, + &last_defined_vector_sym_index); + + /* Could not find interrupt vector handler symbol - quit */ + if (last_vector_sym_index == -1) + return; + + delete_unused_vector_table_bytes (abfd, sec, + last_vector_sym_index, + last_defined_vector_sym_index); +} + /* This function handles relaxing for the avr. Many important relaxing opportunities within functions are already realized by the compiler itself. @@ -1673,7 +1884,9 @@ elf32_avr_relax_delete_bytes (bfd *abfd, ".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. + for the first instruction of the irq service function). In addition, the + if the shrink-ivt option is passed, the ".vectors" section contents are + shrunk down to the last interrupt vector that has a handler. 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 @@ -1699,6 +1912,11 @@ elf32_avr_relax_section (bfd *abfd, filled with nop instructions. */ bfd_boolean shrinkable = TRUE; + if (avr_shrink_ivt && !strcmp (sec->name,".vectors")) + { + shrink_vector_table (abfd, sec); + } + if (!strcmp (sec->name,".vectors") || !strcmp (sec->name,".jumptables")) shrinkable = FALSE; @@ -2598,7 +2816,8 @@ elf32_avr_setup_params (struct bfd_link_info *info, bfd_boolean deb_stubs, bfd_boolean deb_relax, bfd_vma pc_wrap_around, - bfd_boolean call_ret_replacement) + bfd_boolean call_ret_replacement, + bfd_boolean shrink_ivt) { struct elf32_avr_link_hash_table *htab = avr_link_hash_table (info); @@ -2612,6 +2831,7 @@ elf32_avr_setup_params (struct bfd_link_info *info, debug_stubs = deb_stubs; avr_pc_wrap_around = pc_wrap_around; avr_replace_call_ret_sequences = call_ret_replacement; + avr_shrink_ivt = shrink_ivt; } diff --git bfd/elf32-avr.h bfd/elf32-avr.h index 5eeca86..60bed13 100644 --- bfd/elf32-avr.h +++ bfd/elf32-avr.h @@ -26,7 +26,7 @@ extern void elf32_avr_setup_params (struct bfd_link_info *, bfd *, asection *, bfd_boolean, bfd_boolean, bfd_boolean, - bfd_vma, bfd_boolean); + bfd_vma, bfd_boolean, bfd_boolean); extern int elf32_avr_setup_section_lists (bfd *, struct bfd_link_info *); diff --git ld/emultempl/avrelf.em ld/emultempl/avrelf.em index 90894a1..781b612 100644 --- ld/emultempl/avrelf.em +++ ld/emultempl/avrelf.em @@ -44,6 +44,7 @@ static bfd_boolean avr_debug_relax = FALSE; static bfd_boolean avr_debug_stubs = FALSE; static bfd_boolean avr_replace_call_ret_sequences = TRUE; static bfd_vma avr_pc_wrap_around = 0x10000000; +static bfd_boolean avr_shrink_ivt = FALSE; /* Transfers information to the bfd frontend. */ @@ -57,7 +58,8 @@ avr_elf_set_global_bfd_parameters (void) avr_debug_stubs, avr_debug_relax, avr_pc_wrap_around, - avr_replace_call_ret_sequences); + avr_replace_call_ret_sequences, + avr_shrink_ivt); } @@ -186,6 +188,7 @@ PARSE_AND_LIST_PROLOGUE=' #define OPTION_NO_STUBS 303 #define OPTION_DEBUG_STUBS 304 #define OPTION_DEBUG_RELAX 305 +#define OPTION_SHRINK_VECTOR_TABLE 306 ' PARSE_AND_LIST_LONGOPTS=' @@ -199,6 +202,8 @@ PARSE_AND_LIST_LONGOPTS=' NULL, OPTION_DEBUG_STUBS}, { "debug-relax", no_argument, NULL, OPTION_DEBUG_RELAX}, + { "shrink-ivt", no_argument, + NULL, OPTION_SHRINK_VECTOR_TABLE}, ' PARSE_AND_LIST_OPTIONS=' @@ -228,6 +233,8 @@ PARSE_AND_LIST_OPTIONS=' "Used for debugging avr-ld.\n")); fprintf (file, _(" --debug-relax " "Used for debugging avr-ld.\n")); + fprintf (file, _(" --shrink-ivt " + "Shrinks the IVT down to the last handled interrupt.\n")); ' PARSE_AND_LIST_ARGS_CASES=' @@ -260,6 +267,10 @@ PARSE_AND_LIST_ARGS_CASES=' avr_no_stubs = TRUE; break; + case OPTION_SHRINK_VECTOR_TABLE: + avr_shrink_ivt = TRUE; + break; + case OPTION_NO_CALL_RET_REPLACEMENT: { /* This variable is defined in the bfd library. */