Changes to support linker relaxing of embedded MIPS PIC code to

use a five instruction sequence for funtion calls which are out of
	range of the bal instruction.
	* libecoff.h (struct ecoff_section_tdata): Define.
	(ecoff_section_data): Define.
	(ecoff_bfd_relax_section): Don't define.
	* ecoff.c (ecoff_final_link_debug_accumulate): Don't read or free
	the debugging information if it has already been read.
	(ecoff_indirect_link_order): Handle _cooked_size being different
	from _raw_size.  Don't reread the contents or the relocs if they
	have already been read in.
	* coff-mips.c (mips_howto_table): Change bitsize of PCREL16 from
	18 to 16.
	(PCREL16_EXPANSION_ADJUSTMENT): Define.
	(mips_relocate_refhi): Take adjust argument.
	(mips_relocate_section): Handle reloc offsets stored in section
	used_by_bfd field.  Call mips_relax_pcrel16 to handle details of
	expanding an out of range PCREL16.  Keep trace of adjustments
	required by expansions.  Set s and unset h when converting a reloc
	from undefined to section.  Change handling of PC relative relocs:
	if against a section, they are correct in the object file, if
	against an external symbol they are pcrel_offset.
	(mips_relax_section): New function.
	(mips_relax_pcrel16): New function.
	(ecoff_bfd_relax_section): Define.
	* coff-alpha.c (ecoff_bfd_relax_section): Define.
	* ecofflink.c (bfd_ecoff_debug_accumulate): Handle adjustments
	built by mips_relax_section when writing out addresses.
	* elf32-mips.c (mips_elf_read_ecoff_info): Clear adjust field.
This commit is contained in:
Ian Lance Taylor 1994-03-25 22:37:55 +00:00
parent f078dc7cf2
commit a3a33af390
7 changed files with 1247 additions and 220 deletions

View file

