* config/tc-sh.h [OBJ_ELF] (TC_FIX_ADJUSTABLE): Define.

* config/tc-sh.c (md_apply_fix): Map 32-bit relocations that
become PC-relative to BFD_RELOC_32_PCREL.  Reject 16- or 8-bit
similar relocs.
(sh_obj_adjustable): Return 1 for PC-relative offsets used in
branches.


* config/tc-sh.h (DIFF_EXPR_OK, GLOBAL_OFFSET_TABLE_NAME,
TC_RELOC_GLOBAL_OFFSET_TABLE, TC_RELOC_RTSYM_LOC_FIXUP): Define.
* config/tc-sh.c (sh_elf_cons, sh_elf_suffix): New functions.
[OBJ_ELF] (md_pseudo_table) <long, int, word, short>: Use them.
(GOT_symbol): New variable.
(md_undefined_symbol): Set it.
This commit is contained in:
Alexandre Oliva 2000-09-02 02:36:27 +00:00
parent 37c644f2f5
commit a1cc9221e2
3 changed files with 357 additions and 0 deletions

View file

@ -1,3 +1,21 @@
2000-09-01 Alexandre Oliva <aoliva@redhat.com>
* config/tc-sh.h [OBJ_ELF] (TC_FIX_ADJUSTABLE): Define.
* config/tc-sh.c (md_apply_fix): Map 32-bit relocations that
become PC-relative to BFD_RELOC_32_PCREL. Reject 16- or 8-bit
similar relocs.
(sh_obj_adjustable): Return 1 for PC-relative offsets used in
branches.
2000-09-01 Niibe Yutaka <gniibe@m17n.org>, Kaz Kojima <kkojima@rr.iij4u.or.jp>, Alexandre Oliva <aoliva@redhat.com>
* config/tc-sh.h (DIFF_EXPR_OK, GLOBAL_OFFSET_TABLE_NAME,
TC_RELOC_GLOBAL_OFFSET_TABLE, TC_RELOC_RTSYM_LOC_FIXUP): Define.
* config/tc-sh.c (sh_elf_cons, sh_elf_suffix): New functions.
[OBJ_ELF] (md_pseudo_table) <long, int, word, short>: Use them.
(GOT_symbol): New variable.
(md_undefined_symbol): Set it.
2000-09-01 Richard Henderson <rth@cygnus.com>
* config/tc-ia64.c (match): Don't inline.

View file

