@@ -34,6 +34,20 @@ static bool debug_relax = false;
/* Enable debugging printout at stdout with this variable. */
static bool debug_stubs = false;
+static void dprint (const char *fmt, ...) ATTRIBUTE_PRINTF_1;
+static void dprint (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ if (debug_relax)
+ {
+ vprintf (fmt, args);
+ fflush (stdout);
+ }
+ va_end (args);
+}
+
+
static bfd_reloc_status_type
bfd_elf_avr_diff_reloc (bfd *, arelent *, asymbol *, void *,
asection *, bfd *, char **);
@@ -735,6 +749,9 @@ static bool avr_replace_call_ret_sequences = true;
include plain RJMP .+0 which is used by GCC to delay 2 cycles.
This can be switched off by --no-elide-rjmp0. */
static bool avr_elide_rjmp0 = true;
+
+/* Prune the vector table provided --relax. */
+static bool avr_prune_vectab = false;
/* Per-section relaxation related information for avr. */
@@ -999,6 +1016,13 @@ avr_is_RJMP (uint16_t w)
return 0xc000 == (w & 0xf000);
}
+/* Return true iff W is a CLH instruction. */
+
+static bool
+avr_is_CLH (uint16_t w)
+{
+ return 0x94d8 == w;
+}
/* Return true iff W is a RET instruction. */
@@ -2539,6 +2563,394 @@ avr_reloc_at (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
}
+/* Return the target address of a global symbol NAME, or -1 if not found. */
+
+static int
+avr_global_symbol_address (struct bfd_link_info *link_info, const char *name)
+{
+ struct bfd_link_hash_entry *h;
+ h = bfd_link_hash_lookup (link_info->hash, name,
+ false /*create*/, false /*copy*/, true /*follow*/);
+ if (h)
+ {
+ const bfd_vma value = h->u.def.value;
+ const asection *sec = h->u.def.section;
+ return (int) (value
+ + sec->output_offset
+ + sec->output_section->vma);
+ }
+
+ return -1;
+}
+
+
+/* Return NULL or the name of the global symbol used by reloc REL. */
+
+static const char *
+avr_reloc_global_symbol_name (bfd *abfd, Elf_Internal_Rela *rel)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+
+ if (ELF32_R_SYM (rel->r_info) < symtab_hdr->sh_info)
+ // This is a local symbol.
+ return NULL;
+
+ struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+ struct elf_link_hash_entry *h;
+ h = sym_hashes[ELF32_R_SYM (rel->r_info) - symtab_hdr->sh_info];
+ if (h != NULL)
+ {
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ return h->root.root.string;
+ }
+
+ return NULL;
+}
+
+
+// AVR-LibC produces vectabs with up to 128 entries at most.
+#define N_IRQS 128
+
+// Symbol name of an unused vectab entry. Vectab entries that target
+// the address of this global symbol are regarded as unused.
+#define S_BAD_IRQ "__bad_interrupt"
+
+typedef struct
+{
+ int idx;
+ // Targte address of jump.
+ unsigned symval;
+ Elf_Internal_Rela *reloc;
+ // Code address of the jump / vectab entry.
+ bfd_vma dot;
+ // Whether something is targeting the vectab entry.
+ bool has_label_or_reloc;
+} avr_irq_t;
+
+typedef struct
+{
+ // Size in bytes of .vectors without an optional trailing CLH.
+ unsigned size;
+ // The number of (expected) IRQs.
+ int n_irqs;
+ // The size of one entry is 2 bytes or 4 bytes.
+ int vec_size;
+ // Code address of __bad_interrupt.
+ int bad_irq;
+ // The very table.
+ avr_irq_t irqs[N_IRQS];
+ // Whether vectab pruning has been attempted.
+ bool pruned;
+} avr_vectab_t;
+
+
+/* Traverse all input bfds and find all CALL / JMP relocs that point
+ into the vectab. This allows code like
+
+ JMP __vectors + 0x8
+
+ to work as expected, i.e. the respective IRQ entry won't be pruned.
+ Notice that JMP 0x8 is *NOT SUPPORTED* since it doesn't have a
+ reloc, and hence we can't find it without scanning the entire code.
+ Set vtab->irqs[].has_label_or_reloc when such a reloc is found. */
+
+static bool
+avr_find_relocs_to_vectab (struct bfd_link_info *link_info, avr_vectab_t *vtab)
+{
+ for (bfd *abfd = link_info->input_bfds; abfd; abfd = abfd->link.next)
+ {
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+
+ for (struct bfd_section *sec = abfd->sections; sec; sec = sec->next)
+ {
+ if (sec->reloc_count == 0
+ || (sec->flags & SEC_RELOC) == 0
+ || (sec->flags & SEC_HAS_CONTENTS) == 0
+ || (sec->flags & SEC_CODE) == 0)
+ continue;
+
+ Elf_Internal_Rela *rel = elf_section_data (sec)->relocs;
+ if (rel == NULL)
+ rel = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, true);
+
+ Elf_Internal_Rela *relend = rel + sec->reloc_count;
+ for (; rel && rel < relend; ++rel)
+ {
+ if (ELF32_R_SYM (rel->r_info) >= symtab_hdr->sh_info
+ && (ELF32_R_TYPE (rel->r_info) == R_AVR_CALL
+ || ELF32_R_TYPE (rel->r_info) == R_AVR_13_PCREL))
+ {
+ // A global symbol.
+ const char *sname = avr_reloc_global_symbol_name (abfd, rel);
+ if (sname && !strcmp (sname, "__vectors"))
+ {
+ const int idx = rel->r_addend / vtab->vec_size;
+ if (idx >= 0 && idx < vtab->n_irqs)
+ vtab->irqs[idx].has_label_or_reloc = true;
+ }
+ } // Jump / call related
+ } // Relocs
+ } // Input sections
+ } // BFDs
+
+ return false;
+}
+
+
+/* Try to prune a vector table from AVR-LibC. When entries are unused, i.e.
+ they effectively jump to __bad_interrupt, then we can remove them provided
+ all later entries can also be removed. The trigger is --prune-vectab,
+ which only takes effect when linker relaxation is on.
+ return 1: Vectab has just been changed.
+ return 0: Nothing changed and no internal error occurred.
+ return -1: An error occurred. */
+
+static int
+avr_maybe_prune_vectab (bfd *abfd, struct bfd_link_info *link_info,
+ asection *sec, Elf_Internal_Rela *internal_relocs,
+ bfd_byte **pcontents)
+{
+ static avr_vectab_t avr_vectab;
+ avr_vectab_t *vtab = &avr_vectab;
+
+ if (!avr_prune_vectab || vtab->pruned || strcmp (sec->name, ".vectors"))
+ return 0;
+
+ vtab->pruned = true;
+ vtab->size = (unsigned) sec->size;
+
+ dprint ("== Pruning .vectors of size 0x%x\n", vtab->size);
+
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+
+ // Get a cached copy of the contents if it exists.
+ if (elf_section_data (sec)->this_hdr.contents != NULL)
+ *pcontents = elf_section_data (sec)->this_hdr.contents;
+ else
+ // Go get them off disk.
+ if (! bfd_malloc_and_get_section (abfd, sec, pcontents))
+ {
+ dprint ("Prune failed: error reading contents\n");
+ return -1;
+ }
+
+ // Look up __bad_interrupt in the linker's global hash table.
+ vtab->bad_irq = avr_global_symbol_address (link_info, S_BAD_IRQ);
+ if (vtab->bad_irq < 0)
+ {
+ dprint ("Prune failed: %s not found\n", S_BAD_IRQ);
+ return 0;
+ }
+ dprint ("Prune: %s = 0x%x\n", S_BAD_IRQ, vtab->bad_irq);
+
+ // The very first word must be a JMP or a RJMP, and it heralds the
+ // sizes of all the following entries.
+ const uint16_t code_word0 = avr_word (abfd, *pcontents + 0);
+ vtab->vec_size = 0?0
+ : avr_is_RJMP (code_word0) ? 2
+ : avr_is_JMP (code_word0) ? 4
+ : 0;
+
+ if (vtab->vec_size == 0)
+ {
+ dprint ("Prune failed: vectab doesn't start with JMP nor RJMP\n");
+ return 0;
+ }
+
+ const uint16_t code_last = avr_word (abfd, *pcontents + (vtab->size - 2));
+
+ if (vtab->vec_size == 4
+ && vtab->size % vtab->vec_size == 2
+ && avr_is_CLH (code_last))
+ {
+ // This is the case for AVR-SD devices that need a valid insn after each
+ // executed instruction. AVR-LibC inserts a CLH at the vectab end.
+ dprint ("Prune: vectab has a trailing CLH\n");
+ vtab->size -= 2;
+ }
+ else if (vtab->size % vtab->vec_size != 0)
+ {
+ dprint ("Prune failed: odd vectab layout\n");
+ return 0;
+ }
+
+ vtab->n_irqs = vtab->size / vtab->vec_size;
+
+ dprint ("Prune: expecting %d %sJMP entries\n", vtab->n_irqs,
+ vtab->vec_size == 2 ? "R" : "");
+
+ if (vtab->n_irqs > N_IRQS)
+ {
+ dprint ("Prune failed: vectab has more than %d entries\n", N_IRQS);
+ return 0;
+ }
+
+ // As soon as we know the vec_size, find any relocs that point
+ // into the vectab. Mark .has_label_or_reloc accordingly.
+ avr_find_relocs_to_vectab (link_info, vtab);
+
+ int n_irqs = 0;
+ Elf_Internal_Rela *irelend = internal_relocs + sec->reloc_count;
+ for (Elf_Internal_Rela *irel = internal_relocs; irel < irelend; ++irel)
+ {
+ dprint ("Prune: reloc #%d off=%x, add=%x", (int) (irel - internal_relocs),
+ (int) irel->r_offset, (int) irel->r_addend);
+
+ if (irel->r_addend)
+ {
+ dprint ("\nPrune failed: non-zero addend\n");
+ return 0;
+ }
+
+ // The size in bytes of the entry.
+ const int sz = 0?0
+ : ELF32_R_TYPE (irel->r_info) == R_AVR_13_PCREL ? 2
+ : ELF32_R_TYPE (irel->r_info) == R_AVR_CALL ? 4
+ : 0;
+
+ // The sizes of all relocs must be the same, otherwise it's not
+ // an AVR-LibC vector table.
+ if (sz == 0
+ || (vtab->vec_size != 0 && sz != vtab->vec_size))
+ {
+ dprint ("\nPrune failed: unexpected reloc\n");
+ return 0;
+ }
+
+ vtab->vec_size = sz;
+
+ if (irel->r_offset % sz != 0
+ || irel->r_offset + sz > vtab->size)
+ {
+ dprint ("\nPrune failed: unexpected reloc offset\n");
+ return 0;
+ }
+
+ dprint (" %s", sz == 2 ? "R_AVR_13_PCREL" : "R_AVR_CALL");
+
+ const int idx = irel->r_offset / sz;
+ if (idx >= N_IRQS)
+ {
+ dprint ("\nPrune failed: vectab has more than %d entries\n", N_IRQS);
+ return 0;
+ }
+
+ avr_irq_t *irq = &vtab->irqs[idx];
+
+ if (irq->reloc != NULL)
+ {
+ dprint ("\nPrune failed: overlapping relocs\n");
+ return 0;
+ }
+
+ irq->idx = idx;
+ irq->reloc = irel;
+ n_irqs += 1;
+
+ if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+ {
+ dprint ("\nPrune failed: symbol is not global\n");
+ return 0;
+ }
+ else
+ {
+ // Symbol is external. Get its value.
+ unsigned long i = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+ struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[i];
+ BFD_ASSERT (h != NULL);
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ {
+ dprint ("\nPrune failed: symbol is not defined\n");
+ return 0;
+ }
+
+ irq->symval = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+
+ dprint (" symval=0x%x idx=%d", irq->symval, irq->idx);
+
+ const char *s_code = NULL;
+ const uint16_t code_word = avr_word (abfd, *pcontents + irel->r_offset);
+ if (vtab->vec_size == 2 && avr_is_RJMP (code_word))
+ s_code = "rjmp";
+ else if (vtab->vec_size == 4 && avr_is_JMP (code_word))
+ s_code = "jmp";
+ else
+ {
+ dprint ("\nPrune failed: unexpected opcode 0x%04x\n", code_word);
+ return 0;
+ }
+
+ // Get the address of this instruction.
+ irq->dot = sec->output_section->vma + sec->output_offset + irel->r_offset;
+
+ dprint (" %s .=0x%x", s_code, (unsigned) irq->dot);
+
+ if (idx == 0
+ || avr_local_label_at (abfd, sec, symtab_hdr, irel->r_offset)
+ || avr_global_label_at (abfd, sec, symtab_hdr, irel->r_offset))
+ irq->has_label_or_reloc = true;
+
+ if (irq->has_label_or_reloc)
+ dprint (" label_or_reloc");
+ if (irq->symval != (unsigned) vtab->bad_irq)
+ dprint (" required");
+
+ dprint ("\n");
+ } // Relocs
+
+ // Finally, we have all the checks done and data in place, so that
+ // we can perform the actual pruning.
+
+ if (n_irqs != vtab->n_irqs)
+ {
+ dprint ("Prune failed: incomplete vectab\n");
+ return 0;
+ }
+
+ int last_idx = 0;
+ for (int i = 0; i < n_irqs; ++i)
+ if (vtab->irqs[i].has_label_or_reloc
+ || vtab->irqs[i].symval != (unsigned) vtab->bad_irq)
+ last_idx = i;
+
+ const int n_delete = (n_irqs - 1 - last_idx) * vtab->vec_size;
+ dprint ("Prune: last remaining irq:%d, ", last_idx);
+ dprint ("pruning 0x%x=%d bytes from %d irqs\n",
+ n_delete, n_delete, n_delete / vtab->vec_size);
+ if (n_delete == 0)
+ return 0;
+
+ dprint ("Prune: remove irq");
+
+ elf_section_data (sec)->this_hdr.contents = *pcontents;
+
+ for (int i = n_irqs - 1; i > last_idx; --i)
+ {
+ const avr_irq_t *irq = &vtab->irqs[i];
+
+ dprint (" %d", irq->idx);
+ if (!elf32_avr_relax_delete_bytes (abfd, sec, irq->reloc->r_offset,
+ vtab->vec_size, true))
+ return -1;
+ // Avoid relocs to point outside of the section.
+ // This is needed even for R_AVR_NONE.
+ irq->reloc->r_offset = 0;
+ irq->reloc->r_info = R_AVR_NONE;
+ }
+
+ dprint ("\nPrune vectab accomplished\n");
+
+ return 1;
+}
+
+
/* This function handles relaxing for the avr.
Many important relaxing opportunities within functions are already
realized by the compiler itself. Though when the back end emits
@@ -2662,9 +3074,16 @@ elf32_avr_relax_section (bfd *abfd, asection *sec,
if (internal_relocs == NULL)
goto error_return;
+ const int mpv = avr_maybe_prune_vectab (abfd, link_info, sec,
+ internal_relocs, &contents);
+ if (mpv < 0)
+ goto error_return;
+ else if (mpv > 0)
+ *again = true;
+
/* Walk through the relocs looking for relaxing opportunities. */
irelend = internal_relocs + sec->reloc_count;
- for (irel = internal_relocs; irel < irelend; irel++)
+ for (irel = internal_relocs; irel < irelend && !mpv; irel++)
{
bfd_vma symval;
bool sym_is_global = false;
@@ -2835,8 +3254,19 @@ elf32_avr_relax_section (bfd *abfd, asection *sec,
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
R_AVR_13_PCREL);
+ // Allow to ditch the final NOP?
+ bfd_byte *pmaybe_clh = contents + irel->r_offset + 4;
+ const bool may_prune_last_vectab_entry
+ = (avr_prune_vectab
+ && !strcmp (sec->name, ".vectors")
+ && (irel->r_offset + 4 == sec->size
+ || (irel->r_offset + 6 == sec->size
+ && avr_is_CLH (avr_word (abfd, pmaybe_clh)))));
+
// We should not modify the ordering if 'shrinkable' is FALSE.
- if (!shrinkable)
+ if (!shrinkable
+ // ...except --prune-vectab allows it for the last entry.
+ && !may_prune_last_vectab_entry)
{
// Let's insert a NOP.
bfd_put_8 (abfd, 0x00, contents + irel->r_offset + 2);
@@ -3419,7 +3849,7 @@ elf32_avr_setup_params (struct bfd_link_info *info, bfd *avr_stub_bfd,
asection *avr_stub_section,
bool no_stubs, bool deb_stubs, bool deb_relax,
bfd_vma pc_wrap_around, bool call_ret_replacement,
- bool elide_rjmp0)
+ bool elide_rjmp0, bool prune_vectab)
{
elf32_avr_link_hash_table_t *htab = avr_link_hash_table (info);
@@ -3434,6 +3864,7 @@ elf32_avr_setup_params (struct bfd_link_info *info, bfd *avr_stub_bfd,
avr_pc_wrap_around = pc_wrap_around;
avr_replace_call_ret_sequences = call_ret_replacement;
avr_elide_rjmp0 = elide_rjmp0;
+ avr_prune_vectab = prune_vectab;
}
@@ -24,7 +24,8 @@
/* These four functions will be called from the ld back end. */
extern void elf32_avr_setup_params (struct bfd_link_info *, bfd *, asection *,
- bool, bool, bool, bfd_vma, bool, bool);
+ bool, bool, bool, bfd_vma, bool, bool,
+ bool);
extern int elf32_avr_setup_section_lists (bfd *, struct bfd_link_info *);
extern bool elf32_avr_size_stubs (bfd *, struct bfd_link_info *, bool);
extern bool elf32_avr_build_stubs (struct bfd_link_info *);
@@ -44,6 +44,7 @@ static bool avr_debug_relax = false;
static bool avr_debug_stubs = false;
static bool avr_replace_call_ret_sequences = true;
static bool avr_elide_rjmp0 = true;
+static bool avr_prune_vectab = false;
static bfd_vma avr_pc_wrap_around = 0x10000000;
/* Transfers information to the bfd frontend. */
@@ -59,7 +60,8 @@ avr_elf_set_global_bfd_parameters (void)
avr_debug_relax,
avr_pc_wrap_around,
avr_replace_call_ret_sequences,
- avr_elide_rjmp0);
+ avr_elide_rjmp0,
+ avr_prune_vectab);
}
@@ -241,6 +243,8 @@ PARSE_AND_LIST_LONGOPTS='
NULL, OPTION_NO_ELIDE_RJMP0},
{ "pmem-wrap-around", required_argument,
NULL, OPTION_PMEM_WRAP_AROUND},
+ { "prune-vectab", no_argument,
+ NULL, OPTION_PRUNE_VECTAB },
{ "no-stubs", no_argument,
NULL, OPTION_NO_STUBS},
{ "debug-stubs", no_argument,
@@ -264,7 +268,7 @@ PARSE_AND_LIST_OPTIONS='
" instructions by a single jump instruction.\n"
" "
" This option disables this optimization.\n"));
- fprintf (file, _(" --no-elide-rjmp0 "
+ fprintf (file, _(" --no-elide-rjmp0 "
"The relaxation machine normally will\n"
" "
" remove an rjmp instruction when it targets a\n"
@@ -272,6 +276,10 @@ PARSE_AND_LIST_OPTIONS='
" global symbol at a jump offset of 0.\n"
" "
" This option disables this optimization.\n"));
+ fprintf (file, _(" --prune-vectab "
+ "Prune AVR-LibC vector table provided relaxation\n"
+ " "
+ " is on.\n"));
fprintf (file, _(" --no-stubs "
"If the linker detects to attempt to access\n"
" "
@@ -329,6 +337,11 @@ PARSE_AND_LIST_ARGS_CASES='
avr_elide_rjmp0 = false;
}
break;
+
+ case OPTION_PRUNE_VECTAB:
+ /* This variable is defined in the bfd library. */
+ avr_prune_vectab = true;
+ break;
'
#
@@ -272,6 +272,7 @@ enum option_values
OPTION_NO_CALL_RET_REPLACEMENT,
OPTION_NO_ELIDE_RJMP0,
OPTION_PMEM_WRAP_AROUND,
+ OPTION_PRUNE_VECTAB,
OPTION_NO_STUBS,
OPTION_DEBUG_STUBS,
OPTION_DEBUG_RELAX,
new file mode 100644
@@ -0,0 +1,2 @@
+;;; empty
+
new file mode 100644
@@ -0,0 +1,19 @@
+#name: Prune RJMP vectab, no code
+#source: vectab-rjmp.s
+#source: prune-vtab-1.s
+#target: avr-*-*
+#as: -mavr4 -I$srcdir/$subdir
+#ld: -mavr4 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: f8 94 cli
+
+00000002 <__bad_interrupt>:
+ 2: fe cf rjmp .-4 ; 0x0 <__vectors>
+
new file mode 100644
@@ -0,0 +1,18 @@
+#name: Prune JMP vectab, no code
+#source: vectab-jmp.s
+#source: prune-vtab-1.s
+#target: avr-*-*
+#as: -mavr51 -I$srcdir/$subdir
+#ld: -mavr51 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: f8 94 cli
+
+00000002 <__bad_interrupt>:
+ 2: fe cf rjmp .-4 ; 0x0 <__vectors>
new file mode 100644
@@ -0,0 +1,22 @@
+#name: Prune JMP+CLH vectab, no code
+#source: vectab-jmp-clh.s
+#source: prune-vtab-1.s
+#target: avr-*-*
+#as: -mavr51 -I$srcdir/$subdir
+#ld: -mavr51 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: 01 c0 rjmp .+2 ; 0x4 <__ctors_end>
+ 2: d8 94 clh
+
+00000004 <__ctors_end>:
+ 4: f8 94 cli
+
+00000006 <__bad_interrupt>:
+ 6: fc cf rjmp .-8 ; 0x0 <__vectors>
new file mode 100644
@@ -0,0 +1,4 @@
+.global __vector_2
+.type __vector_2, @function
+__vector_2:
+ reti
new file mode 100644
@@ -0,0 +1,26 @@
+#name: Prune RJMP vectab, one ISR
+#source: vectab-rjmp.s
+#source: prune-vtab-2.s
+#target: avr-*-*
+#as: -mavr4 -I$srcdir/$subdir
+#ld: -mavr4 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: 02 c0 rjmp .+4 ; 0x6 <__ctors_end>
+ 2: 02 c0 rjmp .+4 ; 0x8 <__bad_interrupt>
+ 4: 02 c0 rjmp .+4 ; 0xa <__vector_2>
+
+00000006 <__ctors_end>:
+ 6: f8 94 cli
+
+00000008 <__bad_interrupt>:
+ 8: fb cf rjmp .-10 ; 0x0 <__vectors>
+
+0000000a <__vector_2>:
+ a: 18 95 reti
new file mode 100644
@@ -0,0 +1,28 @@
+#name: Prune JMP vectab, one ISR
+#source: vectab-jmp.s
+#source: prune-vtab-2.s
+#target: avr-*-*
+#as: -mavr51 -I$srcdir/$subdir
+#ld: -mavr51 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: 04 c0 rjmp .+8 ; 0xa <__ctors_end>
+ 2: 00 00 nop
+ 4: 03 c0 rjmp .+6 ; 0xc <__bad_interrupt>
+ 6: 00 00 nop
+ 8: 02 c0 rjmp .+4 ; 0xe <__vector_2>
+
+0000000a <__ctors_end>:
+ a: f8 94 cli
+
+0000000c <__bad_interrupt>:
+ c: f9 cf rjmp .-14 ; 0x0 <__vectors>
+
+0000000e <__vector_2>:
+ e: 18 95 reti
new file mode 100644
@@ -0,0 +1,29 @@
+#name: Prune JMP+CLH vectab, one ISR
+#source: vectab-jmp-clh.s
+#source: prune-vtab-2.s
+#target: avr-*-*
+#as: -mavr51 -I$srcdir/$subdir
+#ld: -mavr51 --relax --prune-vectab
+#objdump: -d
+
+.*: file format elf32-avr
+
+
+Disassembly of section .text:
+
+00000000 <__vectors>:
+ 0: 05 c0 rjmp .+10 ; 0xc <__ctors_end>
+ 2: 00 00 nop
+ 4: 04 c0 rjmp .+8 ; 0xe <__bad_interrupt>
+ 6: 00 00 nop
+ 8: 03 c0 rjmp .+6 ; 0x10 <__vector_2>
+ a: d8 94 clh
+
+0000000c <__ctors_end>:
+ c: f8 94 cli
+
+0000000e <__bad_interrupt>:
+ e: f8 cf rjmp .-16 ; 0x0 <__vectors>
+
+00000010 <__vector_2>:
+ 10: 18 95 reti
new file mode 100644
@@ -0,0 +1,9 @@
+.macro XJMP x
+ jmp \x
+.endm
+
+.macro XCLH
+ clh
+.endm
+
+.include "vectab.h"
new file mode 100644
@@ -0,0 +1,9 @@
+.macro XJMP x
+ jmp \x
+.endm
+
+.macro XCLH
+ ;; clh
+.endm
+
+.include "vectab.h"
new file mode 100644
@@ -0,0 +1,9 @@
+.macro XJMP x
+ rjmp \x
+.endm
+
+.macro XCLH
+ ;; clh
+.endm
+
+.include "vectab.h"
new file mode 100644
@@ -0,0 +1,32 @@
+.macro vector name
+ .weak \name
+ .set \name, __bad_interrupt
+ XJMP \name
+.endm
+
+ .section .vectors,"ax",@progbits
+ .global __vectors
+ .type __vectors,@function
+__vectors:
+ XJMP __init
+ vector __vector_1
+ vector __vector_2
+ vector __vector_3
+ vector __vector_4
+ vector __vector_5
+ vector __vector_6
+ XCLH
+
+ .text
+ .global __bad_interrupt
+ .func __bad_interrupt
+__bad_interrupt:
+ .weak __vector_default
+ .set __vector_default, __vectors
+ XJMP __vector_default
+ .endfunc
+
+ .section .init0,"ax",@progbits
+ .weak __init
+__init:
+ cli