From: Ken Raeburn Date: Tue, 13 Sep 1994 02:15:23 +0000 (+0000) Subject: * tc-i386.c (TC_RELOC): New macro. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f0b37b4ad64b9ff80992f5242512e6ffcc2f3680;p=binutils-gdb.git * tc-i386.c (TC_RELOC): New macro. (struct _i386_insn): New field disp_reloc. (GOT_symbol): New variable. (operand_special_chars): Added square-brackets and at-sign. (reloc) [BFD_ASSEMBLER]: Added new argument OTHER; if it is not NO_RELOC, just return it. (reloc) [! BFD_ASSEMBLER]: Add third argument to dummy macro. (BFD_RELOC_386_PLT32, _GOT32, _GOTOFF) [! BFD_ASSEMBLER]: More dummy macros. (tc_i386_fix_adjustable): New function. Returns zero if symbol in fixup is not local, to prevent relocations against externals from being dropped. (md_assemble): Initialize disp_reloc field to NO_RELOC. Pass disp_reloc field to reloc() function, and use TC_RELOC to generate value to pass to fix_new_exp. (md_assemble): Change 32-bit reloc against GOT_symbol into a GOTPC reloc. (i386_operand): Initialize disp_reloc field to NO_RELOC. Handle @GOTOFF, @PLT, @GOT operands. For GOTOFF relocations with local symbols, force generation of the section symbol. (md_estimate_size_before_relax): If GOT_symbol exists, decide we're generating PIC code, and convert relocations against undefined symbols from PCREL to PLT32. (md_apply_fix_1) [OBJ_ELF]: Fix up values for dynamic-linking relocs. (md_undefined_symbol): Notice GLOBAL_OFFSET_TABLE_NAME and set and return GOT_symbol if it matches. (F, MAP): Move macro definitions outside function. (tc_gen_reloc): Only switch on size and pcrel if code wasn't already supplied as PLT32. GOT32, GOTOFF, or GOTPC. Convert BFD_RELOC_32 using GOT_symbol into GOTPC. * tc-i386.h (TC_RELOC, tc_fix_adjustable, TC_RELOC_GLOBAL_OFFSET_TABLE, TC_RELOC_RTSYM_LOC_FIXUP): New macros. (NEED_FX_R_TYPE): Define. (LOCAL_LABEL): Accept ".X" prefix too. (GLOBAL_OFFSET_TABLE_NAME): Default to "_GLOBAL_OFFSET_TABLE_". --- diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index e3caab803d3..8ccb17fac16 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -31,6 +31,10 @@ #include "obstack.h" #include "opcode/i386.h" +#ifndef TC_RELOC +#define TC_RELOC(X,Y) (Y) +#endif + /* 'md_assemble ()' gathers together information and puts it into a i386_insn. */ @@ -58,6 +62,13 @@ struct _i386_insn /* Displacements (if given) for each operand. */ expressionS *disps[MAX_OPERANDS]; + /* Relocation type for operand */ +#ifdef BFD_ASSEMBLER + enum bfd_reloc_code_real disp_reloc[MAX_OPERANDS]; +#else + int disp_reloc[MAX_OPERANDS]; +#endif + /* Immediate operands (if given) for each operand. */ expressionS *imms[MAX_OPERANDS]; @@ -137,7 +148,7 @@ static char digit_chars[256]; #define is_digit_char(x) (digit_chars[(unsigned char) x]) /* put here all non-digit non-letter charcters that may occur in an operand */ -static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:"; +static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:[@]"; static char *ordinal_names[] = {"first", "second", "third"}; /* for printfs */ @@ -233,6 +244,8 @@ static reg_entry *parse_register PARAMS ((char *reg_string)); static void s_bss PARAMS ((int)); #endif +symbolS *GOT_symbol; /* Pre-defined "__GLOBAL_OFFSET_TABLE" */ + static INLINE unsigned long mode_from_disp_size (t) unsigned long t; @@ -622,10 +635,13 @@ pt (t) #ifdef BFD_ASSEMBLER static bfd_reloc_code_real_type -reloc (size, pcrel) +reloc (size, pcrel, other) int size; int pcrel; + bfd_reloc_code_real_type other; { + if (other != NO_RELOC) return other; + if (pcrel) switch (size) { @@ -650,20 +666,38 @@ reloc (size, pcrel) return BFD_RELOC_NONE; } #else -#define reloc(SIZE,PCREL) 0 +#define reloc(SIZE,PCREL,OTHER) 0 #define BFD_RELOC_32 0 #define BFD_RELOC_32_PCREL 0 +#define BFD_RELOC_386_PLT32 0 +#define BFD_RELOC_386_GOT32 0 +#define BFD_RELOC_386_GOTOFF 0 #endif +/* + * Here we decide which fixups can be adjusted to make them relative to + * the beginning of the section instead of the symbol. Basically we need + * to make sure that the dynamic relocations are done correctly, so in + * some cases we force the original symbol to be used. + */ +tc_i386_fix_adjustable(fixP) + fixS * fixP; +{ + /* Prevent all adjustments to global symbols. */ + if(!S_IS_LOCAL(fixP->fx_addsy)) return 0; + return 1; +} + /* This is the guts of the machine-dependent assembler. LINE points to a machine dependent instruction. This function is supposed to emit the frags/bytes it assembles to. */ + void md_assemble (line) char *line; { /* Holds template once we've found it. */ - register template *t; + template *t; /* Count the size of the instruction generated. */ int insn_size = 0; @@ -671,8 +705,12 @@ md_assemble (line) /* Possible templates for current insn */ templates *current_templates = (templates *) 0; + int j; + /* Initialize globals. */ memset (&i, '\0', sizeof (i)); + for (j = 0; j < MAX_OPERANDS; j++) + i.disp_reloc[j] = NO_RELOC; memset (disp_expressions, '\0', sizeof (disp_expressions)); memset (im_expressions, '\0', sizeof (im_expressions)); save_stack_p = save_stack; /* reset stack pointer */ @@ -1459,7 +1497,8 @@ md_assemble (line) else { fix_new_exp (frag_now, p - frag_now->fr_literal, size, - i.disps[0], 1, reloc (size, 1)); + i.disps[0], 1, reloc (size, 1, i.disp_reloc[0])); + } } else if (t->opcode_modifier & JumpInterSegment) @@ -1587,7 +1626,8 @@ md_assemble (line) p = frag_more (4); insn_size += 4; fix_new_exp (frag_now, p - frag_now->fr_literal, 4, - i.disps[n], 0, BFD_RELOC_32); + i.disps[n], 0, + TC_RELOC(i.disp_reloc[n], BFD_RELOC_32)); } } } @@ -1631,19 +1671,34 @@ md_assemble (line) } else { /* not absolute_section */ - /* need a 32-bit fixup (don't support 8bit non-absolute ims) */ - /* try to support other sizes ... */ + /* Need a 32-bit fixup (don't support 8bit + non-absolute ims). Try to support other + sizes ... */ + int r_type; int size; + int pcrel = 0; + if (i.types[n] & (Imm8 | Imm8S)) size = 1; else if (i.types[n] & Imm16) size = 2; else size = 4; + r_type = reloc (size, 0, i.disp_reloc[0]); p = frag_more (size); insn_size += size; +#ifdef BFD_ASSEMBLER + if (r_type = BFD_RELOC_32 + && i.imms[n]->X_op == O_symbol + && GOT_symbol + && GOT_symbol == i.imms[n]->X_add_symbol) + { + r_type = BFD_RELOC_386_GOTPC; + i.imms[n]->X_add_number += 3; + } +#endif fix_new_exp (frag_now, p - frag_now->fr_literal, size, - i.imms[n], 0, reloc (size, 0)); + i.imms[n], pcrel, r_type); } } } @@ -1990,11 +2045,61 @@ i386_operand (operand_string) char *save_input_line_pointer; exp = &disp_expressions[i.disp_operands]; i.disps[this_operand] = exp; + i.disp_reloc[this_operand] = NO_RELOC; i.disp_operands++; save_input_line_pointer = input_line_pointer; input_line_pointer = displacement_string_start; END_STRING_AND_SAVE (displacement_string_end); + + { + /* + * We can have operands of the form + * @GOTOFF+ + * Take the easy way out here and copy everything + * into a temporary buffer... + */ + register char *cp; + if (cp = strchr(input_line_pointer,'@')) { + char tmpbuf[BUFSIZ]; + + if(!GOT_symbol) + GOT_symbol = symbol_find_or_make(GLOBAL_OFFSET_TABLE_NAME); + + if (strncmp(cp+1, "PLT", 3) == 0) { + i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32; + *cp = '\0'; + strcpy(tmpbuf, input_line_pointer); + strcat(tmpbuf, cp+1+3); + *cp = '@'; + } else if (strncmp(cp+1, "GOTOFF", 6) == 0) { + i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF; + *cp = '\0'; + strcpy(tmpbuf, input_line_pointer); + strcat(tmpbuf, cp+1+6); + *cp = '@'; + } else if (strncmp(cp+1, "GOT", 3) == 0) { + i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32; + *cp = '\0'; + strcpy(tmpbuf, input_line_pointer); + strcat(tmpbuf, cp+1+3); + *cp = '@'; + } else + as_bad("Bad reloc specifier '%s' in expression", cp+1); + input_line_pointer = tmpbuf; + } + } + exp_seg = expression (exp); + +#ifdef BFD_ASSEMBLER + /* We do this to make sure that the section symbol is in + the symbol table. We will ultimately change the relocation + to be relative to the beginning of the section */ + if(i.disp_reloc[this_operand] == BFD_RELOC_386_GOTOFF && + S_IS_LOCAL(exp->X_add_symbol)) + section_symbol(exp->X_add_symbol->bsym->section); +#endif + if (*input_line_pointer) as_bad ("Ignoring junk '%s' after expression", input_line_pointer); RESTORE_END_STRING (displacement_string_end); @@ -2103,8 +2208,17 @@ md_estimate_size_before_relax (fragP, segment) opcode[0] = 0xe9; /* dword disp jmp */ fragP->fr_fix += 4; fix_new (fragP, old_fr_fix, 4, - fragP->fr_symbol, - fragP->fr_offset, 1, BFD_RELOC_32_PCREL); + fragP->fr_symbol, + fragP->fr_offset, 1, + (GOT_symbol && /* Not quite right - we should switch on + presence of @PLT, but I cannot see how + to get to that from here. We should have + done this in md_assemble to really + get it right all of the time, but I + think it does not matter that much, as + this will be right most of the time. ERY*/ + S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)? + BFD_RELOC_386_PLT32 : BFD_RELOC_32_PCREL); break; default: @@ -2115,7 +2229,12 @@ md_estimate_size_before_relax (fragP, segment) fragP->fr_fix += 1 + 4; /* we've added an opcode byte */ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol, - fragP->fr_offset, 1, BFD_RELOC_32_PCREL); + fragP->fr_offset, 1, + (GOT_symbol && /* Not quite right - we should switch on + presence of @PLT, but I cannot see how + to get to that from here. ERY */ + S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)? + BFD_RELOC_386_PLT32 : BFD_RELOC_32_PCREL); break; } frag_wane (fragP); @@ -2303,6 +2422,68 @@ md_apply_fix_1 (fixP, value) } #endif } + + /* Fix a few things - the dynamic linker expects certain values here, + and we must not dissappoint it. */ +#ifdef OBJ_ELF + if(fixP->fx_addsy) + switch(fixP->fx_r_type) { + case BFD_RELOC_386_PLT32: + /* Make the jump instruction point to the address of the operand. At + runtime we merely add the offset to the actual PLT entry. */ + value = 0xfffffffc; + break; + case BFD_RELOC_386_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. The actual sequence of instructions often look + * something like: + * + * call .L66 + * .L66: + * popl %ebx + * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx + * + * The call and pop essentially return the absolute address of + * the label .L66 and store it in %ebx. The linker itself will + * ultimately change the first operand of the addl so that %ebx points to + * the GOT, but to keep things simple, the .o file must have this operand + * set so that it generates not the absolute address of .L66, but the + * absolute address of itself. This allows the linker itself simply + * treat a GOTPC relocation as asking for a pcrel offset to the GOT to be + * added in, and the addend of the relocation is stored in the operand + * field for the instruction itself. + * + * Our job here is to fix the operand so that it would add the correct + * offset so that %ebx would point to itself. The thing that is tricky is + * that .-.L66 will point to the beginning of the instruction, so we need + * to further modify the operand so that it will point to itself. + * There are other 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. + */ + value += fixP->fx_where + fixP->fx_frag->fr_address - fixP->fx_offset; + break; + case BFD_RELOC_386_GOT32: + value = 0; /* Fully resolved at runtime. No addend. */ + case BFD_RELOC_386_GOTOFF: + /* Here everything should be correct already. Just wanted to mention + this explicitly so no one things I forgot it. */ + default: + break; + } +#endif + #endif md_number_to_chars (p, value, fixP->fx_size); } @@ -2483,6 +2664,18 @@ symbolS * md_undefined_symbol (name) char *name; { + if (*name == '_' && *(name+1) == 'G' + && strcmp(name, GLOBAL_OFFSET_TABLE_NAME) == 0) + { + if(!GOT_symbol) + { + if(symbol_find(name)) + as_bad("GOT already in symbol table"); + GOT_symbol = symbol_new (name, undefined_section, + (valueT) 0, &zero_address_frag); + }; + return GOT_symbol; + } return 0; } @@ -2533,6 +2726,8 @@ s_bss (ignore) #ifdef BFD_ASSEMBLER +#define F(SZ,PCREL) (((SZ) << 1) + (PCREL)) +#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break arelent * tc_gen_reloc (section, fixp) @@ -2542,27 +2737,40 @@ tc_gen_reloc (section, fixp) arelent *rel; bfd_reloc_code_real_type code; -#define F(SZ,PCREL) (((SZ) << 1) + (PCREL)) - switch (F (fixp->fx_size, fixp->fx_pcrel)) + switch(fixp->fx_r_type) { -#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break + case BFD_RELOC_386_PLT32: + case BFD_RELOC_386_GOT32: + case BFD_RELOC_386_GOTOFF: + case BFD_RELOC_386_GOTPC: + code = fixp->fx_r_type; + break; + default: + switch (F (fixp->fx_size, fixp->fx_pcrel)) + { #ifndef OBJ_ELF - MAP (1, 0, BFD_RELOC_8); - MAP (2, 0, BFD_RELOC_16); + MAP (1, 0, BFD_RELOC_8); + MAP (2, 0, BFD_RELOC_16); #endif - MAP (4, 0, BFD_RELOC_32); + MAP (4, 0, BFD_RELOC_32); #ifndef OBJ_ELF - MAP (1, 1, BFD_RELOC_8_PCREL); - MAP (2, 1, BFD_RELOC_16_PCREL); + MAP (1, 1, BFD_RELOC_8_PCREL); + MAP (2, 1, BFD_RELOC_16_PCREL); #endif - MAP (4, 1, BFD_RELOC_32_PCREL); - default: - as_bad ("Can not do %d byte %srelocation", fixp->fx_size, - fixp->fx_pcrel ? "pc-relative" : ""); + MAP (4, 1, BFD_RELOC_32_PCREL); + default: + as_bad ("Can not do %d byte %srelocation", fixp->fx_size, + fixp->fx_pcrel ? "pc-relative" : ""); + } } #undef MAP #undef F + if (code == BFD_RELOC_32 + && GOT_symbol + && fixp->fx_addsy == GOT_symbol) + code = BFD_RELOC_386_GOTPC; + rel = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent)); assert (rel != 0); rel->sym_ptr_ptr = &fixp->fx_addsy->bsym;