@ -51,6 +51,12 @@ static void s_uacons PARAMS ((int));
static sh_opcode_info *find_cooked_opcode PARAMS ((char **));
static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *));
#ifdef OBJ_ELF
static void sh_elf_cons PARAMS ((int));
symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
#endif
int shl = 0;
static void
@ -69,8 +75,15 @@ little (ignore)
const pseudo_typeS md_pseudo_table[] =
{
#ifdef OBJ_ELF
{"long", sh_elf_cons, 4},
{"int", sh_elf_cons, 4},
{"word", sh_elf_cons, 2},
{"short", sh_elf_cons, 2},
#else
{"int", cons, 4},
{"word", cons, 2},
#endif /* OBJ_ELF */
{"form", listing_psize, 0},
{"little", little, 0},
{"heading", listing_title, 0},
@ -200,6 +213,191 @@ const relax_typeS md_relax_table[C (END, 0)] = {
static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
#ifdef OBJ_ELF
/* Parse @got, etc. and return the desired relocation.
If we have additional arithmetic expression, then we fill in new_exp_p. */
static bfd_reloc_code_real_type
sh_elf_suffix (str_p, exp_p, new_exp_p)
char **str_p;
expressionS *exp_p, *new_exp_p;
{
struct map_bfd {
char *string;
int length;
bfd_reloc_code_real_type reloc;
};
char ident[20];
char *str = *str_p;
char *str2;
int ch;
int len;
struct map_bfd *ptr;
#define MAP(str,reloc) { str, sizeof(str)-1, reloc }
static struct map_bfd mapping[] = {
MAP ("got", BFD_RELOC_32_GOT_PCREL),
MAP ("plt", BFD_RELOC_32_PLT_PCREL),
MAP ("gotoff", BFD_RELOC_32_GOTOFF),
{ (char *)0, 0, BFD_RELOC_UNUSED }
};
if (*str++ != '@')
return BFD_RELOC_UNUSED;
for (ch = *str, str2 = ident;
(str2 < ident + sizeof (ident) - 1
&& (isalnum (ch) || ch == '@'));
ch = *++str)
{
*str2++ = (islower (ch)) ? ch : tolower (ch);
}
*str2 = '\0';
len = str2 - ident;
ch = ident[0];
for (ptr = &mapping[0]; ptr->length > 0; ptr++)
if (ch == ptr->string[0]
&& len == ptr->length
&& memcmp (ident, ptr->string, ptr->length) == 0)
{
/* Now check for identifier@suffix+constant */
if (*str == '-' || *str == '+')
{
char *orig_line = input_line_pointer;
input_line_pointer = str;
expression (new_exp_p);
if (new_exp_p->X_op == O_constant)
{
exp_p->X_add_number += new_exp_p->X_add_number;
str = input_line_pointer;
}
if (new_exp_p->X_op == O_subtract)
str = input_line_pointer;
if (&input_line_pointer != str_p)
input_line_pointer = orig_line;
}
*str_p = str;
return ptr->reloc;
}
return BFD_RELOC_UNUSED;
}
/* The regular cons() function, that reads constants, doesn't support
suffixes such as @GOT, @GOTOFF and @PLT, that generate
machine-specific relocation types. So we must define it here. */
/* Clobbers input_line_pointer, checks end-of-line. */
static void
sh_elf_cons (nbytes)
register int nbytes; /* 1=.byte, 2=.word, 4=.long */
{
expressionS exp, new_exp;
bfd_reloc_code_real_type reloc;
const char *name;
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
do
{
expression (&exp);
new_exp.X_op = O_absent;
new_exp.X_add_symbol = new_exp.X_op_symbol = NULL;
/* If the _GLOBAL_OFFSET_TABLE_ symbol hasn't been found yet,
use the name of the symbol to tell whether it's the
_GLOBAL_OFFSET_TABLE_. If it has, comparing the symbols is
sufficient. */
if (! GOT_symbol && exp.X_add_symbol)
name = S_GET_NAME (exp.X_add_symbol);
else
name = NULL;
/* Check whether this expression involves the
_GLOBAL_OFFSET_TABLE_ symbol, by itself or added to a
difference of two other symbols. */
if (((GOT_symbol && GOT_symbol == exp.X_add_symbol)
|| (! GOT_symbol && name
&& strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
&& (exp.X_op == O_symbol
|| (exp.X_op == O_add
&& ((symbol_get_value_expression (exp.X_op_symbol)->X_op)
== O_subtract))))
{
reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
BFD_RELOC_32);
int size = bfd_get_reloc_size (reloc_howto);
if (GOT_symbol == NULL)
GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
if (size > nbytes)
as_bad (_("%s relocations do not fit in %d bytes\n"),
reloc_howto->name, nbytes);
else
{
register char *p = frag_more ((int) nbytes);
int offset = nbytes - size;
fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
size, &exp, 0, TC_RELOC_GLOBAL_OFFSET_TABLE);
}
}
/* Check if this symbol involves one of the magic suffixes, such
as @GOT, @GOTOFF or @PLT, and determine which relocation type
to use. */
else if ((exp.X_op == O_symbol || (exp.X_op == O_add && exp.X_op_symbol))
&& *input_line_pointer == '@'
&& ((reloc = sh_elf_suffix (&input_line_pointer, &exp, &new_exp))
!= BFD_RELOC_UNUSED))
{
reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
reloc);
int size = bfd_get_reloc_size (reloc_howto);
/* Force a GOT to be generated. */
if (GOT_symbol == NULL)
GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
if (size > nbytes)
as_bad (_("%s relocations do not fit in %d bytes\n"),
reloc_howto->name, nbytes);
else
{
register char *p = frag_more ((int) nbytes);
int offset = nbytes - size;
fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
&exp, 0, reloc);
if (new_exp.X_op != O_absent)
fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
&new_exp, 0, BFD_RELOC_32);
}
}
else
emit_expr (&exp, (unsigned int) nbytes);
}
while (*input_line_pointer++ == ',');
input_line_pointer--; /* Put terminator back into stream. */
if (*input_line_pointer == '#' || *input_line_pointer == '!')
{
while (! is_end_of_line[*input_line_pointer++]);
}
else
demand_empty_rest_of_line ();
}
#endif /* OBJ_ELF */
/* This function is called once, at assembler startup time. This should
set up all the tables, etc that the MD part of the assembler needs. */
@ -1795,6 +1993,24 @@ symbolS *
md_undefined_symbol (name)
char *name;
{
#ifdef OBJ_ELF
/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. Otherwise we
have no need to default values of symbols. */
if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
{
if (!GOT_symbol)
{
if (symbol_find (name))
as_bad ("GOT already in the symbol table");
GOT_symbol = symbol_new (name, undefined_section,
(valueT)0, & zero_address_frag);
}
return GOT_symbol;
}
#endif /* OBJ_ELF */
return 0;
}
@ -2544,6 +2760,20 @@ sh_fix_adjustable (fixP)
if (fixP->fx_addsy == NULL)
return 1;
if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
|| fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
|| fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
|| fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
|| fixP->fx_r_type == BFD_RELOC_8_PCREL
|| fixP->fx_r_type == BFD_RELOC_SH_SWITCH16
|| fixP->fx_r_type == BFD_RELOC_SH_SWITCH32)
return 1;
if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP)
|| fixP->fx_r_type == BFD_RELOC_32_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_RVA)
return 0;
/* We need the symbol name for the VTABLE entries */
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
@ -2605,6 +2835,35 @@ md_apply_fix (fixP, val)
int shift;
#ifdef BFD_ASSEMBLER
/* A difference between two symbols, the second of which is in the
current section, is transformed in a PC-relative relocation to
the other symbol. We have to adjust the relocation type here. */
if (fixP->fx_pcrel)
{
switch (fixP->fx_r_type)
{
default:
break;
case BFD_RELOC_32:
fixP->fx_r_type = BFD_RELOC_32_PCREL;
break;
/* Currently, we only support 32-bit PCREL relocations.
We'd need a new reloc type to handle 16_PCREL, and
8_PCREL is already taken for R_SH_SWITCH8, which
apparently does something completely different than what
we need. FIXME. */
case BFD_RELOC_16:
bfd_set_error (bfd_error_bad_value);
return false;
case BFD_RELOC_8:
bfd_set_error (bfd_error_bad_value);
return false;
}
}
/* The function adjust_reloc_syms won't convert a reloc against a weak
symbol into a reloc against a section, but bfd_install_relocation
will screw up if the symbol is defined, so we have to adjust val here
@ -2715,6 +2974,7 @@ md_apply_fix (fixP, val)
break;
case BFD_RELOC_32:
case BFD_RELOC_32_PCREL:
md_number_to_chars (buf, val, 4);
break;
@ -2747,6 +3007,43 @@ md_apply_fix (fixP, val)
return;
#endif
#ifdef OBJ_ELF
case BFD_RELOC_32_PLT_PCREL:
/* Make the jump instruction point to the address of the operand. At
runtime we merely add the offset to the actual PLT entry. */
*valp = 0xfffffffc;
break;
case BFD_RELOC_SH_GOTPC:
/* This is tough to explain. We end up with this one if we have
operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]".
The goal here is to obtain the absolute address of the GOT,
and it is strongly preferable from a performance point of
view to avoid using a runtime relocation for this. There are
cases where you have something like:
.long _GLOBAL_OFFSET_TABLE_+[.-.L66]
and here no correction would be required. Internally in the
assembler we treat operands of this form as not being pcrel
since the '.' is explicitly mentioned, and I wonder whether
it would simplify matters to do it this way. Who knows. In
earlier versions of the PIC patches, the pcrel_adjust field
was used to store the correction, but since the expression is
not pcrel, I felt it would be confusing to do it this way. */
*valp -= 1;
md_number_to_chars (buf, val, 4);
break;
case BFD_RELOC_32_GOT_PCREL:
*valp = 0; /* Fully resolved at runtime. No addend. */
md_number_to_chars (buf, 0, 4);
break;
case BFD_RELOC_32_GOTOFF:
break;
#endif
default:
abort ();
}
@ -3098,6 +3395,8 @@ tc_gen_reloc (section, fixp)
}
else if (fixp->fx_pcrel)
rel->addend = fixp->fx_addnumber;
else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
rel->addend = fixp->fx_addnumber;
else
rel->addend = 0;

