* coff-mips.c (mips_howto_table): Add dummy entries to account for
numbering changes in include/coff/mips.h. Add entries for MIPS_R_RELHI and MIPS_R_RELLO. (mips_ecoff_swap_reloc_in): Handle an extra bit for the reloc type when little endian. Treat internal MIPS_R_RELLO or MIPS_R_RELHI relocs like MIPS_R_SWITCH, and convert r_offset from 24 to 32 bits. (mips_ecoff_swap_reloc_out): Likewise. (mips_adjust_reloc_in): Handle internal MIPS_R_RELLO or MIPS_R_RELHI relocs like MIPS_R_SWITCH. (mips_adjust_reloc_out): Likewise. (mips_relhi_addr, mips_relhi_addend): New static variables. (mips_relhi_reloc, mips_rello_reloc): New functions. (mips_bfd_reloc_type_lookup): Turn BFD_RELOC_PCREL_HI16_S into MIPS_R_RELHI and turn BFD_RELOC_PCREL_LO16 into MIPS_R_RELLO. (mips_relocate_hi): Rename from mips_relocate_refhi, and add pcrel argument. Changed all callers. (mips_relocate_section): Rename got_reflo to got_lo and reflo_int_rel to lo_int_rel. Handle MIPS_R_RELLO and MIPS_R_RELHI relocs. (mips_relax_section): Adjust MIPS_R_RELHI/MIPS_R_RELLO pairs when expanding a PC relative call.
This commit is contained in:
parent
7efb18503d
commit
4f996613fa
|
@ -1,3 +1,32 @@
|
|||
Thu Apr 14 13:05:10 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
|
||||
|
||||
* coff-mips.c (mips_howto_table): Add dummy entries to account for
|
||||
numbering changes in include/coff/mips.h. Add entries for
|
||||
MIPS_R_RELHI and MIPS_R_RELLO.
|
||||
(mips_ecoff_swap_reloc_in): Handle an extra bit for the reloc type
|
||||
when little endian. Treat internal MIPS_R_RELLO or MIPS_R_RELHI
|
||||
relocs like MIPS_R_SWITCH, and convert r_offset from 24 to 32
|
||||
bits.
|
||||
(mips_ecoff_swap_reloc_out): Likewise.
|
||||
(mips_adjust_reloc_in): Handle internal MIPS_R_RELLO or
|
||||
MIPS_R_RELHI relocs like MIPS_R_SWITCH.
|
||||
(mips_adjust_reloc_out): Likewise.
|
||||
(mips_relhi_addr, mips_relhi_addend): New static variables.
|
||||
(mips_relhi_reloc, mips_rello_reloc): New functions.
|
||||
(mips_bfd_reloc_type_lookup): Turn BFD_RELOC_PCREL_HI16_S into
|
||||
MIPS_R_RELHI and turn BFD_RELOC_PCREL_LO16 into MIPS_R_RELLO.
|
||||
(mips_relocate_hi): Rename from mips_relocate_refhi, and add pcrel
|
||||
argument. Changed all callers.
|
||||
(mips_relocate_section): Rename got_reflo to got_lo and
|
||||
reflo_int_rel to lo_int_rel. Handle MIPS_R_RELLO and MIPS_R_RELHI
|
||||
relocs.
|
||||
(mips_relax_section): Adjust MIPS_R_RELHI/MIPS_R_RELLO pairs when
|
||||
expanding a PC relative call.
|
||||
|
||||
* reloc.c (bfd_reloc_code_real_type): Add BFD_RELOC_PCREL_HI16_S
|
||||
and BFD_RELOC_PCREL_LO16.
|
||||
* bfd-in2.h: Rebuilt.
|
||||
|
||||
Tue Apr 12 13:36:20 1994 Jeffrey A. Law (law@snake.cs.utah.edu)
|
||||
|
||||
* som.c (som_write_fixups): Always emit at least
|
||||
|
|
594
bfd/coff-mips.c
594
bfd/coff-mips.c
|
@ -72,6 +72,20 @@ static bfd_reloc_status_type mips_gprel_reloc PARAMS ((bfd *abfd,
|
|||
asection *section,
|
||||
bfd *output_bfd,
|
||||
char **error));
|
||||
static bfd_reloc_status_type mips_relhi_reloc PARAMS ((bfd *abfd,
|
||||
arelent *reloc,
|
||||
asymbol *symbol,
|
||||
PTR data,
|
||||
asection *section,
|
||||
bfd *output_bfd,
|
||||
char **error));
|
||||
static bfd_reloc_status_type mips_rello_reloc PARAMS ((bfd *abfd,
|
||||
arelent *reloc,
|
||||
asymbol *symbol,
|
||||
PTR data,
|
||||
asection *section,
|
||||
bfd *output_bfd,
|
||||
char **error));
|
||||
static bfd_reloc_status_type mips_switch_reloc PARAMS ((bfd *abfd,
|
||||
arelent *reloc,
|
||||
asymbol *symbol,
|
||||
|
@ -79,13 +93,14 @@ static bfd_reloc_status_type mips_switch_reloc PARAMS ((bfd *abfd,
|
|||
asection *section,
|
||||
bfd *output_bfd,
|
||||
char **error));
|
||||
static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi,
|
||||
struct internal_reloc *reflo,
|
||||
bfd *input_bfd,
|
||||
asection *input_section,
|
||||
bfd_byte *contents,
|
||||
size_t adjust,
|
||||
bfd_vma relocation));
|
||||
static void mips_relocate_hi PARAMS ((struct internal_reloc *refhi,
|
||||
struct internal_reloc *reflo,
|
||||
bfd *input_bfd,
|
||||
asection *input_section,
|
||||
bfd_byte *contents,
|
||||
size_t adjust,
|
||||
bfd_vma relocation,
|
||||
boolean pcrel));
|
||||
static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *,
|
||||
bfd *, asection *,
|
||||
bfd_byte *, PTR));
|
||||
|
@ -253,6 +268,11 @@ static reloc_howto_type mips_howto_table[] =
|
|||
0xffff, /* dst_mask */
|
||||
false), /* pcrel_offset */
|
||||
|
||||
{ 8 },
|
||||
{ 9 },
|
||||
{ 10 },
|
||||
{ 11 },
|
||||
|
||||
/* This reloc is a Cygnus extension used when generating position
|
||||
independent code for embedded systems. It represents a 16 bit PC
|
||||
relative reloc rightshifted twice as used in the MIPS branch
|
||||
|
@ -271,6 +291,52 @@ static reloc_howto_type mips_howto_table[] =
|
|||
0xffff, /* dst_mask */
|
||||
true), /* pcrel_offset */
|
||||
|
||||
/* This reloc is a Cygnus extension used when generating position
|
||||
independent code for embedded systems. It represents the high 16
|
||||
bits of a PC relative reloc. The next reloc must be
|
||||
MIPS_R_RELLO, and the addend is formed from the addends of the
|
||||
two instructions, just as in MIPS_R_REFHI and MIPS_R_REFLO. The
|
||||
final value is actually PC relative to the location of the
|
||||
MIPS_R_RELLO reloc, not the MIPS_R_RELHI reloc. */
|
||||
HOWTO (MIPS_R_RELHI, /* type */
|
||||
16, /* rightshift */
|
||||
2, /* size (0 = byte, 1 = short, 2 = long) */
|
||||
16, /* bitsize */
|
||||
true, /* pc_relative */
|
||||
0, /* bitpos */
|
||||
complain_overflow_bitfield, /* complain_on_overflow */
|
||||
mips_relhi_reloc, /* special_function */
|
||||
"RELHI", /* name */
|
||||
true, /* partial_inplace */
|
||||
0xffff, /* src_mask */
|
||||
0xffff, /* dst_mask */
|
||||
true), /* pcrel_offset */
|
||||
|
||||
/* This reloc is a Cygnus extension used when generating position
|
||||
independent code for embedded systems. It represents the low 16
|
||||
bits of a PC relative reloc. */
|
||||
HOWTO (MIPS_R_RELLO, /* type */
|
||||
0, /* rightshift */
|
||||
2, /* size (0 = byte, 1 = short, 2 = long) */
|
||||
16, /* bitsize */
|
||||
true, /* pc_relative */
|
||||
0, /* bitpos */
|
||||
complain_overflow_dont, /* complain_on_overflow */
|
||||
mips_rello_reloc, /* special_function */
|
||||
"RELLO", /* name */
|
||||
true, /* partial_inplace */
|
||||
0xffff, /* src_mask */
|
||||
0xffff, /* dst_mask */
|
||||
true), /* pcrel_offset */
|
||||
|
||||
{ 15 },
|
||||
{ 16 },
|
||||
{ 17 },
|
||||
{ 18 },
|
||||
{ 19 },
|
||||
{ 20 },
|
||||
{ 21 },
|
||||
|
||||
/* This reloc is a Cygnus extension used when generating position
|
||||
independent code for embedded systems. It represents an entry in
|
||||
a switch table, which is the difference between two symbols in
|
||||
|
@ -376,21 +442,29 @@ mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern)
|
|||
<< RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE)
|
||||
| ((int) ext->r_bits[2]
|
||||
<< RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE));
|
||||
intern->r_type = ((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
|
||||
>> RELOC_BITS3_TYPE_SH_LITTLE);
|
||||
intern->r_type = (((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
|
||||
>> RELOC_BITS3_TYPE_SH_LITTLE)
|
||||
| ((ext->r_bits[3] & RELOC_BITS3_TYPEHI_LITTLE)
|
||||
<< RELOC_BITS3_TYPEHI_SH_LITTLE));
|
||||
intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0;
|
||||
}
|
||||
|
||||
/* If this is a MIPS_R_SWITCH reloc, r_symndx is actually the offset
|
||||
from the reloc address to the base of the difference (see
|
||||
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
|
||||
MIPS_R_RELLO reloc, r_symndx is actually the offset from the
|
||||
reloc address to the base of the difference (see
|
||||
include/coff/mips.h for more details). We copy symndx into the
|
||||
r_offset field so as not to confuse ecoff_slurp_reloc_table in
|
||||
ecoff.c. In adjust_reloc_in we then copy r_offset into the reloc
|
||||
addend. */
|
||||
if (intern->r_type == MIPS_R_SWITCH)
|
||||
if (intern->r_type == MIPS_R_SWITCH
|
||||
|| (! intern->r_extern
|
||||
&& (intern->r_type == MIPS_R_RELLO
|
||||
|| intern->r_type == MIPS_R_RELHI)))
|
||||
{
|
||||
BFD_ASSERT (! intern->r_extern);
|
||||
intern->r_offset = intern->r_symndx;
|
||||
if (intern->r_offset & 0x800000)
|
||||
intern->r_offset -= 0x1000000;
|
||||
intern->r_symndx = RELOC_SECTION_TEXT;
|
||||
}
|
||||
}
|
||||
|
@ -409,15 +483,19 @@ mips_ecoff_swap_reloc_out (abfd, intern, dst)
|
|||
BFD_ASSERT (intern->r_extern
|
||||
|| (intern->r_symndx >= 0 && intern->r_symndx <= 12));
|
||||
|
||||
/* If this is a MIPS_R_SWITCH reloc, we actually want to write the
|
||||
contents of r_offset out as the symbol index. This undoes the
|
||||
change made by mips_ecoff_swap_reloc_in. */
|
||||
if (intern->r_type != MIPS_R_SWITCH)
|
||||
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELLO or
|
||||
MIPS_R_RELHI reloc, we actually want to write the contents of
|
||||
r_offset out as the symbol index. This undoes the change made by
|
||||
mips_ecoff_swap_reloc_in. */
|
||||
if (intern->r_type != MIPS_R_SWITCH
|
||||
&& (intern->r_extern
|
||||
|| (intern->r_type != MIPS_R_RELHI
|
||||
&& intern->r_type != MIPS_R_RELLO)))
|
||||
r_symndx = intern->r_symndx;
|
||||
else
|
||||
{
|
||||
BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT);
|
||||
r_symndx = intern->r_offset;
|
||||
r_symndx = intern->r_offset & 0xffffff;
|
||||
}
|
||||
|
||||
bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) ext->r_vaddr);
|
||||
|
@ -437,6 +515,8 @@ mips_ecoff_swap_reloc_out (abfd, intern, dst)
|
|||
ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE;
|
||||
ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_LITTLE)
|
||||
& RELOC_BITS3_TYPE_LITTLE)
|
||||
| ((intern->r_type >> RELOC_BITS3_TYPEHI_SH_LITTLE
|
||||
& RELOC_BITS3_TYPEHI_LITTLE))
|
||||
| (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0));
|
||||
}
|
||||
}
|
||||
|
@ -464,12 +544,16 @@ mips_adjust_reloc_in (abfd, intern, rptr)
|
|||
if (intern->r_type == MIPS_R_IGNORE)
|
||||
rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr;
|
||||
|
||||
/* If this is a MIPS_R_SWITCH reloc, we want the addend field of the
|
||||
BFD relocto hold the value which was originally in the symndx
|
||||
field of the internal MIPS ECOFF reloc. This value was copied
|
||||
into intern->r_offset by mips_swap_reloc_in, and here we copy it
|
||||
into the addend field. */
|
||||
if (intern->r_type == MIPS_R_SWITCH)
|
||||
/* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
|
||||
MIPS_R_RELLO reloc, we want the addend field of the BFD relocto
|
||||
hold the value which was originally in the symndx field of the
|
||||
internal MIPS ECOFF reloc. This value was copied into
|
||||
intern->r_offset by mips_swap_reloc_in, and here we copy it into
|
||||
the addend field. */
|
||||
if (intern->r_type == MIPS_R_SWITCH
|
||||
|| (! intern->r_extern
|
||||
&& (intern->r_type == MIPS_R_RELHI
|
||||
|| intern->r_type == MIPS_R_RELLO)))
|
||||
rptr->addend = intern->r_offset;
|
||||
|
||||
rptr->howto = &mips_howto_table[intern->r_type];
|
||||
|
@ -484,11 +568,15 @@ mips_adjust_reloc_out (abfd, rel, intern)
|
|||
const arelent *rel;
|
||||
struct internal_reloc *intern;
|
||||
{
|
||||
/* For a MIPS_R_SWITCH reloc we must copy rel->addend into
|
||||
/* For a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
|
||||
MIPS_R_RELLO reloc, we must copy rel->addend into
|
||||
intern->r_offset. This will then be written out as the symbol
|
||||
index by mips_ecoff_swap_reloc_out. This operation parallels the
|
||||
action of mips_adjust_reloc_in. */
|
||||
if (intern->r_type == MIPS_R_SWITCH)
|
||||
if (intern->r_type == MIPS_R_SWITCH
|
||||
|| (! intern->r_extern
|
||||
&& (intern->r_type == MIPS_R_RELHI
|
||||
|| intern->r_type == MIPS_R_RELLO)))
|
||||
intern->r_offset = rel->addend;
|
||||
}
|
||||
|
||||
|
@ -790,6 +878,166 @@ mips_gprel_reloc (abfd,
|
|||
return bfd_reloc_ok;
|
||||
}
|
||||
|
||||
/* Do a RELHI relocation. We do this in conjunction with a RELLO
|
||||
reloc, just as REFHI and REFLO are done together. RELHI and RELLO
|
||||
are Cygnus extensions used when generating position independent
|
||||
code for embedded systems. */
|
||||
|
||||
static bfd_byte *mips_relhi_addr;
|
||||
static bfd_vma mips_relhi_addend;
|
||||
|
||||
static bfd_reloc_status_type
|
||||
mips_relhi_reloc (abfd,
|
||||
reloc_entry,
|
||||
symbol,
|
||||
data,
|
||||
input_section,
|
||||
output_bfd,
|
||||
error_message)
|
||||
bfd *abfd;
|
||||
arelent *reloc_entry;
|
||||
asymbol *symbol;
|
||||
PTR data;
|
||||
asection *input_section;
|
||||
bfd *output_bfd;
|
||||
char **error_message;
|
||||
{
|
||||
bfd_reloc_status_type ret;
|
||||
bfd_vma relocation;
|
||||
|
||||
/* If this is a reloc against a section symbol, then it is correct
|
||||
in the object file. The only time we want to change this case is
|
||||
when we are relaxing, and that is handled entirely by
|
||||
mips_relocate_section and never calls this function. */
|
||||
if ((symbol->flags & BSF_SECTION_SYM) != 0)
|
||||
{
|
||||
if (output_bfd != (bfd *) NULL)
|
||||
reloc_entry->address += input_section->output_offset;
|
||||
return bfd_reloc_ok;
|
||||
}
|
||||
|
||||
/* This is an external symbol. If we're relocating, we don't want
|
||||
to change anything. */
|
||||
if (output_bfd != (bfd *) NULL)
|
||||
{
|
||||
reloc_entry->address += input_section->output_offset;
|
||||
return bfd_reloc_ok;
|
||||
}
|
||||
|
||||
ret = bfd_reloc_ok;
|
||||
if (symbol->section == &bfd_und_section
|
||||
&& output_bfd == (bfd *) NULL)
|
||||
ret = bfd_reloc_undefined;
|
||||
|
||||
if (bfd_is_com_section (symbol->section))
|
||||
relocation = 0;
|
||||
else
|
||||
relocation = symbol->value;
|
||||
|
||||
relocation += symbol->section->output_section->vma;
|
||||
relocation += symbol->section->output_offset;
|
||||
relocation += reloc_entry->addend;
|
||||
|
||||
if (reloc_entry->address > input_section->_cooked_size)
|
||||
return bfd_reloc_outofrange;
|
||||
|
||||
/* Save the information, and let RELLO do the actual relocation. */
|
||||
mips_relhi_addr = (bfd_byte *) data + reloc_entry->address;
|
||||
mips_relhi_addend = relocation;
|
||||
|
||||
if (output_bfd != (bfd *) NULL)
|
||||
reloc_entry->address += input_section->output_offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do a RELLO relocation. This is a straightforward 16 bit PC
|
||||
relative relocation; this function exists in order to do the RELHI
|
||||
relocation described above. */
|
||||
|
||||
static bfd_reloc_status_type
|
||||
mips_rello_reloc (abfd,
|
||||
reloc_entry,
|
||||
symbol,
|
||||
data,
|
||||
input_section,
|
||||
output_bfd,
|
||||
error_message)
|
||||
bfd *abfd;
|
||||
arelent *reloc_entry;
|
||||
asymbol *symbol;
|
||||
PTR data;
|
||||
asection *input_section;
|
||||
bfd *output_bfd;
|
||||
char **error_message;
|
||||
{
|
||||
if (mips_relhi_addr != (bfd_byte *) NULL)
|
||||
{
|
||||
unsigned long insn;
|
||||
unsigned long val;
|
||||
unsigned long vallo;
|
||||
|
||||
/* Do the RELHI relocation. Note that we actually don't need to
|
||||
know anything about the RELLO itself, except where to find
|
||||
the low 16 bits of the addend needed by the RELHI. */
|
||||
insn = bfd_get_32 (abfd, mips_relhi_addr);
|
||||
vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
|
||||
& 0xffff);
|
||||
val = ((insn & 0xffff) << 16) + vallo;
|
||||
val += mips_relhi_addend;
|
||||
|
||||
/* If the symbol is defined, make val PC relative. If the
|
||||
symbol is not defined we don't want to do this, because we
|
||||
don't want the value in the object file to incorporate the
|
||||
address of the reloc. */
|
||||
if (bfd_get_section (symbol) != &bfd_und_section
|
||||
&& ! bfd_is_com_section (bfd_get_section (symbol)))
|
||||
val -= (input_section->output_section->vma
|
||||
+ input_section->output_offset
|
||||
+ reloc_entry->address);
|
||||
|
||||
/* The low order 16 bits are always treated as a signed value.
|
||||
Therefore, a negative value in the low order bits requires an
|
||||
adjustment in the high order bits. We need to make this
|
||||
adjustment in two ways: once for the bits we took from the
|
||||
data, and once for the bits we are putting back in to the
|
||||
data. */
|
||||
if ((vallo & 0x8000) != 0)
|
||||
val -= 0x10000;
|
||||
if ((val & 0x8000) != 0)
|
||||
val += 0x10000;
|
||||
|
||||
insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
|
||||
bfd_put_32 (abfd, insn, mips_relhi_addr);
|
||||
|
||||
mips_relhi_addr = (bfd_byte *) NULL;
|
||||
}
|
||||
|
||||
/* If this is a reloc against a section symbol, then it is correct
|
||||
in the object file. The only time we want to change this case is
|
||||
when we are relaxing, and that is handled entirely by
|
||||
mips_relocate_section and never calls this function. */
|
||||
if ((symbol->flags & BSF_SECTION_SYM) != 0)
|
||||
{
|
||||
if (output_bfd != (bfd *) NULL)
|
||||
reloc_entry->address += input_section->output_offset;
|
||||
return bfd_reloc_ok;
|
||||
}
|
||||
|
||||
/* bfd_perform_relocation does not handle pcrel_offset relocations
|
||||
correctly when generating a relocateable file, so handle them
|
||||
directly here. */
|
||||
if (output_bfd != (bfd *) NULL)
|
||||
{
|
||||
reloc_entry->address += input_section->output_offset;
|
||||
return bfd_reloc_ok;
|
||||
}
|
||||
|
||||
/* Now do the RELLO reloc in the usual way. */
|
||||
return mips_generic_reloc (abfd, reloc_entry, symbol, data,
|
||||
input_section, output_bfd, error_message);
|
||||
}
|
||||
|
||||
/* This is the special function for the MIPS_R_SWITCH reloc. This
|
||||
special reloc is normally correct in the object file, and only
|
||||
requires special handling when relaxing. We don't want
|
||||
|
@ -851,6 +1099,12 @@ mips_bfd_reloc_type_lookup (abfd, code)
|
|||
case BFD_RELOC_16_PCREL_S2:
|
||||
mips_type = MIPS_R_PCREL16;
|
||||
break;
|
||||
case BFD_RELOC_PCREL_HI16_S:
|
||||
mips_type = MIPS_R_RELHI;
|
||||
break;
|
||||
case BFD_RELOC_PCREL_LO16:
|
||||
mips_type = MIPS_R_RELLO;
|
||||
break;
|
||||
case BFD_RELOC_GPREL32:
|
||||
mips_type = MIPS_R_SWITCH;
|
||||
break;
|
||||
|
@ -862,13 +1116,13 @@ mips_bfd_reloc_type_lookup (abfd, code)
|
|||
}
|
||||
|
||||
/* A helper routine for mips_relocate_section which handles the REFHI
|
||||
relocation. The REFHI relocation must be followed by a REFLO
|
||||
relocation, and the addend used is formed from the addends of both
|
||||
instructions. */
|
||||
and RELHI relocations. The REFHI relocation must be followed by a
|
||||
REFLO relocation (and RELHI by a RELLO), and the addend used is
|
||||
formed from the addends of both instructions. */
|
||||
|
||||
static void
|
||||
mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
|
||||
adjust, relocation)
|
||||
mips_relocate_hi (refhi, reflo, input_bfd, input_section, contents, adjust,
|
||||
relocation, pcrel)
|
||||
struct internal_reloc *refhi;
|
||||
struct internal_reloc *reflo;
|
||||
bfd *input_bfd;
|
||||
|
@ -876,6 +1130,7 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
|
|||
bfd_byte *contents;
|
||||
size_t adjust;
|
||||
bfd_vma relocation;
|
||||
boolean pcrel;
|
||||
{
|
||||
unsigned long insn;
|
||||
unsigned long val;
|
||||
|
@ -896,6 +1151,12 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
|
|||
and once for the bits we are putting back in to the data. */
|
||||
if ((vallo & 0x8000) != 0)
|
||||
val -= 0x10000;
|
||||
|
||||
if (pcrel)
|
||||
val -= (input_section->output_section->vma
|
||||
+ input_section->output_offset
|
||||
+ (reflo->r_vaddr - input_section->vma + adjust));
|
||||
|
||||
if ((val & 0x8000) != 0)
|
||||
val += 0x10000;
|
||||
|
||||
|
@ -925,8 +1186,8 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
struct external_reloc *ext_rel;
|
||||
struct external_reloc *ext_rel_end;
|
||||
unsigned int i;
|
||||
boolean got_reflo;
|
||||
struct internal_reloc reflo_int_rel;
|
||||
boolean got_lo;
|
||||
struct internal_reloc lo_int_rel;
|
||||
|
||||
BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p
|
||||
== output_bfd->xvec->header_byteorder_big_p);
|
||||
|
@ -984,7 +1245,7 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
else
|
||||
gp_undefined = false;
|
||||
|
||||
got_reflo = false;
|
||||
got_lo = false;
|
||||
|
||||
adjust = 0;
|
||||
|
||||
|
@ -1005,29 +1266,33 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
bfd_vma relocation;
|
||||
bfd_reloc_status_type r;
|
||||
|
||||
if (! got_reflo)
|
||||
if (! got_lo)
|
||||
mips_ecoff_swap_reloc_in (input_bfd, (PTR) ext_rel, &int_rel);
|
||||
else
|
||||
{
|
||||
int_rel = reflo_int_rel;
|
||||
got_reflo = false;
|
||||
int_rel = lo_int_rel;
|
||||
got_lo = false;
|
||||
}
|
||||
|
||||
BFD_ASSERT (int_rel.r_type
|
||||
< sizeof mips_howto_table / sizeof mips_howto_table[0]);
|
||||
|
||||
/* The REFHI reloc requires special handling. It must be
|
||||
followed by a REFLO reloc, and the addend is formed from both
|
||||
fields. */
|
||||
if (int_rel.r_type == MIPS_R_REFHI)
|
||||
/* The REFHI and RELHI relocs requires special handling. they
|
||||
must be followed by a REFLO or RELLO reloc, respectively, and
|
||||
the addend is formed from both relocs. */
|
||||
if (int_rel.r_type == MIPS_R_REFHI
|
||||
|| int_rel.r_type == MIPS_R_RELHI)
|
||||
{
|
||||
BFD_ASSERT ((ext_rel + 1) < ext_rel_end);
|
||||
mips_ecoff_swap_reloc_in (input_bfd, (PTR) (ext_rel + 1),
|
||||
&reflo_int_rel);
|
||||
BFD_ASSERT (reflo_int_rel.r_type == MIPS_R_REFLO
|
||||
&& int_rel.r_extern == reflo_int_rel.r_extern
|
||||
&& int_rel.r_symndx == reflo_int_rel.r_symndx);
|
||||
got_reflo = true;
|
||||
&lo_int_rel);
|
||||
BFD_ASSERT ((lo_int_rel.r_type
|
||||
== (int_rel.r_type == MIPS_R_REFHI
|
||||
? MIPS_R_REFLO
|
||||
: MIPS_R_RELLO))
|
||||
&& int_rel.r_extern == lo_int_rel.r_extern
|
||||
&& int_rel.r_symndx == lo_int_rel.r_symndx);
|
||||
got_lo = true;
|
||||
}
|
||||
|
||||
howto = &mips_howto_table[int_rel.r_type];
|
||||
|
@ -1139,17 +1404,17 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
&& offsets[i] != 0)
|
||||
{
|
||||
BFD_ASSERT (! info->relocateable);
|
||||
BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16);
|
||||
BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16
|
||||
|| int_rel.r_type == MIPS_R_RELHI
|
||||
|| int_rel.r_type == MIPS_R_RELLO);
|
||||
if (offsets[i] != 1)
|
||||
{
|
||||
BFD_ASSERT (! int_rel.r_extern);
|
||||
addend += offsets[i];
|
||||
}
|
||||
addend += offsets[i];
|
||||
else
|
||||
{
|
||||
bfd_byte *here;
|
||||
|
||||
BFD_ASSERT (int_rel.r_extern);
|
||||
BFD_ASSERT (int_rel.r_extern
|
||||
&& int_rel.r_type == MIPS_R_PCREL16);
|
||||
|
||||
/* Move the rest of the instructions up. */
|
||||
here = (contents
|
||||
|
@ -1254,7 +1519,43 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
currently holds just the addend. We must adjust
|
||||
by the address to get the right value. */
|
||||
if (howto->pc_relative)
|
||||
relocation -= int_rel.r_vaddr - input_section->vma;
|
||||
{
|
||||
relocation -= int_rel.r_vaddr - input_section->vma;
|
||||
|
||||
/* If we are converting a RELHI or RELLO reloc
|
||||
from being against an external symbol to
|
||||
being against a section, we must put a
|
||||
special value into the r_offset field. This
|
||||
value is the old addend. The r_offset for
|
||||
both the RELOHI and RELLO relocs are the
|
||||
same, and we set both when we see RELHI. */
|
||||
if (int_rel.r_type == MIPS_R_RELHI)
|
||||
{
|
||||
long addhi, addlo;
|
||||
|
||||
addhi = bfd_get_32 (input_bfd,
|
||||
(contents
|
||||
+ adjust
|
||||
+ int_rel.r_vaddr
|
||||
- input_section->vma));
|
||||
addhi &= 0xffff;
|
||||
if (addhi & 0x8000)
|
||||
addhi -= 0x10000;
|
||||
addhi <<= 16;
|
||||
|
||||
addlo = bfd_get_32 (input_bfd,
|
||||
(contents
|
||||
+ adjust
|
||||
+ lo_int_rel.r_vaddr
|
||||
- input_section->vma));
|
||||
addlo &= 0xffff;
|
||||
if (addlo & 0x8000)
|
||||
addlo -= 0x10000;
|
||||
|
||||
int_rel.r_offset = addhi + addlo;
|
||||
lo_int_rel.r_offset = int_rel.r_offset;
|
||||
}
|
||||
}
|
||||
|
||||
h = NULL;
|
||||
}
|
||||
|
@ -1289,8 +1590,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
|
||||
/* Adjust a PC relative relocation by removing the reference
|
||||
to the original address in the section and including the
|
||||
reference to the new address. */
|
||||
if (howto->pc_relative)
|
||||
reference to the new address. However, external RELHI
|
||||
and RELLO relocs are PC relative, but don't include any
|
||||
reference to the address. The addend is merely an
|
||||
addend. */
|
||||
if (howto->pc_relative
|
||||
&& (! int_rel.r_extern
|
||||
|| (int_rel.r_type != MIPS_R_RELHI
|
||||
&& int_rel.r_type != MIPS_R_RELLO)))
|
||||
relocation -= (input_section->output_section->vma
|
||||
+ input_section->output_offset
|
||||
- input_section->vma);
|
||||
|
@ -1300,7 +1607,8 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
r = bfd_reloc_ok;
|
||||
else
|
||||
{
|
||||
if (int_rel.r_type != MIPS_R_REFHI)
|
||||
if (int_rel.r_type != MIPS_R_REFHI
|
||||
&& int_rel.r_type != MIPS_R_RELHI)
|
||||
r = _bfd_relocate_contents (howto, input_bfd, relocation,
|
||||
(contents
|
||||
+ adjust
|
||||
|
@ -1308,9 +1616,10 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
- input_section->vma));
|
||||
else
|
||||
{
|
||||
mips_relocate_refhi (&int_rel, &reflo_int_rel,
|
||||
input_bfd, input_section, contents,
|
||||
adjust, relocation);
|
||||
mips_relocate_hi (&int_rel, &lo_int_rel,
|
||||
input_bfd, input_section, contents,
|
||||
adjust, relocation,
|
||||
int_rel.r_type == MIPS_R_RELHI);
|
||||
r = bfd_reloc_ok;
|
||||
}
|
||||
}
|
||||
|
@ -1359,10 +1668,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
file. Make it look like a pcrel_offset relocation by
|
||||
adding in the start address. */
|
||||
if (howto->pc_relative)
|
||||
relocation += int_rel.r_vaddr + adjust;
|
||||
{
|
||||
if (int_rel.r_type != MIPS_R_RELHI)
|
||||
relocation += int_rel.r_vaddr + adjust;
|
||||
else
|
||||
relocation += lo_int_rel.r_vaddr + adjust;
|
||||
}
|
||||
}
|
||||
|
||||
if (int_rel.r_type != MIPS_R_REFHI)
|
||||
if (int_rel.r_type != MIPS_R_REFHI
|
||||
&& int_rel.r_type != MIPS_R_RELHI)
|
||||
r = _bfd_final_link_relocate (howto,
|
||||
input_bfd,
|
||||
input_section,
|
||||
|
@ -1374,9 +1689,10 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
|
|||
addend);
|
||||
else
|
||||
{
|
||||
mips_relocate_refhi (&int_rel, &reflo_int_rel, input_bfd,
|
||||
input_section, contents, adjust,
|
||||
relocation);
|
||||
mips_relocate_hi (&int_rel, &lo_int_rel, input_bfd,
|
||||
input_section, contents, adjust,
|
||||
relocation,
|
||||
int_rel.r_type == MIPS_R_RELHI);
|
||||
r = bfd_reloc_ok;
|
||||
}
|
||||
}
|
||||
|
@ -1652,46 +1968,25 @@ mips_relax_section (abfd, sec, info, again)
|
|||
|
||||
offsets[i] = 1;
|
||||
|
||||
/* Now look for all PC relative branches or switch table entries
|
||||
that cross this reloc and adjust their offsets. We will turn
|
||||
the single branch instruction into a four instruction
|
||||
sequence. In this loop we are only interested in local PC
|
||||
relative branches. */
|
||||
/* Now look for all PC relative references that cross this reloc
|
||||
and adjust their offsets. */
|
||||
adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs;
|
||||
for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++)
|
||||
{
|
||||
int r_type;
|
||||
struct internal_reloc adj_int_rel;
|
||||
|
||||
/* Quickly check that this reloc is internal PCREL16 or
|
||||
SWITCH. */
|
||||
if (abfd->xvec->header_byteorder_big_p)
|
||||
{
|
||||
if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0)
|
||||
continue;
|
||||
r_type = ((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
|
||||
>> RELOC_BITS3_TYPE_SH_BIG);
|
||||
if (r_type != MIPS_R_PCREL16
|
||||
&& r_type != MIPS_R_SWITCH)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0)
|
||||
continue;
|
||||
r_type = ((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
|
||||
>> RELOC_BITS3_TYPE_SH_LITTLE);
|
||||
if (r_type != MIPS_R_PCREL16
|
||||
&& r_type != MIPS_R_SWITCH)
|
||||
continue;
|
||||
}
|
||||
bfd_vma start, stop;
|
||||
int change;
|
||||
|
||||
mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
|
||||
|
||||
if (r_type == MIPS_R_PCREL16)
|
||||
if (adj_int_rel.r_type == MIPS_R_PCREL16)
|
||||
{
|
||||
unsigned long insn;
|
||||
bfd_vma dst;
|
||||
|
||||
/* We only care about local references. External ones
|
||||
will be relocated correctly anyhow. */
|
||||
if (adj_int_rel.r_extern)
|
||||
continue;
|
||||
|
||||
/* We are only interested in a PC relative reloc within
|
||||
this section. FIXME: Cross section PC relative
|
||||
|
@ -1700,29 +1995,76 @@ mips_relax_section (abfd, sec, info, again)
|
|||
if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
|
||||
continue;
|
||||
|
||||
/* Fetch the branch instruction. */
|
||||
start = adj_int_rel.r_vaddr;
|
||||
|
||||
insn = bfd_get_32 (abfd,
|
||||
contents + adj_int_rel.r_vaddr - sec->vma);
|
||||
|
||||
/* Work out the destination address. */
|
||||
dst = (insn & 0xffff) << 2;
|
||||
if ((dst & 0x20000) != 0)
|
||||
dst -= 0x40000;
|
||||
dst += adj_int_rel.r_vaddr + 4;
|
||||
|
||||
/* If this branch crosses the branch we just decided to
|
||||
expand, adjust the offset appropriately. */
|
||||
if (adj_int_rel.r_vaddr <= int_rel.r_vaddr
|
||||
&& dst > int_rel.r_vaddr)
|
||||
offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT;
|
||||
else if (adj_int_rel.r_vaddr > int_rel.r_vaddr
|
||||
&& dst <= int_rel.r_vaddr)
|
||||
offsets[adj_i] -= PCREL16_EXPANSION_ADJUSTMENT;
|
||||
stop = (insn & 0xffff) << 2;
|
||||
if ((stop & 0x20000) != 0)
|
||||
stop -= 0x40000;
|
||||
stop += adj_int_rel.r_vaddr + 4;
|
||||
}
|
||||
else
|
||||
else if (adj_int_rel.r_type == MIPS_R_RELHI)
|
||||
{
|
||||
bfd_vma start, stop;
|
||||
struct internal_reloc rello;
|
||||
long addhi, addlo;
|
||||
|
||||
/* The next reloc must be MIPS_R_RELLO, and we handle
|
||||
them together. */
|
||||
BFD_ASSERT (adj_ext_rel + 1 < ext_rel_end);
|
||||
|
||||
mips_ecoff_swap_reloc_in (abfd, (PTR) (adj_ext_rel + 1), &rello);
|
||||
|
||||
BFD_ASSERT (rello.r_type == MIPS_R_RELLO);
|
||||
|
||||
addhi = bfd_get_32 (abfd,
|
||||
contents + adj_int_rel.r_vaddr - sec->vma);
|
||||
addhi &= 0xffff;
|
||||
if (addhi & 0x8000)
|
||||
addhi -= 0x10000;
|
||||
addhi <<= 16;
|
||||
|
||||
addlo = bfd_get_32 (abfd, contents + rello.r_vaddr - sec->vma);
|
||||
addlo &= 0xffff;
|
||||
if (addlo & 0x8000)
|
||||
addlo -= 0x10000;
|
||||
|
||||
if (adj_int_rel.r_extern)
|
||||
{
|
||||
/* The value we want here is
|
||||
sym - RELLOaddr + addend
|
||||
which we can express as
|
||||
sym - (RELLOaddr - addend)
|
||||
Therefore if we are expanding the area between
|
||||
RELLOaddr and RELLOaddr - addend we must adjust
|
||||
the addend. This is admittedly ambiguous, since
|
||||
we might mean (sym + addend) - RELLOaddr, but in
|
||||
practice we don't, and there is no way to handle
|
||||
that case correctly since at this point we have
|
||||
no idea whether any reloc is being expanded
|
||||
between sym and sym + addend. */
|
||||
start = rello.r_vaddr - (addhi + addlo);
|
||||
stop = rello.r_vaddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* An internal RELHI/RELLO pair represents the
|
||||
difference between two addresses, $LC0 - foo.
|
||||
The symndx value is actually the difference
|
||||
between the reloc address and $LC0. This lets us
|
||||
compute $LC0, and, by considering the addend,
|
||||
foo. If the reloc we are expanding falls between
|
||||
those two relocs, we must adjust the addend. At
|
||||
this point, the symndx value is actually in the
|
||||
r_offset field, where it was put by
|
||||
mips_ecoff_swap_reloc_in. */
|
||||
start = rello.r_vaddr - adj_int_rel.r_offset;
|
||||
stop = start + addhi + addlo;
|
||||
}
|
||||
}
|
||||
else if (adj_int_rel.r_type == MIPS_R_SWITCH)
|
||||
{
|
||||
/* A MIPS_R_SWITCH reloc represents a word of the form
|
||||
.word $L3-$LS12
|
||||
The value in the object file is correct, assuming the
|
||||
|
@ -1736,20 +2078,34 @@ mips_relax_section (abfd, sec, info, again)
|
|||
this point, the symndx value is actually found in the
|
||||
r_offset field, since it was moved by
|
||||
mips_ecoff_swap_reloc_in. */
|
||||
|
||||
start = adj_int_rel.r_vaddr - adj_int_rel.r_offset;
|
||||
stop = start + bfd_get_32 (abfd,
|
||||
(contents
|
||||
+ adj_int_rel.r_vaddr
|
||||
- sec->vma));
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
/* The value we want in the object file is stop - start.
|
||||
If the expanded branch lies between start and stop,
|
||||
we must adjust the offset. */
|
||||
if (start <= int_rel.r_vaddr && stop > int_rel.r_vaddr)
|
||||
offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT;
|
||||
else if (start > int_rel.r_vaddr && stop <= int_rel.r_vaddr)
|
||||
offsets[adj_i] -= PCREL16_EXPANSION_ADJUSTMENT;
|
||||
/* If the range expressed by this reloc, which is the
|
||||
distance between START and STOP crosses the reloc we are
|
||||
expanding, we must adjust the offset. The sign of the
|
||||
adjustment depends upon the direction in which the range
|
||||
crosses the reloc being expanded. */
|
||||
if (start <= int_rel.r_vaddr && stop > int_rel.r_vaddr)
|
||||
change = PCREL16_EXPANSION_ADJUSTMENT;
|
||||
else if (start > int_rel.r_vaddr && stop <= int_rel.r_vaddr)
|
||||
change = - PCREL16_EXPANSION_ADJUSTMENT;
|
||||
else
|
||||
change = 0;
|
||||
|
||||
offsets[adj_i] += change;
|
||||
|
||||
if (adj_int_rel.r_type == MIPS_R_RELHI)
|
||||
{
|
||||
adj_ext_rel++;
|
||||
adj_i++;
|
||||
offsets[adj_i] += change;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue