aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2016-12-18 22:53:48 -0800
committerAlan Modra <amodra@gmail.com>2016-12-20 12:26:33 +1030
commit45f764234a71431b581340957a3c8338e0593fdb (patch)
tree15fdb770f8d357dde9e570452a2afed2ba0772d0 /gas
parentFormatting changes for RISC-V (diff)
downloadbinutils-gdb-45f764234a71431b581340957a3c8338e0593fdb.tar.gz
binutils-gdb-45f764234a71431b581340957a3c8338e0593fdb.tar.bz2
binutils-gdb-45f764234a71431b581340957a3c8338e0593fdb.zip
Rework RISC-V relocations
Before this commit we didn't cleanly support CFI directives because the internal offsets used to get relaxed which broke them. This patch significantly reworks how we handle linker relaxations: * DWARF is now properly supported * There is a ".option norelax" to disable relaxations, for when users write assembly that can't be relaxed (if it's to be later patched up, for example). * There is an additional _RELAX relocation that specifies when previous relocations can be relaxed. We're in the process of documenting the RISC-V ELF ABI, which will include documentation of our relocations https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md but we expect that this relocation set will remain ABI compatible in the future (ie, it's safe to release). Thanks to Kuan-Lin Chen for figuring out how to correctly relax the debug info! include/ * elf/riscv.h: Add R_RISCV_TPREL_I through R_RISCV_SET32. bfd/ * reloc.c (BFD_RELOC_RISCV_TPREL_I): New relocation. (BFD_RELOC_RISCV_TPREL_S): Likewise. (BFD_RELOC_RISCV_RELAX): Likewise. (BFD_RELOC_RISCV_CFA): Likewise. (BFD_RELOC_RISCV_SUB6): Likewise. (BFD_RELOC_RISCV_SET8): Likewise. (BFD_RELOC_RISCV_SET8): Likewise. (BFD_RELOC_RISCV_SET16): Likewise. (BFD_RELOC_RISCV_SET32): Likewise. * elfnn-riscv.c (perform_relocation): Handle the new relocations. (_bfd_riscv_relax_tls_le): Likewise. (_bfd_riscv_relax_align): Likewise. (_bfd_riscv_relax_section): Likewise. (howto_table): Likewise. (riscv_reloc_map): Likewise. (relax_func_t): New type. (_bfd_riscv_relax_call): Add reserve_size argument, which controls the maximal offset pessimism. Correct type of max_alignment. (_bfd_riscv_relax_lui): Likewise. (_bfd_riscv_relax_tls_le): Likewise. (_bfd_riscv_relax_align): Likewise. (_bfd_riscv_relax_section): Compute the required reserve size when relocating and use it to when calling relax_func. * bfd-in2.h: Regenerate. * libbfd.h: Likewise. gas/ * config/tc-riscv.c (riscv_set_options): Add relax. (riscv_opts): Likewise. (s_riscv_option): Add relax and norelax. (riscv_apply_const_reloc): New function. (append_insn): Move constant relocation handling to riscv_apply_const_reloc. (md_pcrel_from): Likewise. (parse_relocation): Skip BFD_RELOC_UNUSED. (md_pcrel_from): Handle BFD_RELOC_RISCV_SUB6, BFD_RELOC_RISCV_RELAX, BFD_RELOC_RISCV_CFA. (md_apply_fix): Likewise. (riscv_pre_output_hook): New function. * config/tc-riscv.h (md_pre_output_hook): Define. (riscv_pre_output_hook): Declare. (DWARF_CIE_DATA_ALIGNMENT): Always -4.
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog19
-rw-r--r--gas/config/tc-riscv.c213
-rw-r--r--gas/config/tc-riscv.h7
3 files changed, 185 insertions, 54 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 36f1c2fb573..0e652b13469 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,4 +1,23 @@
2016-12-20 Andrew Waterman <andrew@sifive.com>
+ Kuan-Lin Chen <kuanlinchentw@gmail.com>
+
+ * config/tc-riscv.c (riscv_set_options): Add relax.
+ (riscv_opts): Likewise.
+ (s_riscv_option): Add relax and norelax.
+ (riscv_apply_const_reloc): New function.
+ (append_insn): Move constant relocation handling to
+ riscv_apply_const_reloc.
+ (md_pcrel_from): Likewise.
+ (parse_relocation): Skip BFD_RELOC_UNUSED.
+ (md_pcrel_from): Handle BFD_RELOC_RISCV_SUB6,
+ BFD_RELOC_RISCV_RELAX, BFD_RELOC_RISCV_CFA.
+ (md_apply_fix): Likewise.
+ (riscv_pre_output_hook): New function.
+ * config/tc-riscv.h (md_pre_output_hook): Define.
+ (riscv_pre_output_hook): Declare.
+ (DWARF_CIE_DATA_ALIGNMENT): Always -4.
+
+2016-12-20 Andrew Waterman <andrew@sifive.com>
* config/tc-riscv.c: Formatting and comment fixes throughout.
diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index d8a627d14ab..d011864fd70 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -74,12 +74,14 @@ struct riscv_set_options
{
int pic; /* Generate position-independent code. */
int rvc; /* Generate RVC code. */
+ int relax; /* Emit relocs the linker is allowed to relax. */
};
static struct riscv_set_options riscv_opts =
{
0, /* pic */
0, /* rvc */
+ 1, /* relax */
};
static void
@@ -648,6 +650,28 @@ md_begin (void)
record_alignment (text_section, riscv_opts.rvc ? 1 : 2);
}
+static insn_t
+riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value)
+{
+ switch (reloc_type)
+ {
+ case BFD_RELOC_32:
+ return value;
+
+ case BFD_RELOC_RISCV_HI20:
+ return ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
+
+ case BFD_RELOC_RISCV_LO12_S:
+ return ENCODE_STYPE_IMM (value);
+
+ case BFD_RELOC_RISCV_LO12_I:
+ return ENCODE_ITYPE_IMM (value);
+
+ default:
+ abort ();
+ }
+}
+
/* Output an instruction. IP is the instruction information.
ADDRESS_EXPR is an operand of the instruction to be used with
RELOC_TYPE. */
@@ -676,43 +700,22 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr,
return;
}
else if (address_expr->X_op == O_constant)
+ ip->insn_opcode |= riscv_apply_const_reloc (reloc_type,
+ address_expr->X_add_number);
+ else
{
- switch (reloc_type)
- {
- case BFD_RELOC_32:
- ip->insn_opcode |= address_expr->X_add_number;
- goto append;
+ howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
+ if (howto == NULL)
+ as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
- case BFD_RELOC_RISCV_HI20:
- {
- insn_t imm = RISCV_CONST_HIGH_PART (address_expr->X_add_number);
- ip->insn_opcode |= ENCODE_UTYPE_IMM (imm);
- goto append;
- }
-
- case BFD_RELOC_RISCV_LO12_S:
- ip->insn_opcode |= ENCODE_STYPE_IMM (address_expr->X_add_number);
- goto append;
+ ip->fixp = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr, FALSE, reloc_type);
- case BFD_RELOC_RISCV_LO12_I:
- ip->insn_opcode |= ENCODE_ITYPE_IMM (address_expr->X_add_number);
- goto append;
-
- default:
- break;
- }
+ ip->fixp->fx_tcbit = riscv_opts.relax;
}
-
- howto = bfd_reloc_type_lookup (stdoutput, reloc_type);
- if (howto == NULL)
- as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type);
-
- ip->fixp = fix_new_exp (ip->frag, ip->where,
- bfd_get_reloc_size (howto),
- address_expr, FALSE, reloc_type);
}
-append:
add_fixed_insn (ip);
install_insn (ip);
}
@@ -1085,7 +1088,8 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc,
/* Check whether the output BFD supports this relocation.
If not, issue an error and fall back on something safe. */
- if (!bfd_reloc_type_lookup (stdoutput, percent_op->reloc))
+ if (*reloc != BFD_RELOC_UNUSED
+ && !bfd_reloc_type_lookup (stdoutput, *reloc))
{
as_bad ("relocation %s isn't supported by the current ABI",
percent_op->str);
@@ -1826,45 +1830,56 @@ md_pcrel_from (fixS *fixP)
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
+ unsigned int subtype;
bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+ bfd_boolean relaxable = FALSE;
/* Remember value for tc_gen_reloc. */
fixP->fx_addnumber = *valP;
switch (fixP->fx_r_type)
{
- case BFD_RELOC_RISCV_TLS_GOT_HI20:
- case BFD_RELOC_RISCV_TLS_GD_HI20:
- case BFD_RELOC_RISCV_TLS_DTPREL32:
- case BFD_RELOC_RISCV_TLS_DTPREL64:
- case BFD_RELOC_RISCV_TPREL_HI20:
- case BFD_RELOC_RISCV_TPREL_LO12_I:
- case BFD_RELOC_RISCV_TPREL_LO12_S:
- case BFD_RELOC_RISCV_TPREL_ADD:
- S_SET_THREAD_LOCAL (fixP->fx_addsy);
- /* Fall through. */
-
- case BFD_RELOC_RISCV_GOT_HI20:
- case BFD_RELOC_RISCV_PCREL_HI20:
case BFD_RELOC_RISCV_HI20:
case BFD_RELOC_RISCV_LO12_I:
case BFD_RELOC_RISCV_LO12_S:
+ bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP)
+ | bfd_getl32 (buf), buf);
+ relaxable = TRUE;
+ break;
+
+ case BFD_RELOC_RISCV_GOT_HI20:
+ case BFD_RELOC_RISCV_PCREL_HI20:
case BFD_RELOC_RISCV_ADD8:
case BFD_RELOC_RISCV_ADD16:
case BFD_RELOC_RISCV_ADD32:
case BFD_RELOC_RISCV_ADD64:
+ case BFD_RELOC_RISCV_SUB6:
case BFD_RELOC_RISCV_SUB8:
case BFD_RELOC_RISCV_SUB16:
case BFD_RELOC_RISCV_SUB32:
case BFD_RELOC_RISCV_SUB64:
- gas_assert (fixP->fx_addsy != NULL);
- /* Nothing needed to do. The value comes from the reloc entry. */
+ case BFD_RELOC_RISCV_RELAX:
+ break;
+
+ case BFD_RELOC_RISCV_TPREL_HI20:
+ case BFD_RELOC_RISCV_TPREL_LO12_I:
+ case BFD_RELOC_RISCV_TPREL_LO12_S:
+ case BFD_RELOC_RISCV_TPREL_ADD:
+ relaxable = TRUE;
+ /* Fall through. */
+
+ case BFD_RELOC_RISCV_TLS_GOT_HI20:
+ case BFD_RELOC_RISCV_TLS_GD_HI20:
+ case BFD_RELOC_RISCV_TLS_DTPREL32:
+ case BFD_RELOC_RISCV_TLS_DTPREL64:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
break;
case BFD_RELOC_64:
case BFD_RELOC_32:
case BFD_RELOC_16:
case BFD_RELOC_8:
+ case BFD_RELOC_RISCV_CFA:
if (fixP->fx_addsy && fixP->fx_subsy)
{
fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
@@ -1895,6 +1910,49 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
break;
+ case BFD_RELOC_RISCV_CFA:
+ /* Load the byte to get the subtype. */
+ subtype = bfd_get_8 (NULL, &fixP->fx_frag->fr_literal[fixP->fx_where]);
+ switch (subtype)
+ {
+ case DW_CFA_advance_loc1:
+ fixP->fx_where++;
+ fixP->fx_next->fx_where++;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET8;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8;
+ break;
+
+ case DW_CFA_advance_loc2:
+ fixP->fx_size = 2;
+ fixP->fx_where++;
+ fixP->fx_next->fx_size = 2;
+ fixP->fx_next->fx_where++;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET16;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16;
+ break;
+
+ case DW_CFA_advance_loc4:
+ fixP->fx_size = 4;
+ fixP->fx_where++;
+ fixP->fx_next->fx_size = 4;
+ fixP->fx_next->fx_where++;
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET32;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32;
+ break;
+
+ default:
+ if (subtype < 0x80 && (subtype & 0x40))
+ {
+ /* DW_CFA_advance_loc */
+ fixP->fx_r_type = BFD_RELOC_RISCV_SET6;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6;
+ }
+ else
+ as_fatal (_("internal error: bad CFA value #%d"), subtype);
+ break;
+ }
+ break;
+
default:
/* This case is unreachable. */
abort ();
@@ -1954,10 +2012,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
}
break;
- case BFD_RELOC_RISCV_PCREL_LO12_S:
- case BFD_RELOC_RISCV_PCREL_LO12_I:
case BFD_RELOC_RISCV_CALL:
case BFD_RELOC_RISCV_CALL_PLT:
+ relaxable = TRUE;
+ break;
+
+ case BFD_RELOC_RISCV_PCREL_LO12_S:
+ case BFD_RELOC_RISCV_PCREL_LO12_I:
case BFD_RELOC_RISCV_ALIGN:
break;
@@ -1966,8 +2027,54 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL)
as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type);
}
+
+ /* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */
+ if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL)
+ {
+ fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
+ fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL;
+ fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX;
+ }
}
+/* Because the value of .cfi_remember_state may changed after relaxation,
+ we insert a fix to relocate it again in link-time. */
+
+void
+riscv_pre_output_hook (void)
+{
+ const frchainS *frch;
+ const asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next)
+ {
+ const fragS *frag;
+
+ for (frag = frch->frch_root; frag; frag = frag->fr_next)
+ {
+ if (frag->fr_type == rs_cfa)
+ {
+ const fragS *loc4_frag;
+ expressionS exp;
+
+ symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol;
+ symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol;
+
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = add_symbol;
+ exp.X_add_number = 0;
+ exp.X_op_symbol = op_symbol;
+
+ loc4_frag = (fragS *) frag->fr_opcode;
+ fix_new_exp (loc4_frag, (int) frag->fr_offset, 1, &exp, 0,
+ BFD_RELOC_RISCV_CFA);
+ }
+ }
+ }
+}
+
+
/* This structure is used to hold a stack of .option values. */
struct riscv_option_stack
@@ -1998,10 +2105,10 @@ s_riscv_option (int x ATTRIBUTE_UNUSED)
riscv_opts.pic = TRUE;
else if (strcmp (name, "nopic") == 0)
riscv_opts.pic = FALSE;
- else if (strcmp (name, "soft-float") == 0)
- float_mode = FLOAT_MODE_SOFT;
- else if (strcmp (name, "hard-float") == 0)
- float_mode = FLOAT_MODE_HARD;
+ else if (strcmp (name, "relax") == 0)
+ riscv_opts.relax = TRUE;
+ else if (strcmp (name, "norelax") == 0)
+ riscv_opts.relax = FALSE;
else if (strcmp (name, "push") == 0)
{
struct riscv_option_stack *s;
diff --git a/gas/config/tc-riscv.h b/gas/config/tc-riscv.h
index c2a11cefe9a..32cf3eea403 100644
--- a/gas/config/tc-riscv.h
+++ b/gas/config/tc-riscv.h
@@ -61,6 +61,9 @@ extern void riscv_after_parse_args (void);
#define md_parse_long_option(arg) riscv_parse_long_option (arg)
extern int riscv_parse_long_option (const char *);
+#define md_pre_output_hook riscv_pre_output_hook()
+extern void riscv_pre_output_hook (void);
+
/* Let the linker resolve all the relocs due to relaxation. */
#define tc_fix_adjustable(fixp) 0
#define md_allow_local_subtract(l,r,s) 0
@@ -93,7 +96,9 @@ extern int tc_riscv_regname_to_dw2regnum (char *);
extern unsigned xlen;
#define DWARF2_DEFAULT_RETURN_COLUMN X_RA
-#define DWARF2_CIE_DATA_ALIGNMENT (-(int) (xlen / 8))
+
+/* Even on RV64, use 4-byte alignment, as F registers may be only 32 bits. */
+#define DWARF2_CIE_DATA_ALIGNMENT -4
#define elf_tc_final_processing riscv_elf_final_processing
extern void riscv_elf_final_processing (void);