* 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_".
This commit is contained in:
Ken Raeburn 1994-09-13 02:15:23 +00:00
parent d9d6f09465
commit f0b37b4ad6

View file

@ -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
* <symbol>@GOTOFF+<nnn>
* 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
#ifndef OBJ_ELF
MAP (1, 0, BFD_RELOC_8);
MAP (2, 0, BFD_RELOC_16);
#endif
MAP (4, 0, BFD_RELOC_32);
#ifndef OBJ_ELF
MAP (1, 1, BFD_RELOC_8_PCREL);
MAP (2, 1, BFD_RELOC_16_PCREL);
#endif
MAP (4, 1, BFD_RELOC_32_PCREL);
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:
as_bad ("Can not do %d byte %srelocation", fixp->fx_size,
fixp->fx_pcrel ? "pc-relative" : "");
switch (F (fixp->fx_size, fixp->fx_pcrel))
{
#ifndef OBJ_ELF
MAP (1, 0, BFD_RELOC_8);
MAP (2, 0, BFD_RELOC_16);
#endif
MAP (4, 0, BFD_RELOC_32);
#ifndef OBJ_ELF
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" : "");
}
}
#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;