and we're going to need it, get it now. */
switch (r_type)
{
+ case R_MIPS_GOT_PAGE:
+ /* If we didn't create a dynamic index for this symbol, it can
+ be regarded as local. */
+ if (local_p || ! h || h->root.dynindx < 0)
+ break;
+ /* Fall through. */
+
case R_MIPS_CALL16:
case R_MIPS_GOT16:
case R_MIPS_GOT_DISP:
/* Find the index into the GOT where this value is located. */
if (!local_p)
{
- BFD_ASSERT (addend == 0);
+ /* GOT_PAGE may take a non-zero addend, that is ignored in a
+ GOT_PAGE relocation that decays to GOT_DISP because the
+ symbol turns out to be global. The addend is then added
+ as GOT_OFST. */
+ BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
input_bfd,
(struct elf_link_hash_entry *) h);
We must initialize this entry in the GOT. */
bfd *tmpbfd = elf_hash_table (info)->dynobj;
asection *sgot = mips_elf_got_section (tmpbfd, FALSE);
- MIPS_ELF_PUT_WORD (tmpbfd, symbol + addend, sgot->contents + g);
+ MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g);
}
}
else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
/* Fall through. */
case R_MIPS_GOT_DISP:
+ got_disp:
value = g;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
break;
case R_MIPS_GOT_PAGE:
+ /* GOT_PAGE relocations that reference non-local symbols decay
+ to GOT_DISP. The corresponding GOT_OFST relocation decays to
+ 0. */
+ if (! (local_p || ! h || h->root.dynindx < 0))
+ goto got_disp;
value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
break;
case R_MIPS_GOT_OFST:
- mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value);
+ if (local_p || ! h || h->root.dynindx < 0)
+ mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value);
+ else
+ value = addend;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
}
break;
+ case R_MIPS_GOT_PAGE:
+ /* If this is a global, overridable symbol, GOT_PAGE will
+ decay to GOT_DISP, so we'll need a GOT entry for it. */
+ if (h == NULL)
+ break;
+ else
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+
+ while (hmips->root.root.type == bfd_link_hash_indirect
+ || hmips->root.root.type == bfd_link_hash_warning)
+ hmips = (struct mips_elf_link_hash_entry *)
+ hmips->root.root.u.i.link;
+
+ if ((hmips->root.root.type == bfd_link_hash_defined
+ || hmips->root.root.type == bfd_link_hash_defweak)
+ && hmips->root.root.u.def.section
+ && ! (info->shared && ! info->symbolic
+ && ! (hmips->root.elf_link_hash_flags
+ & ELF_LINK_FORCED_LOCAL))
+ /* If we've encountered any other relocation
+ referencing the symbol, we'll have marked it as
+ dynamic, and, even though we might be able to get
+ rid of the GOT entry should we know for sure all
+ previous relocations were GOT_PAGE ones, at this
+ point we can't tell, so just keep using the
+ symbol as dynamic. This is very important in the
+ multi-got case, since we don't decide whether to
+ decay GOT_PAGE to GOT_DISP on a per-GOT basis: if
+ the symbol is dynamic, we'll need a GOT entry for
+ every GOT in which the symbol is referenced with
+ a GOT_PAGE relocation. */
+ && hmips->root.dynindx == -1)
+ break;
+ }
+ /* Fall through. */
+
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16: