* coff-mips.c (mips_howto_table): Add entry for MIPS_R_SWITCH.

(mips_ecoff_swap_reloc_in): For MIPS_R_SWTICH, copy r_symndx into
	r_offset and set r_symndx to RELOC_SECTION_TEXT.
	(mips_ecoff_swap_reloc_out): For MIPS_R_SWITCH, get the r_symndx
	value from the r_offset field.
	(mips_adjust_reloc_in): Maximum r_type value is now MIPS_R_SWITCH.
	For MIPS_R_SWITCH, copy the r_offset field into the addend field.
	(mips_adjust_reloc_out): For MIPS_R_SWITCH, copy the addend field
	into the r_offset field.
	(mips_switch_reloc): New function.
	(mips_bfd_reloc_type_lookup): Translate BFD_RELOC_GPREL32 into
	MIPS_R_SWITCH.
	(mips_relocate_section): Handle MIPS_R_SWITCH.
	(mips_relax_section): Adjust MIPS_R_SWITCH offset if necessary.
This commit is contained in:
Ian Lance Taylor 1994-04-07 18:29:38 +00:00
parent 14bf9e4b42
commit dabf906e9b
2 changed files with 224 additions and 42 deletions

View File

@ -1,3 +1,20 @@
Thu Apr 7 14:23:05 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
* coff-mips.c (mips_howto_table): Add entry for MIPS_R_SWITCH.
(mips_ecoff_swap_reloc_in): For MIPS_R_SWTICH, copy r_symndx into
r_offset and set r_symndx to RELOC_SECTION_TEXT.
(mips_ecoff_swap_reloc_out): For MIPS_R_SWITCH, get the r_symndx
value from the r_offset field.
(mips_adjust_reloc_in): Maximum r_type value is now MIPS_R_SWITCH.
For MIPS_R_SWITCH, copy the r_offset field into the addend field.
(mips_adjust_reloc_out): For MIPS_R_SWITCH, copy the addend field
into the r_offset field.
(mips_switch_reloc): New function.
(mips_bfd_reloc_type_lookup): Translate BFD_RELOC_GPREL32 into
MIPS_R_SWITCH.
(mips_relocate_section): Handle MIPS_R_SWITCH.
(mips_relax_section): Adjust MIPS_R_SWITCH offset if necessary.
Thu Apr 7 11:10:51 1994 Jeffrey A. Law (law@snake.cs.utah.edu)
* elfcode.h (elf_set_section_contents): Support calling the backend

View File

@ -72,6 +72,13 @@ static bfd_reloc_status_type mips_gprel_reloc PARAMS ((bfd *abfd,
asection *section,
bfd *output_bfd,
char **error));
static bfd_reloc_status_type mips_switch_reloc PARAMS ((bfd *abfd,
arelent *reloc,
asymbol *symbol,
PTR data,
asection *section,
bfd *output_bfd,
char **error));
static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi,
struct internal_reloc *reflo,
bfd *input_bfd,
@ -262,6 +269,26 @@ static reloc_howto_type mips_howto_table[] =
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 an entry in
a switch table, which is the difference between two symbols in
the .text section. The symndx is actually the offset from the
reloc address to the subtrahend. See include/coff/mips.h for
more details. */
HOWTO (MIPS_R_SWITCH, /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
32, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
mips_switch_reloc, /* special_function */
"SWITCH", /* name */
true, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
true) /* pcrel_offset */
};
@ -353,6 +380,19 @@ mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern)
>> RELOC_BITS3_TYPE_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
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)
{
BFD_ASSERT (! intern->r_extern);
intern->r_offset = intern->r_symndx;
intern->r_symndx = RELOC_SECTION_TEXT;
}
}
/* Swap a reloc out. */
@ -364,25 +404,37 @@ mips_ecoff_swap_reloc_out (abfd, intern, dst)
PTR dst;
{
RELOC *ext = (RELOC *) dst;
long r_symndx;
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)
r_symndx = intern->r_symndx;
else
{
BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT);
r_symndx = intern->r_offset;
}
bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) ext->r_vaddr);
if (abfd->xvec->header_byteorder_big_p != false)
{
ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG;
ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG;
ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_BIG)
& RELOC_BITS3_TYPE_BIG)
| (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0));
}
else
{
ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
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_extern ? RELOC_BITS3_EXTERN_LITTLE : 0));
@ -399,7 +451,7 @@ mips_adjust_reloc_in (abfd, intern, rptr)
const struct internal_reloc *intern;
arelent *rptr;
{
if (intern->r_type > MIPS_R_PCREL16)
if (intern->r_type > MIPS_R_SWITCH)
abort ();
if (! intern->r_extern
@ -412,6 +464,14 @@ 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)
rptr->addend = intern->r_offset;
rptr->howto = &mips_howto_table[intern->r_type];
}
@ -424,6 +484,12 @@ 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
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)
intern->r_offset = rel->addend;
}
/* ECOFF relocs are either against external symbols, or against
@ -724,6 +790,31 @@ mips_gprel_reloc (abfd,
return bfd_reloc_ok;
}
/* 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
bfd_perform_relocation to tamper with it at all. */
/*ARGSUSED*/
static bfd_reloc_status_type
mips_switch_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;
{
return bfd_reloc_ok;
}
/* Get the howto structure for a generic reloc type. */
static CONST struct reloc_howto_struct *
@ -760,6 +851,9 @@ mips_bfd_reloc_type_lookup (abfd, code)
case BFD_RELOC_16_PCREL_S2:
mips_type = MIPS_R_PCREL16;
break;
case BFD_RELOC_GPREL32:
mips_type = MIPS_R_SWITCH;
break;
default:
return (CONST struct reloc_howto_struct *) NULL;
}
@ -938,6 +1032,32 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
howto = &mips_howto_table[int_rel.r_type];
/* The SWITCH reloc must be handled specially. This reloc is
marks the location of a difference between two portions of an
object file. The symbol index does not reference a symbol,
but is actually the offset from the reloc to the subtrahend
of the difference. This reloc is correct in the object file,
and needs no further adjustment, unless we are relaxing. If
we are relaxing, we may have to add in an offset. Since no
symbols are involved in this reloc, we handle it completely
here. */
if (int_rel.r_type == MIPS_R_SWITCH)
{
if (offsets != NULL
&& offsets[i] != 0)
{
r = _bfd_relocate_contents (howto, input_bfd,
(bfd_vma) offsets[i],
(contents
+ adjust
+ int_rel.r_vaddr
- input_section->vma));
BFD_ASSERT (r == bfd_reloc_ok);
}
continue;
}
if (int_rel.r_extern)
{
h = sym_hashes[int_rel.r_symndx];
@ -1532,60 +1652,105 @@ mips_relax_section (abfd, sec, info, again)
offsets[i] = 1;
/* Now look for all PC relative branches 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 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. */
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;
unsigned long insn;
bfd_vma dst;
/* Quickly check that this reloc is internal PCREL16. */
/* 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
|| (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
>> RELOC_BITS3_TYPE_SH_BIG)
!= MIPS_R_PCREL16))
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
|| (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
>> RELOC_BITS3_TYPE_SH_LITTLE)
!= MIPS_R_PCREL16))
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;
}
mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
/* We are only interested in a PC relative reloc within this
section. FIXME: Cross section PC relative relocs may not
be handled correctly; does anybody care? */
if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
continue;
if (r_type == MIPS_R_PCREL16)
{
unsigned long insn;
bfd_vma dst;
/* Fetch the branch instruction. */
insn = bfd_get_32 (abfd, contents + adj_int_rel.r_vaddr - sec->vma);
/* We are only interested in a PC relative reloc within
this section. FIXME: Cross section PC relative
relocs may not be handled correctly; does anybody
care? */
if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
continue;
/* Work out the destination address. */
dst = (insn & 0xffff) << 2;
if ((dst & 0x20000) != 0)
dst -= 0x40000;
dst += adj_int_rel.r_vaddr + 4;
/* Fetch the branch instruction. */
insn = bfd_get_32 (abfd,
contents + adj_int_rel.r_vaddr - sec->vma);
/* 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;
/* 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;
}
else
{
bfd_vma start, stop;
/* A MIPS_R_SWITCH reloc represents a word of the form
.word $L3-$LS12
The value in the object file is correct, assuming the
original value of $L3. The symndx value is actually
the difference between the reloc address and $LS12.
This lets us compute the original value of $LS12 as
vaddr - symndx
and the original value of $L3 as
vaddr - symndx + addend
where addend is the value from the object file. At
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));
/* 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;
}
}
/* Find all symbols in this section defined by this object file