View file

@ -61,6 +61,10 @@ extern int sh_force_relocation ();
#define obj_fix_adjustable(fixP) sh_fix_adjustable(fixP)
struct fix;
extern boolean sh_fix_adjustable PARAMS ((struct fix *));
/* This arranges for gas/write.c to not apply a relocation if
obj_fix_adjustable() says it is not adjustable. */
#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP)
#endif
#define IGNORE_NONSTANDARD_ESCAPES
@ -161,6 +165,42 @@ extern int target_big_endian;
#define elf_tc_final_processing sh_elf_final_processing
extern void sh_elf_final_processing PARAMS ((void));
#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
/* This is the relocation type for direct references to
GLOBAL_OFFSET_TABLE. It comes up in complicated expressions such
as _GLOBAL_OFFSET_TABLE_+[.-.L284], which cannot be expressed
normally with the regular expressions. The fixup specified here
when used at runtime implies that we should add the address of the
GOT to the specified location, and as a result we have simplified
the expression into something we can use. */
#define TC_RELOC_GLOBAL_OFFSET_TABLE BFD_RELOC_SH_GOTPC
/* This expression evaluates to false if the relocation is for a local object
for which we still want to do the relocation at runtime. True if we
are willing to perform this relocation while building the .o file.
This is only used for pcrel relocations, so GOTOFF does not need to be
checked here. I am not sure if some of the others are ever used with
pcrel, but it is easier to be safe than sorry.
We can't resolve references to the GOT or the PLT when creating the
object file, since these tables are only created by the linker.
Also, if the symbol is global, weak, common or not defined, the
assembler can't compute the appropriate reloc, since its location
can only be determined at link time. */
#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
((FIX)->fx_r_type != BFD_RELOC_32_PLT_PCREL \
&& (FIX)->fx_r_type != BFD_RELOC_32_GOT_PCREL \
&& (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC \
&& ((FIX)->fx_addsy == NULL \
|| (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
&& ! S_IS_WEAK ((FIX)->fx_addsy) \
&& S_IS_DEFINED ((FIX)->fx_addsy) \
&& ! S_IS_COMMON ((FIX)->fx_addsy))))
#endif /* OBJ_ELF */
#define md_end() sh_finalize ()