* 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) 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 * 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, asection *section,
bfd *output_bfd, bfd *output_bfd,
char **error)); 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, static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi,
struct internal_reloc *reflo, struct internal_reloc *reflo,
bfd *input_bfd, bfd *input_bfd,
@ -262,6 +269,26 @@ static reloc_howto_type mips_howto_table[] =
true, /* partial_inplace */ true, /* partial_inplace */
0xffff, /* src_mask */ 0xffff, /* src_mask */
0xffff, /* dst_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 */ true) /* pcrel_offset */
}; };
@ -353,6 +380,19 @@ mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern)
>> RELOC_BITS3_TYPE_SH_LITTLE); >> RELOC_BITS3_TYPE_SH_LITTLE);
intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0; 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. */ /* Swap a reloc out. */
@ -364,25 +404,37 @@ mips_ecoff_swap_reloc_out (abfd, intern, dst)
PTR dst; PTR dst;
{ {
RELOC *ext = (RELOC *) dst; RELOC *ext = (RELOC *) dst;
long r_symndx;
BFD_ASSERT (intern->r_extern BFD_ASSERT (intern->r_extern
|| (intern->r_symndx >= 0 && intern->r_symndx <= 12)); || (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); bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) ext->r_vaddr);
if (abfd->xvec->header_byteorder_big_p != false) if (abfd->xvec->header_byteorder_big_p != false)
{ {
ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG; ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG; ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_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) ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_BIG)
& RELOC_BITS3_TYPE_BIG) & RELOC_BITS3_TYPE_BIG)
| (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0)); | (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0));
} }
else else
{ {
ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE; ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE; ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_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) ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_LITTLE)
& RELOC_BITS3_TYPE_LITTLE) & RELOC_BITS3_TYPE_LITTLE)
| (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0)); | (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0));
@ -399,7 +451,7 @@ mips_adjust_reloc_in (abfd, intern, rptr)
const struct internal_reloc *intern; const struct internal_reloc *intern;
arelent *rptr; arelent *rptr;
{ {
if (intern->r_type > MIPS_R_PCREL16) if (intern->r_type > MIPS_R_SWITCH)
abort (); abort ();
if (! intern->r_extern if (! intern->r_extern
@ -412,6 +464,14 @@ mips_adjust_reloc_in (abfd, intern, rptr)
if (intern->r_type == MIPS_R_IGNORE) if (intern->r_type == MIPS_R_IGNORE)
rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr; 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]; rptr->howto = &mips_howto_table[intern->r_type];
} }
@ -424,6 +484,12 @@ mips_adjust_reloc_out (abfd, rel, intern)
const arelent *rel; const arelent *rel;
struct internal_reloc *intern; 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 /* ECOFF relocs are either against external symbols, or against
@ -724,6 +790,31 @@ mips_gprel_reloc (abfd,
return bfd_reloc_ok; 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. */ /* Get the howto structure for a generic reloc type. */
static CONST struct reloc_howto_struct * static CONST struct reloc_howto_struct *
@ -760,6 +851,9 @@ mips_bfd_reloc_type_lookup (abfd, code)
case BFD_RELOC_16_PCREL_S2: case BFD_RELOC_16_PCREL_S2:
mips_type = MIPS_R_PCREL16; mips_type = MIPS_R_PCREL16;
break; break;
case BFD_RELOC_GPREL32:
mips_type = MIPS_R_SWITCH;
break;
default: default:
return (CONST struct reloc_howto_struct *) NULL; 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]; 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) if (int_rel.r_extern)
{ {
h = sym_hashes[int_rel.r_symndx]; h = sym_hashes[int_rel.r_symndx];
@ -1532,45 +1652,57 @@ mips_relax_section (abfd, sec, info, again)
offsets[i] = 1; offsets[i] = 1;
/* Now look for all PC relative branches that cross this reloc /* Now look for all PC relative branches or switch table entries
and adjust their offsets. We will turn the single branch that cross this reloc and adjust their offsets. We will turn
instruction into a four instruction sequence. In this loop the single branch instruction into a four instruction
we are only interested in local PC relative branches. */ sequence. In this loop we are only interested in local PC
relative branches. */
adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs; 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++) for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++)
{ {
int r_type;
struct internal_reloc adj_int_rel; 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 (abfd->xvec->header_byteorder_big_p)
{ {
if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0 if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0)
|| (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG) continue;
>> RELOC_BITS3_TYPE_SH_BIG) r_type = ((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
!= MIPS_R_PCREL16)) >> RELOC_BITS3_TYPE_SH_BIG);
if (r_type != MIPS_R_PCREL16
&& r_type != MIPS_R_SWITCH)
continue; continue;
} }
else else
{ {
if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0 if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0)
|| (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE) continue;
>> RELOC_BITS3_TYPE_SH_LITTLE) r_type = ((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
!= MIPS_R_PCREL16)) >> RELOC_BITS3_TYPE_SH_LITTLE);
if (r_type != MIPS_R_PCREL16
&& r_type != MIPS_R_SWITCH)
continue; continue;
} }
mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel); mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
/* We are only interested in a PC relative reloc within this if (r_type == MIPS_R_PCREL16)
section. FIXME: Cross section PC relative relocs may not {
be handled correctly; does anybody care? */ unsigned long insn;
bfd_vma dst;
/* 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) if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
continue; continue;
/* Fetch the branch instruction. */ /* Fetch the branch instruction. */
insn = bfd_get_32 (abfd, contents + adj_int_rel.r_vaddr - sec->vma); insn = bfd_get_32 (abfd,
contents + adj_int_rel.r_vaddr - sec->vma);
/* Work out the destination address. */ /* Work out the destination address. */
dst = (insn & 0xffff) << 2; dst = (insn & 0xffff) << 2;
@ -1580,13 +1712,46 @@ mips_relax_section (abfd, sec, info, again)
/* If this branch crosses the branch we just decided to /* If this branch crosses the branch we just decided to
expand, adjust the offset appropriately. */ expand, adjust the offset appropriately. */
if (adj_int_rel.r_vaddr < int_rel.r_vaddr if (adj_int_rel.r_vaddr <= int_rel.r_vaddr
&& dst > int_rel.r_vaddr) && dst > int_rel.r_vaddr)
offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT; offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT;
else if (adj_int_rel.r_vaddr > int_rel.r_vaddr else if (adj_int_rel.r_vaddr > int_rel.r_vaddr
&& dst <= int_rel.r_vaddr) && dst <= int_rel.r_vaddr)
offsets[adj_i] -= PCREL16_EXPANSION_ADJUSTMENT; 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 /* Find all symbols in this section defined by this object file
and adjust their values. Note that we decide whether to and adjust their values. Note that we decide whether to