aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog20
-rw-r--r--gas/config/tc-avr.c130
-rw-r--r--gas/config/tc-avr.h23
-rw-r--r--gas/testsuite/ChangeLog6
-rw-r--r--gas/testsuite/gas/avr/diffreloc_withrelax.d16
-rw-r--r--gas/testsuite/gas/avr/noreloc_withoutrelax.d11
-rw-r--r--gas/testsuite/gas/avr/relax.s12
7 files changed, 215 insertions, 3 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 326b47c..d7e5b4a 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,23 @@
+2014-04-10 Senthil Kumar Selvaraj <senthil_kumar.selvaraj@atmel.com>
+
+ * config/tc-avr.c: Add new flag mlink-relax.
+ (md_show_usage): Add flag and help text.
+ (md_parse_option): Record whether link relax is turned on.
+ (relaxable_section): New.
+ (avr_validate_fix_sub): New.
+ (avr_force_relocation): New.
+ (md_apply_fix): Generate DIFF reloc.
+ (avr_allow_local_subtract): New.
+
+ * config/tc-avr.h (TC_LINKRELAX_FIXUP): Define to 0.
+ (TC_FORCE_RELOCATION): Define.
+ (TC_FORCE_RELOCATION_SUB_SAME): Define.
+ (TC_VALIDATE_FIX_SUB): Define.
+ (avr_force_relocation): Declare.
+ (avr_validate_fix_sub): Declare.
+ (md_allow_local_subtract): Define.
+ (avr_allow_local_subtract): Declare.
+
2014-04-10 Andrew Bennett <andrew.bennett@imgtec.com>
* config/tc-mips.c (mips_cpu_info_table): Add P5600
diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c
index ce9708e..e4bc59c 100644
--- a/gas/config/tc-avr.c
+++ b/gas/config/tc-avr.c
@@ -338,9 +338,11 @@ struct avr_opt_s
int all_opcodes; /* -mall-opcodes: accept all known AVR opcodes. */
int no_skip_bug; /* -mno-skip-bug: no warnings for skipping 2-word insns. */
int no_wrap; /* -mno-wrap: reject rjmp/rcall with 8K wrap-around. */
+ int link_relax; /* -mlink-relax: generate relocations for linker
+ relaxation. */
};
-static struct avr_opt_s avr_opt = { 0, 0, 0 };
+static struct avr_opt_s avr_opt = { 0, 0, 0, 0 };
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
@@ -401,7 +403,8 @@ enum options
OPTION_ALL_OPCODES = OPTION_MD_BASE + 1,
OPTION_NO_SKIP_BUG,
OPTION_NO_WRAP,
- OPTION_ISA_RMW
+ OPTION_ISA_RMW,
+ OPTION_LINK_RELAX
};
struct option md_longopts[] =
@@ -411,6 +414,7 @@ struct option md_longopts[] =
{ "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG },
{ "mno-wrap", no_argument, NULL, OPTION_NO_WRAP },
{ "mrmw", no_argument, NULL, OPTION_ISA_RMW },
+ { "mlink-relax", no_argument, NULL, OPTION_LINK_RELAX },
{ NULL, no_argument, NULL, 0 }
};
@@ -517,6 +521,7 @@ md_show_usage (FILE *stream)
" -mno-wrap reject rjmp/rcall instructions with 8K wrap-around\n"
" (default for avr3, avr5)\n"
" -mrmw accept Read-Modify-Write instructions\n"
+ " -mlink-relax generate relocations for linker relaxation\n"
));
show_mcu_list (stream);
}
@@ -587,6 +592,9 @@ md_parse_option (int c, char *arg)
case OPTION_ISA_RMW:
specified_mcu.isa |= AVR_ISA_RMW;
return 1;
+ case OPTION_LINK_RELAX:
+ avr_opt.link_relax = 1;
+ return 1;
}
return 0;
@@ -637,6 +645,7 @@ md_begin (void)
}
bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+ linkrelax = avr_opt.link_relax;
}
/* Resolve STR as a constant expression and return the result.
@@ -1200,6 +1209,53 @@ md_pcrel_from_section (fixS *fixp, segT sec)
return fixp->fx_frag->fr_address + fixp->fx_where;
}
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+ return (sec->flags & SEC_DEBUGGING) == 0;
+}
+
+/* Does whatever the xtensa port does. */
+int
+avr_validate_fix_sub (fixS *fix)
+{
+ segT add_symbol_segment, sub_symbol_segment;
+
+ /* The difference of two symbols should be resolved by the assembler when
+ linkrelax is not set. If the linker may relax the section containing
+ the symbols, then an Xtensa DIFF relocation must be generated so that
+ the linker knows to adjust the difference value. */
+ if (!linkrelax || fix->fx_addsy == NULL)
+ return 0;
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. If the segment is not "normal", then the
+ fix is not valid. If the segment is not "relaxable", then the fix
+ should have been handled earlier. */
+ add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+ if (! SEG_NORMAL (add_symbol_segment) ||
+ ! relaxable_section (add_symbol_segment))
+ return 0;
+
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment);
+}
+
+/* TC_FORCE_RELOCATION hook */
+
+/* If linkrelax is turned on, and the symbol to relocate
+ against is in a relaxable segment, don't compute the value -
+ generate a relocation instead. */
+int
+avr_force_relocation (fixS *fix)
+{
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
/* GAS will call this for each fixup. It should store the correct
value in the object file. */
@@ -1223,11 +1279,47 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
fixP->fx_done = 1;
}
}
-
+ else if (linkrelax && fixP->fx_subsy)
+ {
+ /* For a subtraction relocation expression, generate one
+ of the DIFF relocs, with the value being the difference.
+ Note that a sym1 - sym2 expression is adjusted into a
+ section_start_sym + sym4_offset_from_section_start - sym1
+ expression. fixP->fx_addsy holds the section start symbol,
+ fixP->fx_offset holds sym2's offset, and fixP->fx_subsy
+ holds sym1. Calculate the current difference and write value,
+ but leave fx_offset as is - during relaxation,
+ fx_offset - value gives sym1's value */
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF8;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF16;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF32;
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ break;
+ }
+
+ value = S_GET_VALUE (fixP->fx_addsy) +
+ fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
+
+ fixP->fx_subsy = NULL;
+ }
/* We don't actually support subtracting a symbol. */
if (fixP->fx_subsy != (symbolS *) NULL)
as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ /* For the DIFF relocs, write the value into the object file while still
+ keeping fx_done FALSE, as both the difference (recorded in the object file)
+ and the sym offset (part of fixP) are needed at link relax time */
+ where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
switch (fixP->fx_r_type)
{
default:
@@ -1237,6 +1329,16 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg)
case BFD_RELOC_AVR_13_PCREL:
case BFD_RELOC_32:
case BFD_RELOC_16:
+ break;
+ case BFD_RELOC_AVR_DIFF8:
+ *where = value;
+ break;
+ case BFD_RELOC_AVR_DIFF16:
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+ case BFD_RELOC_AVR_DIFF32:
+ bfd_putl32 ((bfd_vma) value, where);
+ break;
case BFD_RELOC_AVR_CALL:
break;
}
@@ -1654,3 +1756,25 @@ tc_cfi_frame_initial_instructions (void)
do not line up the same way as for targers that use pre-decrement. */
cfi_add_CFA_offset (DWARF2_DEFAULT_RETURN_COLUMN, 1-return_size);
}
+
+bfd_boolean
+avr_allow_local_subtract (expressionS * left,
+ expressionS * right,
+ segT section)
+{
+ /* If we are not in relaxation mode, subtraction is OK. */
+ if (!linkrelax)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ if (left->X_add_symbol == right->X_add_symbol)
+ return TRUE;
+
+ /* We have to assume that there may be instructions between the
+ two symbols and that relaxation may increase the distance between
+ them. */
+ return FALSE;
+}
diff --git a/gas/config/tc-avr.h b/gas/config/tc-avr.h
index df75ac7..fb596ad 100644
--- a/gas/config/tc-avr.h
+++ b/gas/config/tc-avr.h
@@ -112,6 +112,18 @@ extern void avr_cons_fix_new (fragS *,int, int, expressionS *,
visible symbols can be overridden. */
#define EXTERN_FORCE_RELOC 0
+/* If defined, this macro allows control over whether fixups for a
+ given section will be processed when the linkrelax variable is
+ set. Define it to zero and handle things in md_apply_fix instead.*/
+#define TC_LINKRELAX_FIXUP(SEG) 0
+
+/* If this macro returns non-zero, it guarantees that a relocation will be emitted
+ even when the value can be resolved locally. Do that if linkrelax is turned on */
+#define TC_FORCE_RELOCATION(fix) avr_force_relocation (fix)
+#define TC_FORCE_RELOCATION_SUB_SAME(fix, seg) \
+ (! SEG_NORMAL (seg) || avr_force_relocation (fix))
+extern int avr_force_relocation (struct fix *);
+
/* Values passed to md_apply_fix don't include the symbol value. */
#define MD_APPLY_SYM_VALUE(FIX) 0
@@ -169,6 +181,12 @@ extern long md_pcrel_from_section (struct fix *, segT);
goto SKIP; \
}
+/* This macro is evaluated for any fixup with a fx_subsy that
+ fixup_segment cannot reduce to a number. If the macro returns
+ false an error will be reported. */
+#define TC_VALIDATE_FIX_SUB(fix, seg) avr_validate_fix_sub (fix)
+extern int avr_validate_fix_sub (struct fix *);
+
/* This target is buggy, and sets fix size too large. */
#define TC_FX_SIZE_SLACK(FIX) 2
@@ -190,3 +208,8 @@ extern long md_pcrel_from_section (struct fix *, segT);
/* Define a hook to setup initial CFI state. */
extern void tc_cfi_frame_initial_instructions (void);
#define tc_cfi_frame_initial_instructions tc_cfi_frame_initial_instructions
+
+/* The difference between same-section symbols may be affected by linker
+ relaxation, so do not resolve such expressions in the assembler. */
+#define md_allow_local_subtract(l,r,s) avr_allow_local_subtract (l, r, s)
+extern bfd_boolean avr_allow_local_subtract (expressionS *, expressionS *, segT);
diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog
index 61b42dd..30d957d 100644
--- a/gas/testsuite/ChangeLog
+++ b/gas/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2014-04-10 Senthil Kumar Selvaraj <senthil_kumar.selvaraj@atmel.com>
+
+ * gas/avr/diffreloc_withrelax.d: New testcase.
+ * gas/avr/noreloc_withoutrelax.d: Likewise.
+ * gas/avr/relax.s: Likewise.
+
2014-04-04 Ilya Tocar <ilya.tocar@intel.com>
* gas/i386/i386.exp: Run SE1 tests.
diff --git a/gas/testsuite/gas/avr/diffreloc_withrelax.d b/gas/testsuite/gas/avr/diffreloc_withrelax.d
new file mode 100644
index 0000000..6d5bd2e
--- /dev/null
+++ b/gas/testsuite/gas/avr/diffreloc_withrelax.d
@@ -0,0 +1,16 @@
+#name: AVR DIFF relocs with link relax
+#as: -mmcu=avrxmega2 -mlink-relax
+#source: relax.s
+#objdump: -r
+#target: avr-*-*
+
+.*: file format elf32-avr
+
+RELOCATION RECORDS FOR \[.text\]:
+OFFSET TYPE VALUE
+00000000 R_AVR_CALL .text
+
+
+RELOCATION RECORDS FOR \[.data\]:
+OFFSET TYPE VALUE
+00000000 R_AVR_DIFF16 .text\+0x00000004
diff --git a/gas/testsuite/gas/avr/noreloc_withoutrelax.d b/gas/testsuite/gas/avr/noreloc_withoutrelax.d
new file mode 100644
index 0000000..daaaeb2
--- /dev/null
+++ b/gas/testsuite/gas/avr/noreloc_withoutrelax.d
@@ -0,0 +1,11 @@
+#name: AVR no DIFF relocs without link relax
+#as: -mmcu=avrxmega2
+#objdump: -r
+#source: relax.s
+#target: avr-*-*
+
+.*: file format elf32-avr
+
+RELOCATION RECORDS FOR \[.text\]:
+OFFSET TYPE VALUE
+00000000 R_AVR_CALL .text
diff --git a/gas/testsuite/gas/avr/relax.s b/gas/testsuite/gas/avr/relax.s
new file mode 100644
index 0000000..dc6b262
--- /dev/null
+++ b/gas/testsuite/gas/avr/relax.s
@@ -0,0 +1,12 @@
+ .file "diffreloc.s"
+.section .text,"ax",@progbits
+main:
+L1:
+ jmp L1
+L2:
+.global x
+ .section .data
+ .type x, @object
+ .size x, 2
+x:
+ .word L2 - L1