@ -77,10 +77,18 @@ static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
size_t adjust,
bfd_vma relocation));
static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *,
bfd *, asection *,
bfd_byte *, PTR));
static boolean mips_relax_section PARAMS ((bfd *, asection *,
struct bfd_link_info *,
boolean *));
static boolean mips_relax_pcrel16 PARAMS ((struct bfd_link_info *, bfd *,
asection *,
struct ecoff_link_hash_entry *,
bfd_byte *, bfd_vma));
/* ECOFF has COFF sections, but the debugging information is stored in
a completely different format. ECOFF targets use some of the
@ -236,11 +244,45 @@ static reloc_howto_type mips_howto_table[] =
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
false) /* pcrel_offset */
false), /* pcrel_offset */
/* 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
instructions. */
HOWTO (MIPS_R_PCREL16, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
16, /* bitsize */
true, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
mips_generic_reloc, /* special_function */
"PCREL16", /* name */
true, /* partial_inplace */
0xffff, /* src_mask */
0xffff, /* dst_mask */
true) /* pcrel_offset */
};
#define MIPS_HOWTO_COUNT \
(sizeof mips_howto_table / sizeof mips_howto_table[0])
/* When the linker is doing relaxing, it may change a external PCREL16
reloc. This typically represents an instruction like
bal foo
We change it to
.set noreorder
bal $L1
lui $at,%hi(foo - $L1)
$L1:
addiu $at,%lo(foo - $L1)
addu $at,$at,$31
jalr $at
PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the
instruction by. */
#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4)
/* See whether the magic number matches. */
@ -357,7 +399,7 @@ mips_adjust_reloc_in (abfd, intern, rptr)
const struct internal_reloc *intern;
arelent *rptr;
{
if (intern->r_type > MIPS_R_LITERAL)
if (intern->r_type > MIPS_R_PCREL16)
abort ();
if (! intern->r_extern
@ -715,6 +757,9 @@ mips_bfd_reloc_type_lookup (abfd, code)
case BFD_RELOC_MIPS_LITERAL:
mips_type = MIPS_R_LITERAL;
break;
case BFD_RELOC_16_PCREL_S2:
mips_type = MIPS_R_PCREL16;
break;
default:
return (CONST struct reloc_howto_struct *) NULL;
}
@ -729,12 +774,13 @@ mips_bfd_reloc_type_lookup (abfd, code)
static void
mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
relocation)
adjust, relocation)
struct internal_reloc *refhi;
struct internal_reloc *reflo;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
size_t adjust;
bfd_vma relocation;
{
unsigned long insn;
@ -742,9 +788,9 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
unsigned long vallo;
insn = bfd_get_32 (input_bfd,
contents + refhi->r_vaddr - input_section->vma);
contents + adjust + refhi->r_vaddr - input_section->vma);
vallo = (bfd_get_32 (input_bfd,
contents + reflo->r_vaddr - input_section->vma)
contents + adjust + reflo->r_vaddr - input_section->vma)
& 0xffff);
val = ((insn & 0xffff) << 16) + vallo;
val += relocation;
@ -761,7 +807,7 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
bfd_put_32 (input_bfd, (bfd_vma) insn,
contents + refhi->r_vaddr - input_section->vma);
contents + adjust + refhi->r_vaddr - input_section->vma);
}
/* Relocate a section while linking a MIPS ECOFF file. */
@ -780,9 +826,13 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
struct ecoff_link_hash_entry **sym_hashes;
bfd_vma gp;
boolean gp_undefined;
size_t adjust;
long *offsets;
struct external_reloc *ext_rel;
struct external_reloc *ext_rel_end;
unsigned int i;
boolean got_reflo;
struct internal_reloc reflo_int_rel;
BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p
== output_bfd->xvec->header_byteorder_big_p);
@ -842,12 +892,18 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
got_reflo = false;
adjust = 0;
if (ecoff_section_data (input_bfd, input_section) == NULL)
offsets = NULL;
else
offsets = ecoff_section_data (input_bfd, input_section)->offsets;
ext_rel = (struct external_reloc *) external_relocs;
ext_rel_end = ext_rel + input_section->reloc_count;
for (; ext_rel < ext_rel_end; ext_rel++)
for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
{
struct internal_reloc int_rel;
struct internal_reloc reflo_int_rel;
bfd_vma addend;
reloc_howto_type *howto;
struct ecoff_link_hash_entry *h = NULL;
@ -955,6 +1011,54 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
}
}
/* If we are relaxing, mips_relax_section may have set
offsets[i] to some value. A value of 1 means we must expand
a PC relative branch into a multi-instruction of sequence,
and any other value is an addend. */
if (offsets != NULL
&& offsets[i] != 0)
{
BFD_ASSERT (! info->relocateable);
BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16);
if (offsets[i] != 1)
{
BFD_ASSERT (! int_rel.r_extern);
addend += offsets[i];
}
else
{
bfd_byte *here;
BFD_ASSERT (int_rel.r_extern);
/* Move the rest of the instructions up. */
here = (contents
+ adjust
+ int_rel.r_vaddr
- input_section->vma);
memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here,
(input_section->_raw_size
- (int_rel.r_vaddr - input_section->vma)));
/* Generate the new instructions. */
if (! mips_relax_pcrel16 (info, input_bfd, input_section,
h, here,
(input_section->output_section->vma
+ input_section->output_offset
+ (int_rel.r_vaddr
- input_section->vma)
+ adjust)))
return false;
/* We must adjust everything else up a notch. */
adjust += PCREL16_EXPANSION_ADJUSTMENT;
/* mips_relax_pcrel16 handles all the details of this
relocation. */
continue;
}
}
if (info->relocateable)
{
/* We are generating relocateable output, and must convert
@ -963,7 +1067,6 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
{
if (h->root.type == bfd_link_hash_defined)
{
asection *hsec;
const char *name;
/* This symbol is defined in the output. Convert
@ -974,9 +1077,9 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
int_rel.r_extern = 0;
/* Compute a new r_symndx value. */
hsec = h->root.u.def.section;
s = h->root.u.def.section;
name = bfd_get_section_name (output_bfd,
hsec->output_section);
s->output_section);
int_rel.r_symndx = -1;
switch (name[1])
@ -1024,8 +1127,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
/* Add the section VMA and the symbol value. */
relocation = (h->root.u.def.value
+ hsec->output_section->vma
+ hsec->output_offset);
+ s->output_section->vma
+ s->output_offset);
/* For a PC relative relocation, the object file
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;
h = NULL;
}
else
{
@ -1056,6 +1167,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
relocation += addend;
/* 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)
relocation -= (input_section->output_section->vma
+ input_section->output_offset
- input_section->vma);
/* Adjust the contents. */
if (relocation == 0)
r = bfd_reloc_ok;
@ -1064,13 +1183,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
if (int_rel.r_type != MIPS_R_REFHI)
r = _bfd_relocate_contents (howto, input_bfd, relocation,
(contents
+ adjust
+ int_rel.r_vaddr
- input_section->vma));
else
{
mips_relocate_refhi (&int_rel, &reflo_int_rel,
input_bfd, input_section, contents,
relocation);
adjust, relocation);
r = bfd_reloc_ok;
}
}
@ -1115,10 +1235,11 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
+ s->output_offset
- s->vma);
/* Adjust a PC relative relocation by removing the
reference to the original source section. */
/* A PC relative reloc is already correct in the object
file. Make it look like a pcrel_offset relocation by
adding in the start address. */
if (howto->pc_relative)
relocation += input_section->vma;
relocation += int_rel.r_vaddr + adjust;
}
if (int_rel.r_type != MIPS_R_REFHI)
@ -1126,13 +1247,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
input_bfd,
input_section,
contents,
int_rel.r_vaddr - input_section->vma,
(int_rel.r_vaddr
- input_section->vma
+ adjust),
relocation,
addend);
else
{
mips_relocate_refhi (&int_rel, &reflo_int_rel, input_bfd,
input_section, contents, relocation);
input_section, contents, adjust,
relocation);
r = bfd_reloc_ok;
}
}
@ -1166,6 +1290,399 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
return true;
}
/* Relax a section when linking a MIPS ECOFF file. This is used for
embedded PIC code, which always uses PC relative branches which
only have an 18 bit range on MIPS. If a branch is not in range, we
generate a long instruction sequence to compensate. Each time we
find a branch to expand, we have to check all the others again to
make sure they are still in range. This is slow, but it only has
to be done when -relax is passed to the linker.
This routine figures out which branches need to expand; the actual
expansion is done in mips_relocate_section when the section
contents are relocated. The information is stored in the offsets
field of the ecoff_section_tdata structure. An offset of 1 means
that the branch must be expanded into a multi-instruction PC
relative branch (such an offset will only occur for a PC relative
branch to an external symbol). Any other offset must be a multiple
of four, and is the amount to change the branch by (such an offset
will only occur for a PC relative branch within the same section).
We do not modify the section relocs or contents themselves so that
if memory usage becomes an issue we can discard them and read them
again. The only information we must save in memory between this
routine and the mips_relocate_section routine is the table of
offsets. */
static boolean
mips_relax_section (abfd, sec, info, again)
bfd *abfd;
asection *sec;
struct bfd_link_info *info;
boolean *again;
{
struct ecoff_section_tdata *section_tdata;
bfd_byte *contents = NULL;
long *offsets;
struct external_reloc *ext_rel;
struct external_reloc *ext_rel_end;
unsigned int i;
/* Assume we are not going to need another pass. */
*again = false;
/* If we are not generating an ECOFF file, this is much too
confusing to deal with. */
if (info->hash->creator->flavour != bfd_get_flavour (abfd))
return true;
/* If there are no relocs, there is nothing to do. */
if (sec->reloc_count == 0)
return true;
/* We are only interested in PC relative relocs, and why would there
ever be one from anything but the .text section? */
if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0)
return true;
/* Read in the relocs, if we haven't already got them. */
section_tdata = ecoff_section_data (abfd, sec);
if (section_tdata == (struct ecoff_section_tdata *) NULL)
{
bfd_size_type external_reloc_size;
bfd_size_type external_relocs_size;
sec->used_by_bfd =
(PTR) bfd_alloc_by_size_t (abfd, sizeof (struct ecoff_section_tdata));
if (sec->used_by_bfd == NULL)
{
bfd_set_error (bfd_error_no_memory);
goto error_return;
}
section_tdata = ecoff_section_data (abfd, sec);
section_tdata->contents = NULL;
section_tdata->offsets = NULL;
external_reloc_size = ecoff_backend (abfd)->external_reloc_size;
external_relocs_size = external_reloc_size * sec->reloc_count;
section_tdata->external_relocs =
(PTR) bfd_alloc (abfd, external_relocs_size);
if (section_tdata->external_relocs == NULL && external_relocs_size != 0)
{
bfd_set_error (bfd_error_no_memory);
goto error_return;
}
if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
|| (bfd_read (section_tdata->external_relocs, 1,
external_relocs_size, abfd)
!= external_relocs_size))
goto error_return;
/* We must initialize _cooked_size only the first time we are
called. */
sec->_cooked_size = sec->_raw_size;
}
contents = section_tdata->contents;
offsets = section_tdata->offsets;
/* Look for any external PC relative relocs. Internal PC relative
relocs are already correct in the object file, so they certainly
can not overflow. */
ext_rel = (struct external_reloc *) section_tdata->external_relocs;
ext_rel_end = ext_rel + sec->reloc_count;
for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
{
struct internal_reloc int_rel;
struct ecoff_link_hash_entry *h;
asection *hsec;
bfd_signed_vma relocation;
struct external_reloc *adj_ext_rel;
unsigned int adj_i;
unsigned long ext_count;
struct ecoff_link_hash_entry **adj_h_ptr;
struct ecoff_link_hash_entry **adj_h_ptr_end;
struct ecoff_value_adjust *adjust;
/* If we have already expanded this reloc, we certainly don't
need to do it again. */
if (offsets != (long *) NULL && offsets[i] == 1)
continue;
/* Quickly check that this reloc is external PCREL16. */
if (abfd->xvec->header_byteorder_big_p)
{
if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0
|| (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
>> RELOC_BITS3_TYPE_SH_BIG)
!= MIPS_R_PCREL16))
continue;
}
else
{
if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0
|| (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
>> RELOC_BITS3_TYPE_SH_LITTLE)
!= MIPS_R_PCREL16))
continue;
}
mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx];
if (h == (struct ecoff_link_hash_entry *) NULL)
abort ();
if (h->root.type != bfd_link_hash_defined)
{
/* Just ignore undefined symbols. These will presumably
generate an error later in the link. */
continue;
}
/* Get the value of the symbol. */
hsec = h->root.u.def.section;
relocation = (h->root.u.def.value
+ hsec->output_section->vma
+ hsec->output_offset);
/* Subtract out the current address. */
relocation -= (sec->output_section->vma
+ sec->output_offset
+ (int_rel.r_vaddr - sec->vma));
/* The addend is stored in the object file. In the normal case
of ``bal symbol'', the addend will be -4. It will only be
different in the case of ``bal symbol+constant''. To avoid
always reading in the section contents, we don't check the
addend in the object file (we could easily check the contents
if we happen to have already read them in, but I fear that
this could be confusing). This means we will screw up if
there is a branch to a symbol that is in range, but added to
a constant which puts it out of range; in such a case the
link will fail with a reloc overflow error. Since the
compiler will never generate such code, it should be easy
enough to work around it by changing the assembly code in the
source file. */
relocation -= 4;
/* Now RELOCATION is the number we want to put in the object
file. See whether it fits. */
if (relocation >= -0x20000 && relocation < 0x20000)
continue;
/* Now that we know this reloc needs work, which will rarely
happen, go ahead and grab the section contents. */
if (contents == (bfd_byte *) NULL)
{
if (info->keep_memory)
contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size);
else
contents = (bfd_byte *) malloc (sec->_raw_size);
if (contents == (bfd_byte *) NULL)
{
bfd_set_error (bfd_error_no_memory);
goto error_return;
}
if (! bfd_get_section_contents (abfd, sec, (PTR) contents,
(file_ptr) 0, sec->_raw_size))
goto error_return;
if (info->keep_memory)
section_tdata->contents = contents;
}
/* We only support changing the bal instruction. It would be
possible to handle other PC relative branches, but some of
them (the conditional branches) would require a different
length instruction sequence which would complicate both this
routine and mips_relax_pcrel16. It could be written if
somebody felt it were important. Ignoring this reloc will
presumably cause a reloc overflow error later on. */
if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma)
!= 0x0411ffff) /* bgezal $0,. == bal . */
continue;
/* Bother. We need to expand this reloc, and we will need to
make another relaxation pass since this change may put other
relocs out of range. We need to examine the local branches
and we need to allocate memory to hold the offsets we must
add to them. We also need to adjust the values of all
symbols in the object file following this location. */
sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT;
*again = true;
if (offsets == (long *) NULL)
{
size_t size;
size = sec->reloc_count * sizeof (long);
offsets = (long *) bfd_alloc_by_size_t (abfd, size);
if (offsets == (long *) NULL)
{
bfd_set_error (bfd_error_no_memory);
goto error_return;
}
memset (offsets, 0, size);
section_tdata->offsets = offsets;
}
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. */
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++)
{
struct internal_reloc adj_int_rel;
unsigned long insn;
bfd_vma dst;
/* Quickly check that this reloc is internal PCREL16. */
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))
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))
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;
/* Fetch the branch instruction. */
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;
}
/* Find all symbols in this section defined by this object file
and adjust their values. Note that we decide whether to
adjust the value based on the value stored in the ECOFF EXTR
structure, because the value stored in the hash table may
have been changed by an earlier expanded reloc and thus may
no longer correctly indicate whether the symbol is before or
after the expanded reloc. */
ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
adj_h_ptr = ecoff_data (abfd)->sym_hashes;
adj_h_ptr_end = adj_h_ptr + ext_count;
for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++)
{
struct ecoff_link_hash_entry *adj_h;
adj_h = *adj_h_ptr;
if (adj_h != (struct ecoff_link_hash_entry *) NULL
&& adj_h->root.type == bfd_link_hash_defined
&& adj_h->root.u.def.section == sec
&& adj_h->esym.asym.value > int_rel.r_vaddr)
adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT;
}
/* Add an entry to the symbol value adjust list. This is used
by bfd_ecoff_debug_accumulate to adjust the values of
internal symbols and FDR's. */
adjust = ((struct ecoff_value_adjust *)
bfd_alloc (abfd, sizeof (struct ecoff_value_adjust)));
if (adjust == (struct ecoff_value_adjust *) NULL)
{
bfd_set_error (bfd_error_no_memory);
goto error_return;
}
adjust->start = int_rel.r_vaddr;
adjust->end = sec->vma + sec->_raw_size;
adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT;
adjust->next = ecoff_data (abfd)->debug_info.adjust;
ecoff_data (abfd)->debug_info.adjust = adjust;
}
if (contents != (bfd_byte *) NULL && ! info->keep_memory)
free (contents);
return true;
error_return:
if (contents != (bfd_byte *) NULL && ! info->keep_memory)
free (contents);
return false;
}
/* This routine is called from mips_relocate_section when a PC
relative reloc must be expanded into the five instruction sequence.
It handles all the details of the expansion, including resolving
the reloc. */
static boolean
mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address)
struct bfd_link_info *info;
bfd *input_bfd;
asection *input_section;
struct ecoff_link_hash_entry *h;
bfd_byte *location;
bfd_vma address;
{
bfd_vma relocation;
/* 0x0411ffff is bgezal $0,. == bal . */
BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff);
/* We need to compute the distance between the symbol and the
current address plus eight. */
relocation = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
relocation -= address + 8;
/* If the lower half is negative, increment the upper 16 half. */
if ((relocation & 0x8000) != 0)
relocation += 0x10000;
bfd_put_32 (input_bfd, 0x04110001, location); /* bal .+8 */
bfd_put_32 (input_bfd,
0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */
location + 4);
bfd_put_32 (input_bfd,
0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */
location + 8);
bfd_put_32 (input_bfd, 0x003f0821, location + 12); /* addu $at,$at,$ra */
bfd_put_32 (input_bfd, 0x0020f809, location + 16); /* jalr $at */
return true;
}
/* This is the ECOFF backend structure. The backend field of the
target vector points to this. */
@ -1256,6 +1773,9 @@ static const struct ecoff_backend_data mips_ecoff_backend_data =
#define ecoff_bfd_get_relocated_section_contents \
bfd_generic_get_relocated_section_contents
/* Relaxing sections is MIPS specific. */
#define ecoff_bfd_relax_section mips_relax_section
/* Core file support is usually traditional (but note that Irix uses
irix-core.c). */
#define ecoff_core_file_p _bfd_dummy_target