diff options
author | Alexandre Oliva <aoliva@redhat.com> | 2002-01-24 02:54:47 +0000 |
---|---|---|
committer | Alexandre Oliva <aoliva@redhat.com> | 2002-01-24 02:54:47 +0000 |
commit | 538cd60fbb681d40ab2e2990bd00f0b2acea7b24 (patch) | |
tree | c5533f6ef1453ce27cbbd054e446d4484ee073b6 | |
parent | ea87b71bed61d157e1f95597216b08909e109d04 (diff) | |
download | gdb-538cd60fbb681d40ab2e2990bd00f0b2acea7b24.zip gdb-538cd60fbb681d40ab2e2990bd00f0b2acea7b24.tar.gz gdb-538cd60fbb681d40ab2e2990bd00f0b2acea7b24.tar.bz2 |
* config/tc-sh.c (sh_elf_suffix): Removed.
(sh_PIC_related_p, sh_check_fixup, sh_cons_fix_new,
sh_end_of_match, sh_parse_name): New functions.
(sh_elf_cons): Simplify.
(parse_exp): Reject misplaced PIC operands.
(md_undefined_symbol): Simplify.
(sh_fix_adjustable): Let @GOTOFF be adjusted.
(md_apply_fix3): Write @PLT and @GOTOFF addends in place.
(tc_gen_reloc): Move fixp subsy absolute value into addnumber.
Complain if subsy remains at the end.
* config/tc-sh.h (sh_parse_name, sh_cons_fix_new): Declare.
(md_parse_name, TC_CONS_FIX_NEW, O_PIC_reloc): Define.
-rw-r--r-- | gas/ChangeLog | 15 | ||||
-rw-r--r-- | gas/config/tc-sh.c | 411 | ||||
-rw-r--r-- | gas/config/tc-sh.h | 14 |
3 files changed, 275 insertions, 165 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 16961a3..3532a9b 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,18 @@ +2002-01-24 Alexandre Oliva <aoliva@redhat.com> + + * config/tc-sh.c (sh_elf_suffix): Removed. + (sh_PIC_related_p, sh_check_fixup, sh_cons_fix_new, + sh_end_of_match, sh_parse_name): New functions. + (sh_elf_cons): Simplify. + (parse_exp): Reject misplaced PIC operands. + (md_undefined_symbol): Simplify. + (sh_fix_adjustable): Let @GOTOFF be adjusted. + (md_apply_fix3): Write @PLT and @GOTOFF addends in place. + (tc_gen_reloc): Move fixp subsy absolute value into addnumber. + Complain if subsy remains at the end. + * config/tc-sh.h (sh_parse_name, sh_cons_fix_new): Declare. + (md_parse_name, TC_CONS_FIX_NEW, O_PIC_reloc): Define. + 2002-01-22 Alexandre Oliva <aoliva@redhat.com> * config/tc-mn10300.c (xr_registers): Move `pc'... diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c index 27c33cd..b2a7d97 100644 --- a/gas/config/tc-sh.c +++ b/gas/config/tc-sh.c @@ -58,8 +58,6 @@ static sh_opcode_info *find_cooked_opcode PARAMS ((char **)); static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *)); static void little PARAMS ((int)); static void big PARAMS ((int)); -static bfd_reloc_code_real_type sh_elf_suffix - PARAMS ((char **str_p, expressionS *, expressionS *new_exp_p)); static int parse_reg PARAMS ((char *, int *, int *)); static symbolS *dot PARAMS ((void)); static char *parse_exp PARAMS ((char *, sh_operand_info *)); @@ -78,6 +76,10 @@ static unsigned int build_Mytes #ifdef OBJ_ELF static void sh_elf_cons PARAMS ((int)); +inline static int sh_PIC_related_p PARAMS ((symbolS *)); +static int sh_check_fixup PARAMS ((expressionS *, bfd_reloc_code_real_type *)); +inline static char *sh_end_of_match PARAMS ((char *, char *)); + symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ #endif @@ -262,77 +264,150 @@ 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; +/* Determinet whether the symbol needs any kind of PIC relocation. */ + +inline static int +sh_PIC_related_p (sym) + symbolS *sym; { - struct map_bfd { - char *string; - int length; - bfd_reloc_code_real_type reloc; - }; + expressionS *exp; - 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++ = 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) + if (! sym) + return 0; + + if (sym == GOT_symbol) + return 1; + + exp = symbol_get_value_expression (sym); + + return (exp->X_op == O_PIC_reloc + || sh_PIC_related_p (exp->X_add_symbol) + || sh_PIC_related_p (exp->X_op_symbol)); +} + +/* Determine the relocation type to be used to represent the + expression, that may be rearranged. */ + +static int +sh_check_fixup (main_exp, r_type_p) + expressionS *main_exp; + bfd_reloc_code_real_type *r_type_p; +{ + expressionS *exp = main_exp; + + /* This is here for backward-compatibility only. GCC used to generated: + + f@PLT + . - (.LPCS# + 2) + + but we'd rather be able to handle this as a PIC-related reference + plus/minus a symbol. However, gas' parser gives us: + + O_subtract (O_add (f@PLT, .), .LPCS#+2) + + so we attempt to transform this into: + + O_subtract (f@PLT, O_subtract (.LPCS#+2, .)) + + which we can handle simply below. */ + if (exp->X_op == O_subtract) + { + if (sh_PIC_related_p (exp->X_op_symbol)) + return 1; + + exp = symbol_get_value_expression (exp->X_add_symbol); + + if (exp && sh_PIC_related_p (exp->X_op_symbol)) + return 1; + + if (exp && exp->X_op == O_add + && sh_PIC_related_p (exp->X_add_symbol)) + { + symbolS *sym = exp->X_add_symbol; + + exp->X_op = O_subtract; + exp->X_add_symbol = main_exp->X_op_symbol; + + main_exp->X_op_symbol = main_exp->X_add_symbol; + main_exp->X_add_symbol = sym; + + main_exp->X_add_number += exp->X_add_number; + exp->X_add_number = 0; + } + + exp = main_exp; + } + else if (exp->X_op == O_add && sh_PIC_related_p (exp->X_op_symbol)) + return 1; + + if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract) + { + if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol) + { + *r_type_p = BFD_RELOC_SH_GOTPC; + return 0; + } + exp = symbol_get_value_expression (exp->X_add_symbol); + if (! exp) + return 0; + } + + if (exp->X_op == O_PIC_reloc) + { + *r_type_p = exp->X_md; + if (exp == main_exp) + exp->X_op = O_symbol; + else + { + main_exp->X_add_symbol = exp->X_add_symbol; + main_exp->X_add_number += exp->X_add_number; + } + } + else + return (sh_PIC_related_p (exp->X_add_symbol) + || sh_PIC_related_p (exp->X_op_symbol)); + + return 0; +} + +/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */ + +void +sh_cons_fix_new (frag, off, size, exp) + fragS *frag; + int off, size; + expressionS *exp; +{ + bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED; + + if (sh_check_fixup (exp, &r_type)) + as_bad (_("Invalid PIC expression.")); + + if (r_type == BFD_RELOC_UNUSED) + switch (size) { - /* 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; - } + case 1: + r_type = BFD_RELOC_8; + break; - *str_p = str; - return ptr->reloc; - } + case 2: + r_type = BFD_RELOC_16; + break; + + case 4: + r_type = BFD_RELOC_32; + break; - return BFD_RELOC_UNUSED; + default: + goto error; + } + else if (size != 4) + { + error: + as_bad (_("unsupported BFD relocation size %u"), size); + r_type = BFD_RELOC_UNUSED; + } + + fix_new_exp (frag, off, size, exp, 0, r_type); } /* The regular cons() function, that reads constants, doesn't support @@ -343,9 +418,7 @@ 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; + expressionS exp; if (is_it_end_of_statement ()) { @@ -356,79 +429,7 @@ sh_elf_cons (nbytes) 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); + emit_expr (&exp, (unsigned int) nbytes); } while (*input_line_pointer++ == ','); @@ -867,6 +868,12 @@ parse_exp (s, op) expression (&op->immediate); if (op->immediate.X_op == O_absent) as_bad (_("missing operand")); +#ifdef OBJ_ELF + else if (op->immediate.X_op == O_PIC_reloc + || sh_PIC_related_p (op->immediate.X_add_symbol) + || sh_PIC_related_p (op->immediate.X_op_symbol)) + as_bad (_("misplaced PIC operand")); +#endif new = input_line_pointer; input_line_pointer = save; return new; @@ -2012,26 +2019,8 @@ sh_flush_pending_output () symbolS * md_undefined_symbol (name) - char *name; + char *name ATTRIBUTE_UNUSED; { -#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; } @@ -2741,7 +2730,6 @@ sh_fix_adjustable (fixP) 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; @@ -2980,6 +2968,8 @@ md_apply_fix3 (fixP, valP, seg) /* 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; + val = fixP->fx_addnumber - S_GET_VALUE (fixP->fx_subsy); + md_number_to_chars (buf, val, 4); break; case BFD_RELOC_SH_GOTPC: @@ -3009,6 +2999,7 @@ md_apply_fix3 (fixP, valP, seg) break; case BFD_RELOC_32_GOTOFF: + md_number_to_chars (buf, val, 4); break; #endif @@ -3322,6 +3313,13 @@ tc_gen_reloc (section, fixp) *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); rel->address = fixp->fx_frag->fr_address + fixp->fx_where; + if (fixp->fx_subsy + && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section) + { + fixp->fx_addnumber -= S_GET_VALUE (fixp->fx_subsy); + fixp->fx_subsy = 0; + } + r_type = fixp->fx_r_type; if (SWITCH_TABLE (fixp)) @@ -3361,7 +3359,7 @@ tc_gen_reloc (section, fixp) rel->addend = 0; rel->howto = bfd_reloc_type_lookup (stdoutput, r_type); - if (rel->howto == NULL) + if (rel->howto == NULL || fixp->fx_subsy) { as_bad_where (fixp->fx_file, fixp->fx_line, _("Cannot represent relocation type %s"), @@ -3374,4 +3372,87 @@ tc_gen_reloc (section, fixp) return rel; } +#ifdef OBJ_ELF +inline static char * +sh_end_of_match (cont, what) + char *cont, *what; +{ + int len = strlen (what); + + if (strncasecmp (cont, what, strlen (what)) == 0 + && ! is_part_of_name (cont[len])) + return cont + len; + + return NULL; +} + +int +sh_parse_name (name, exprP, nextcharP) + char const *name; + expressionS *exprP; + char *nextcharP; +{ + char *next = input_line_pointer; + char *next_end; + int reloc_type; + segT segment; + + exprP->X_op_symbol = NULL; + + if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0) + { + if (! GOT_symbol) + GOT_symbol = symbol_find_or_make (name); + + exprP->X_add_symbol = GOT_symbol; + no_suffix: + /* If we have an absolute symbol or a reg, then we know its + value now. */ + segment = S_GET_SEGMENT (exprP->X_add_symbol); + if (segment == absolute_section) + { + exprP->X_op = O_constant; + exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol); + exprP->X_add_symbol = NULL; + } + else if (segment == reg_section) + { + exprP->X_op = O_register; + exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol); + exprP->X_add_symbol = NULL; + } + else + { + exprP->X_op = O_symbol; + exprP->X_add_number = 0; + } + + return 1; + } + + exprP->X_add_symbol = symbol_find_or_make (name); + + if (*nextcharP != '@') + goto no_suffix; + else if ((next_end = sh_end_of_match (next + 1, "GOTOFF"))) + reloc_type = BFD_RELOC_32_GOTOFF; + else if ((next_end = sh_end_of_match (next + 1, "GOT"))) + reloc_type = BFD_RELOC_32_GOT_PCREL; + else if ((next_end = sh_end_of_match (next + 1, "PLT"))) + reloc_type = BFD_RELOC_32_PLT_PCREL; + else + goto no_suffix; + + *input_line_pointer = *nextcharP; + input_line_pointer = next_end; + *nextcharP = *input_line_pointer; + *input_line_pointer = '\0'; + + exprP->X_op = O_PIC_reloc; + exprP->X_add_number = 0; + exprP->X_md = reloc_type; + + return 1; +} +#endif #endif /* BFD_ASSEMBLER */ diff --git a/gas/config/tc-sh.h b/gas/config/tc-sh.h index dc7d173..09aacd7 100644 --- a/gas/config/tc-sh.h +++ b/gas/config/tc-sh.h @@ -210,4 +210,18 @@ extern void sh_elf_final_processing PARAMS ((void)); && S_IS_DEFINED ((FIX)->fx_addsy) \ && ! S_IS_COMMON ((FIX)->fx_addsy)))) +#define md_parse_name(name, exprP, nextcharP) \ + sh_parse_name ((name), (exprP), (nextcharP)) +int sh_parse_name PARAMS ((char const *name, + expressionS *exprP, + char *nextchar)); + +#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \ + sh_cons_fix_new ((FRAG), (OFF), (LEN), (EXP)) +void sh_cons_fix_new PARAMS ((fragS *, int, int, expressionS *)); + +/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT + symbols. The relocation type is stored in X_md. */ +#define O_PIC_reloc O_md1 + #endif /* OBJ_ELF */ |