From a1cc9221e2f5771188b5832633eb73ee2bbbb4c5 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Sat, 2 Sep 2000 02:36:27 +0000 Subject: [PATCH] * 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) : Use them. (GOT_symbol): New variable. (md_undefined_symbol): Set it. --- gas/ChangeLog | 18 ++++ gas/config/tc-sh.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gas/config/tc-sh.h | 40 +++++++ 3 files changed, 357 insertions(+) diff --git a/gas/ChangeLog b/gas/ChangeLog index b1df505..ba7429c 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,21 @@ +2000-09-01 Alexandre Oliva + + * 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 , Kaz Kojima , Alexandre Oliva + + * 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) : Use them. + (GOT_symbol): New variable. + (md_undefined_symbol): Set it. + 2000-09-01 Richard Henderson * config/tc-ia64.c (match): Don't inline. diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c index 8362896..a2cd7de 100644 --- a/gas/config/tc-sh.c +++ b/gas/config/tc-sh.c @@ -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; diff --git a/gas/config/tc-sh.h b/gas/config/tc-sh.h index 4e73b8b..54ba1b9 100644 --- a/gas/config/tc-sh.h +++ b/gas/config/tc-sh.h @@ -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 () -- 2.7.4