mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
4bf094292c
Many places report errors of the nature "can't resolve a - b". This provides a utility function to report such errors consistently. I removed the section reporting and quotes around symbol names while I was at it. Compare ifunc-2.s:4: Error: can't resolve `bar1' {.text.1 section} - `foo1' {.text.1 section} with ifunc-2.s:4: Error: can't resolve bar1 - foo1 In many cases the section names don't help the user very much in figuring out what went wrong, and the quotes if present arguably ought to be placed around the entire expression: can't resolve `bar1 - foo1' The patch also tidies some tc_get_reloc functions that leak memory on error paths. * write.h (as_bad_subtract): Declare. * write.c (as_bad_subtract): New function. (fixup_segment): Use as_bad_subtract. * config/tc-arc.c (md_apply_fix): Likewise. * config/tc-avr.c (md_apply_fix, tc_gen_reloc): Likewise. * config/tc-cris.c (md_apply_fix): Likewise. * config/tc-d10v.c (md_apply_fix): Likewise. * config/tc-d30v.c (md_apply_fix): Likewise. * config/tc-ft32.c (md_apply_fix): Likewise. * config/tc-h8300.c (tc_gen_reloc): Likewise. * config/tc-m68hc11.c (md_apply_fix): Likewise. * config/tc-mmix.c (mmix_frob_file): Likewise. * config/tc-mn10200.c (tc_gen_reloc): Likewise. * config/tc-nds32.c (nds32_apply_fix): Likewise. * config/tc-pru.c (md_apply_fix): Likewise. * config/tc-riscv.c (md_apply_fix): Likewise. * config/tc-s12z.c (md_apply_fix): Likewise. * config/tc-s390.c (md_apply_fix): Likewise. * config/tc-tilegx.c (md_apply_fix): Likewise. * config/tc-tilepro.c (md_apply_fix): Likewise. * config/tc-v850.c (md_apply_fix): Likewise. * config/tc-vax.c (md_apply_fix): Likewise. * config/tc-xc16x.c (tc_gen_reloc): Likewise. * config/tc-xgate.c (md_apply_fix): Likewise. * config/tc-xstormy16.c (xstormy16_md_apply_fix): Likewise. * config/tc-xtensa.c (md_apply_fix): Likewise. * config/tc-z80.c (tc_gen_reloc): Likewise. * config/tc-spu.c (md_apply_fix): Likewise. (tc_gen_reloc): Delete dead code. Free memory on error. * config/tc-cr16.c (tc_gen_reloc): Use as_bad_subtract. Free on error. * config/tc-crx.c (tc_gen_reloc): Likewise. * config/tc-ppc.c (tc_gen_reloc): Likewise. * testsuite/gas/i386/ifunc-2.l: Adjust to suit changed error message. * testsuite/gas/mips/lui-2.l: Likewise. * testsuite/gas/tic6x/reloc-bad-1.l: Likewise.
792 lines
19 KiB
C
792 lines
19 KiB
C
/* tc-ft32.c -- Assemble code for ft32
|
||
Copyright (C) 2008-2021 Free Software Foundation, Inc.
|
||
|
||
This file is part of GAS, the GNU Assembler.
|
||
|
||
GAS is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3, or (at your option)
|
||
any later version.
|
||
|
||
GAS is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GAS; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 51 Franklin Street - Fifth Floor,
|
||
Boston, MA 02110-1301, USA. */
|
||
|
||
/* Contributed by Anthony Green <green@spindazzle.org>. */
|
||
|
||
#include "as.h"
|
||
#include "safe-ctype.h"
|
||
#include "opcode/ft32.h"
|
||
|
||
extern const ft32_opc_info_t ft32_opc_info[128];
|
||
|
||
/* See md_parse_option() for meanings of these options. */
|
||
static char norelax; /* True if -norelax switch seen. */
|
||
|
||
const char comment_chars[] = "#";
|
||
const char line_separator_chars[] = ";";
|
||
const char line_comment_chars[] = "#";
|
||
|
||
static int pending_reloc;
|
||
static htab_t opcode_hash_control;
|
||
|
||
static valueT md_chars_to_number (char * buf, int n);
|
||
|
||
const pseudo_typeS md_pseudo_table[] =
|
||
{
|
||
{0, 0, 0}
|
||
};
|
||
|
||
const char FLT_CHARS[] = "rRsSfFdDxXpP";
|
||
const char EXP_CHARS[] = "eE";
|
||
|
||
/* This function is called once, at assembler startup time. It sets
|
||
up the hash table with all the opcodes in it, and also initializes
|
||
some aliases for compatibility with other assemblers. */
|
||
|
||
void
|
||
md_begin (void)
|
||
{
|
||
const ft32_opc_info_t *opcode;
|
||
opcode_hash_control = str_htab_create ();
|
||
|
||
/* Insert names into hash table. */
|
||
for (opcode = ft32_opc_info; opcode->name; opcode++)
|
||
str_hash_insert (opcode_hash_control, opcode->name, opcode, 0);
|
||
|
||
bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
|
||
if (!norelax)
|
||
linkrelax = 1;
|
||
}
|
||
|
||
/* Parse an expression and then restore the input line pointer. */
|
||
|
||
static char *
|
||
parse_exp_save_ilp (char *s, expressionS *op)
|
||
{
|
||
char *save = input_line_pointer;
|
||
|
||
input_line_pointer = s;
|
||
expression (op);
|
||
s = input_line_pointer;
|
||
input_line_pointer = save;
|
||
return s;
|
||
}
|
||
|
||
static int
|
||
parse_condition (char **ptr)
|
||
{
|
||
char *s = *ptr;
|
||
static const struct
|
||
{
|
||
const char *name;
|
||
int bits;
|
||
}
|
||
ccs[] =
|
||
{
|
||
{ "gt," , (2 << FT32_FLD_CR_BIT) | (5 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "gte," , (2 << FT32_FLD_CR_BIT) | (4 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "lt," , (2 << FT32_FLD_CR_BIT) | (4 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "lte," , (2 << FT32_FLD_CR_BIT) | (5 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "a," , (2 << FT32_FLD_CR_BIT) | (6 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "ae," , (2 << FT32_FLD_CR_BIT) | (1 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "be," , (2 << FT32_FLD_CR_BIT) | (6 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "b," , (2 << FT32_FLD_CR_BIT) | (1 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "nz," , (2 << FT32_FLD_CR_BIT) | (0 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "z," , (2 << FT32_FLD_CR_BIT) | (0 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "nc," , (2 << FT32_FLD_CR_BIT) | (1 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "c," , (2 << FT32_FLD_CR_BIT) | (1 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "no," , (2 << FT32_FLD_CR_BIT) | (2 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "o," , (2 << FT32_FLD_CR_BIT) | (2 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ "ns," , (2 << FT32_FLD_CR_BIT) | (3 << FT32_FLD_CB_BIT) | (0 << FT32_FLD_CV_BIT)},
|
||
{ "s," , (2 << FT32_FLD_CR_BIT) | (3 << FT32_FLD_CB_BIT) | (1 << FT32_FLD_CV_BIT)},
|
||
{ NULL, 0}
|
||
}, *pc;
|
||
|
||
for (pc = ccs; pc->name; pc++)
|
||
{
|
||
if (memcmp(pc->name, s, strlen(pc->name)) == 0)
|
||
{
|
||
*ptr += strlen(pc->name) - 1;
|
||
return pc->bits;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
parse_decimal (char **ptr)
|
||
{
|
||
int r = 0;
|
||
char *s = *ptr;
|
||
|
||
while (('0' <= *s) && (*s <= '9'))
|
||
{
|
||
r *= 10;
|
||
r += (*s++ - '0');
|
||
}
|
||
*ptr = s;
|
||
return r;
|
||
}
|
||
|
||
static int
|
||
parse_register_operand (char **ptr)
|
||
{
|
||
int reg;
|
||
char *s = *ptr;
|
||
|
||
if (*s != '$')
|
||
{
|
||
as_bad (_("expecting register"));
|
||
ignore_rest_of_line ();
|
||
return -1;
|
||
}
|
||
if ((s[1] == 's') && (s[2] == 'p'))
|
||
{
|
||
reg = 31;
|
||
}
|
||
else if ((s[1] == 'c') && (s[2] == 'c'))
|
||
{
|
||
reg = 30;
|
||
}
|
||
else if ((s[1] == 'f') && (s[2] == 'p'))
|
||
{
|
||
reg = 29;
|
||
}
|
||
else if (s[1] == 'r')
|
||
{
|
||
reg = s[2] - '0';
|
||
if ((reg < 0) || (reg > 9))
|
||
{
|
||
as_bad (_("illegal register number"));
|
||
ignore_rest_of_line ();
|
||
return -1;
|
||
}
|
||
if ((reg == 1) || (reg == 2) || (reg == 3))
|
||
{
|
||
int r2 = s[3] - '0';
|
||
if ((r2 >= 0) && (r2 <= 9))
|
||
{
|
||
reg = (reg * 10) + r2;
|
||
*ptr += 1;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
as_bad (_("illegal register number"));
|
||
ignore_rest_of_line ();
|
||
return -1;
|
||
}
|
||
|
||
*ptr += 3;
|
||
|
||
return reg;
|
||
}
|
||
|
||
/* This is the guts of the machine-dependent assembler. STR points to
|
||
a machine dependent instruction. This function is supposed to emit
|
||
the frags/bytes it assembles to. */
|
||
|
||
void
|
||
md_assemble (char *str)
|
||
{
|
||
char *op_start;
|
||
char *op_end;
|
||
ft32_opc_info_t *opcode;
|
||
char *output;
|
||
int idx = 0;
|
||
char pend;
|
||
int nlen = 0;
|
||
unsigned int b;
|
||
int f;
|
||
expressionS arg;
|
||
bool fixed = false;
|
||
unsigned int sc;
|
||
bool can_sc;
|
||
|
||
/* Drop leading whitespace. */
|
||
while (*str == ' ')
|
||
str++;
|
||
|
||
/* Find the op code end. */
|
||
op_start = str;
|
||
for (op_end = str;
|
||
*op_end
|
||
&& !is_end_of_line[*op_end & 0xff]
|
||
&& *op_end != ' '
|
||
&& *op_end != '.';
|
||
op_end++)
|
||
nlen++;
|
||
|
||
pend = *op_end;
|
||
*op_end = 0;
|
||
|
||
if (nlen == 0)
|
||
as_bad (_("can't find opcode "));
|
||
|
||
opcode = (ft32_opc_info_t *) str_hash_find (opcode_hash_control, op_start);
|
||
*op_end = pend;
|
||
|
||
if (opcode == NULL)
|
||
{
|
||
as_bad (_("unknown opcode %s"), op_start);
|
||
return;
|
||
}
|
||
|
||
b = opcode->bits;
|
||
f = opcode->fields;
|
||
|
||
if (opcode->dw)
|
||
{
|
||
int dw;
|
||
|
||
if (*op_end == '.')
|
||
{
|
||
switch (op_end[1])
|
||
{
|
||
case 'b':
|
||
dw = 0;
|
||
break;
|
||
case 's':
|
||
dw = 1;
|
||
break;
|
||
case 'l':
|
||
dw = 2;
|
||
break;
|
||
default:
|
||
as_bad (_("unknown width specifier '.%c'"), op_end[1]);
|
||
return;
|
||
}
|
||
op_end += 2;
|
||
}
|
||
else
|
||
{
|
||
dw = 2; /* default is ".l" */
|
||
}
|
||
b |= dw << FT32_FLD_DW_BIT;
|
||
}
|
||
|
||
while (ISSPACE (*op_end))
|
||
op_end++;
|
||
|
||
output = frag_more (4);
|
||
|
||
while (f)
|
||
{
|
||
int lobit = f & -f;
|
||
|
||
if (f & lobit)
|
||
{
|
||
switch (lobit)
|
||
{
|
||
case FT32_FLD_CBCRCV:
|
||
b |= parse_condition( &op_end);
|
||
break;
|
||
case FT32_FLD_CB:
|
||
b |= parse_decimal (&op_end) << FT32_FLD_CB_BIT;
|
||
break;
|
||
case FT32_FLD_R_D:
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
|
||
break;
|
||
case FT32_FLD_CR:
|
||
b |= (parse_register_operand (&op_end) - 28) << FT32_FLD_CR_BIT;
|
||
break;
|
||
case FT32_FLD_CV:
|
||
b |= parse_decimal (&op_end) << FT32_FLD_CV_BIT;
|
||
break;
|
||
case FT32_FLD_R_1:
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
|
||
break;
|
||
case FT32_FLD_RIMM:
|
||
if (*op_end == '$')
|
||
{
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_RIMM_BIT;
|
||
}
|
||
else
|
||
{
|
||
b |= 0x400 << FT32_FLD_RIMM_BIT;
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
2,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_10);
|
||
}
|
||
break;
|
||
case FT32_FLD_R_2:
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_R_2_BIT;
|
||
break;
|
||
case FT32_FLD_K20:
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
3,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_20);
|
||
break;
|
||
case FT32_FLD_PA:
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
3,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_18);
|
||
break;
|
||
case FT32_FLD_AA:
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
3,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_17);
|
||
break;
|
||
case FT32_FLD_K16:
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
2,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_16);
|
||
break;
|
||
case FT32_FLD_K15:
|
||
op_end = parse_exp_save_ilp (op_end, &arg);
|
||
if (arg.X_add_number & 0x80)
|
||
arg.X_add_number ^= 0x7f00;
|
||
fixed = true;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
2,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_15);
|
||
break;
|
||
case FT32_FLD_R_D_POST:
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_R_D_BIT;
|
||
break;
|
||
case FT32_FLD_R_1_POST:
|
||
b |= parse_register_operand (&op_end) << FT32_FLD_R_1_BIT;
|
||
break;
|
||
default:
|
||
as_bad (_("internal error in argument parsing"));
|
||
break;
|
||
}
|
||
|
||
f &= ~lobit;
|
||
|
||
if (f)
|
||
{
|
||
while (ISSPACE (*op_end))
|
||
op_end++;
|
||
|
||
if (*op_end != ',')
|
||
{
|
||
as_bad (_("expected comma separator"));
|
||
ignore_rest_of_line ();
|
||
}
|
||
|
||
op_end++;
|
||
while (ISSPACE (*op_end))
|
||
op_end++;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (*op_end != 0)
|
||
as_warn (_("extra stuff on line ignored"));
|
||
|
||
can_sc = ft32_shortcode (b, &sc);
|
||
|
||
if (!fixed && can_sc)
|
||
{
|
||
arg.X_op = O_constant;
|
||
arg.X_add_number = 0;
|
||
arg.X_add_symbol = NULL;
|
||
arg.X_op_symbol = NULL;
|
||
fix_new_exp (frag_now,
|
||
(output - frag_now->fr_literal),
|
||
2,
|
||
&arg,
|
||
0,
|
||
BFD_RELOC_FT32_RELAX);
|
||
}
|
||
|
||
output[idx++] = 0xff & (b >> 0);
|
||
output[idx++] = 0xff & (b >> 8);
|
||
output[idx++] = 0xff & (b >> 16);
|
||
output[idx++] = 0xff & (b >> 24);
|
||
|
||
dwarf2_emit_insn (4);
|
||
|
||
while (ISSPACE (*op_end))
|
||
op_end++;
|
||
|
||
if (*op_end != 0)
|
||
as_warn ("extra stuff on line ignored");
|
||
|
||
if (pending_reloc)
|
||
as_bad ("Something forgot to clean up\n");
|
||
}
|
||
|
||
/* Turn a string in input_line_pointer into a floating point constant
|
||
of type type, and store the appropriate bytes in *LITP. The number
|
||
of LITTLENUMS emitted is stored in *SIZEP . An error message is
|
||
returned, or NULL on OK. */
|
||
|
||
const char *
|
||
md_atof (int type, char *litP, int *sizeP)
|
||
{
|
||
int prec;
|
||
LITTLENUM_TYPE words[4];
|
||
char *t;
|
||
int i;
|
||
|
||
switch (type)
|
||
{
|
||
case 'f':
|
||
prec = 2;
|
||
break;
|
||
|
||
case 'd':
|
||
prec = 4;
|
||
break;
|
||
|
||
default:
|
||
*sizeP = 0;
|
||
return _("bad call to md_atof");
|
||
}
|
||
|
||
t = atof_ieee (input_line_pointer, type, words);
|
||
if (t)
|
||
input_line_pointer = t;
|
||
|
||
*sizeP = prec * 2;
|
||
|
||
for (i = prec - 1; i >= 0; i--)
|
||
{
|
||
md_number_to_chars (litP, (valueT) words[i], 2);
|
||
litP += 2;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
const char *md_shortopts = "";
|
||
|
||
struct option md_longopts[] =
|
||
{
|
||
#define OPTION_NORELAX (OPTION_MD_BASE)
|
||
{"norelax", no_argument, NULL, OPTION_NORELAX},
|
||
{"no-relax", no_argument, NULL, OPTION_NORELAX},
|
||
{NULL, no_argument, NULL, 0}
|
||
};
|
||
size_t md_longopts_size = sizeof (md_longopts);
|
||
|
||
/* We have no target specific options yet, so these next
|
||
two functions are empty. */
|
||
int
|
||
md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED)
|
||
{
|
||
switch (c)
|
||
{
|
||
case OPTION_NORELAX:
|
||
norelax = 1;
|
||
break;
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
void
|
||
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
|
||
{
|
||
fprintf (stream, _("FT32 options:\n"));
|
||
fprintf (stream, _("\n\
|
||
-no-relax don't relax relocations\n\
|
||
\n"));
|
||
}
|
||
|
||
/* Convert from target byte order to host byte order. */
|
||
|
||
static valueT
|
||
md_chars_to_number (char * buf, int n)
|
||
{
|
||
valueT result = 0;
|
||
unsigned char * where = (unsigned char *) buf;
|
||
|
||
while (n--)
|
||
{
|
||
result <<= 8;
|
||
result |= (where[n] & 255);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Apply a fixup to the object file. */
|
||
|
||
void
|
||
md_apply_fix (fixS *fixP ATTRIBUTE_UNUSED,
|
||
valueT * valP ATTRIBUTE_UNUSED, segT seg ATTRIBUTE_UNUSED)
|
||
{
|
||
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
|
||
long val = *valP;
|
||
long newval;
|
||
|
||
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_32:
|
||
fixP->fx_r_type = BFD_RELOC_FT32_DIFF32;
|
||
break;
|
||
default:
|
||
as_bad_subtract (fixP);
|
||
break;
|
||
}
|
||
|
||
val = S_GET_VALUE (fixP->fx_addsy) +
|
||
fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
|
||
*valP = val;
|
||
|
||
fixP->fx_subsy = NULL;
|
||
}
|
||
|
||
/* We don't actually support subtracting a symbol. */
|
||
if (fixP->fx_subsy != (symbolS *) NULL)
|
||
as_bad_subtract (fixP);
|
||
|
||
switch (fixP->fx_r_type)
|
||
{
|
||
case BFD_RELOC_FT32_DIFF32:
|
||
case BFD_RELOC_32:
|
||
buf[3] = val >> 24;
|
||
buf[2] = val >> 16;
|
||
buf[1] = val >> 8;
|
||
buf[0] = val >> 0;
|
||
break;
|
||
|
||
case BFD_RELOC_16:
|
||
buf[1] = val >> 8;
|
||
buf[0] = val >> 0;
|
||
break;
|
||
|
||
case BFD_RELOC_8:
|
||
*buf = val;
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_10:
|
||
if (!val)
|
||
break;
|
||
newval = md_chars_to_number (buf, 2);
|
||
newval |= (val & ((1 << 10) - 1)) << FT32_FLD_RIMM_BIT;
|
||
md_number_to_chars (buf, newval, 2);
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_20:
|
||
if (!val)
|
||
break;
|
||
newval = md_chars_to_number (buf, 3);
|
||
newval |= val & ((1 << 20) - 1);
|
||
md_number_to_chars (buf, newval, 3);
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_15:
|
||
if (!val)
|
||
break;
|
||
newval = md_chars_to_number (buf, 2);
|
||
newval |= val & ((1 << 15) - 1);
|
||
md_number_to_chars (buf, newval, 2);
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_17:
|
||
if (!val)
|
||
break;
|
||
newval = md_chars_to_number (buf, 3);
|
||
newval |= val & ((1 << 17) - 1);
|
||
md_number_to_chars (buf, newval, 3);
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_18:
|
||
if (!val)
|
||
break;
|
||
newval = md_chars_to_number (buf, 4);
|
||
newval |= (val >> 2) & ((1 << 18) - 1);
|
||
md_number_to_chars (buf, newval, 4);
|
||
break;
|
||
|
||
case BFD_RELOC_FT32_RELAX:
|
||
break;
|
||
|
||
default:
|
||
abort ();
|
||
}
|
||
|
||
if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
|
||
fixP->fx_done = 1;
|
||
}
|
||
|
||
void
|
||
md_number_to_chars (char *ptr, valueT use, int nbytes)
|
||
{
|
||
number_to_chars_littleendian (ptr, use, nbytes);
|
||
}
|
||
|
||
/* Generate a machine-dependent relocation. */
|
||
|
||
arelent *
|
||
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
|
||
{
|
||
arelent *relP;
|
||
bfd_reloc_code_real_type code;
|
||
|
||
switch (fixP->fx_r_type)
|
||
{
|
||
case BFD_RELOC_32:
|
||
case BFD_RELOC_16:
|
||
case BFD_RELOC_8:
|
||
case BFD_RELOC_FT32_10:
|
||
case BFD_RELOC_FT32_20:
|
||
case BFD_RELOC_FT32_15:
|
||
case BFD_RELOC_FT32_17:
|
||
case BFD_RELOC_FT32_18:
|
||
case BFD_RELOC_FT32_RELAX:
|
||
case BFD_RELOC_FT32_DIFF32:
|
||
code = fixP->fx_r_type;
|
||
break;
|
||
default:
|
||
as_bad_where (fixP->fx_file, fixP->fx_line,
|
||
_("Semantics error. This type of operand can not be "
|
||
"relocated, it must be an assembly-time constant"));
|
||
return NULL;
|
||
}
|
||
|
||
relP = XNEW (arelent);
|
||
gas_assert (relP != 0);
|
||
relP->sym_ptr_ptr = XNEW (asymbol *);
|
||
*relP->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
|
||
relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
|
||
|
||
relP->addend = fixP->fx_offset;
|
||
|
||
relP->howto = bfd_reloc_type_lookup (stdoutput, code);
|
||
if (! relP->howto)
|
||
{
|
||
const char *name;
|
||
|
||
name = S_GET_NAME (fixP->fx_addsy);
|
||
if (name == NULL)
|
||
name = _("<unknown>");
|
||
as_fatal (_("Cannot generate relocation type for symbol %s, code %s"),
|
||
name, bfd_get_reloc_code_name (code));
|
||
}
|
||
|
||
return relP;
|
||
}
|
||
|
||
/* TC_FORCE_RELOCATION hook */
|
||
|
||
static bool
|
||
relaxable_section (asection *sec)
|
||
{
|
||
return ((sec->flags & SEC_DEBUGGING) == 0
|
||
&& (sec->flags & SEC_CODE) != 0
|
||
&& (sec->flags & SEC_ALLOC) != 0);
|
||
}
|
||
|
||
/* Does whatever the xtensa port does. */
|
||
|
||
int
|
||
ft32_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
|
||
ft32_force_relocation (fixS *fix)
|
||
{
|
||
if (linkrelax && fix->fx_addsy
|
||
&& relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
return generic_force_reloc (fix);
|
||
}
|
||
|
||
bool
|
||
ft32_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;
|
||
}
|