/* The global symbol in the GOT with the lowest index in the dynamic
symbol table. */
struct elf_link_hash_entry *global_gotsym;
+ /* The number of global .got entries. */
+ unsigned int global_gotno;
/* The number of local .got entries. */
unsigned int local_gotno;
/* The number of local .got entries we have used. */
static boolean mips_elf_sort_hash_table_f
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
static boolean mips_elf_sort_hash_table
- PARAMS ((struct bfd_link_info *));
+ PARAMS ((struct bfd_link_info *, unsigned long));
static asection * mips_elf_got_section PARAMS ((bfd *));
static struct mips_got_info *mips_elf_got_info
PARAMS ((bfd *, asection **));
0x3ffffff, /* dst_mask */
false); /* pcrel_offset */
-/* The reloc used for the mips16 gprel instruction. The src_mask and
- dsk_mask for this howto do not reflect the actual instruction, in
- which the value is not contiguous; the masks are for the
- convenience of the relocate_section routine. */
+/* The reloc used for the mips16 gprel instruction. */
static reloc_howto_type elf_mips16_gprel_howto =
HOWTO (R_MIPS16_GPREL, /* type */
0, /* rightshift */
mips16_gprel_reloc, /* special_function */
"R_MIPS16_GPREL", /* name */
true, /* partial_inplace */
- 0xffff, /* src_mask */
- 0xffff, /* dst_mask */
+ 0x07ff001f, /* src_mask */
+ 0x07ff001f, /* dst_mask */
false); /* pcrel_offset */
elf_elfheader (abfd)->e_flags |= EF_MIPS_CPIC;
}
+ /* We'd carefully arranged the dynamic symbol indices, and then the
+ generic size_dynamic_sections renumbered them out from under us.
+ Rather than trying somehow to prevent the renumbering, just do
+ the sort again. */
+
+ if (elf_hash_table (info)->dynobj)
+ {
+ bfd *dynobj;
+ asection *got;
+ struct mips_got_info *g;
+
+ if (!mips_elf_sort_hash_table (info, bfd_count_sections (abfd) + 1))
+ return false;
+
+ /* Make sure we didn't grow the global .got region. */
+ dynobj = elf_hash_table (info)->dynobj;
+ got = bfd_get_section_by_name (dynobj, ".got");
+ g = (struct mips_got_info *) elf_section_data (got)->tdata;
+
+ BFD_ASSERT ((elf_hash_table (info)->dynsymcount
+ - g->global_gotsym->dynindx)
+ <= g->global_gotno);
+ }
+
/* On IRIX5, we omit the .options section. On IRIX6, however, we
include it, even though we don't process it quite right. (Some
entries are supposed to be merged.) Empirically, we seem to be
/* Sort the dynamic symbol table so that symbols that need GOT entries
appear towards the end. This reduces the amount of GOT space
- required. */
+ required. MAX_LOCAL is used to set the number of local symbols
+ known to be in the dynamic symbol table. During
+ mips_elf_size_dynamic_sections, this value is 1. Afterward, the
+ section symbols are added and the count is higher. */
static boolean
-mips_elf_sort_hash_table (info)
+mips_elf_sort_hash_table (info, max_local)
struct bfd_link_info *info;
+ unsigned long max_local;
{
struct mips_elf_hash_sort_data hsd;
struct mips_got_info *g;
hsd.low = NULL;
hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount;
- hsd.max_non_got_dynindx = 1;
+ hsd.max_non_got_dynindx = max_local;
mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
elf_hash_table (info)),
mips_elf_sort_hash_table_f,
case R_MIPS16_26:
/* The calculation for R_MIPS_26 is just the same as for an
R_MIPS_26. It's only the storage of the relocated field into
- the output file that's different. That's handle in
+ the output file that's different. That's handled in
mips_elf_perform_relocation. So, we just fall through to the
R_MIPS_26 case here. */
case R_MIPS_26:
/* Fall through. */
+ case R_MIPS16_GPREL:
+ /* The R_MIPS16_GPREL performs the same calculation as
+ R_MIPS_GPREL16, but stores the relocated bits in a different
+ order. We don't need to do anything special here; the
+ differences are handled in mips_elf_perform_relocation. */
case R_MIPS_GPREL16:
if (local_p)
value = mips_elf_sign_extend (addend, 16) + symbol + gp0 - gp;
/* We don't do anything with these at present. */
return bfd_reloc_continue;
- case R_MIPS16_GPREL:
- /* These relocations, used for MIPS16, are not clearly
- documented anywhere. What do they do? */
- return bfd_reloc_notsupported;
-
default:
/* An unrecognized relocation type. */
return bfd_reloc_notsupported;
bfd_vma x;
bfd_byte *location = contents + relocation->r_offset;
- switch (bfd_get_reloc_size (howto))
- {
- case 0:
- x = 0;
- break;
-
- case 1:
- x = bfd_get_8 (input_bfd, location);
- break;
-
- case 2:
- x = bfd_get_16 (input_bfd, location);
- break;
-
- case 4:
- x = bfd_get_32 (input_bfd, location);
- break;
+ /* Obtain the bytes. */
+ x = bfd_get (8 * bfd_get_reloc_size (howto), input_bfd, location);
- case 8:
-#ifdef BFD64
- x = bfd_get_64 (input_bfd, location);
-#else
- abort ();
-#endif
- break;
-
- default:
- abort ();
- break;
- }
+ if ((ELF32_R_TYPE (relocation->r_info) == R_MIPS16_26
+ || ELF32_R_TYPE (relocation->r_info) == R_MIPS16_GPREL)
+ && bfd_little_endian (input_bfd))
+ /* The two 16-bit words will be reversed on a little-endian
+ system. See mips_elf_perform_relocation for more details. */
+ x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
return x;
}
| ((value & 0x3e00000) >> 5)
| (value & 0xffff));
- /* Perform the relocation. */
- x |= (value & howto->dst_mask);
+ }
+ else if (ELF32_R_TYPE (relocation->r_info) == R_MIPS16_GPREL)
+ {
+ /* R_MIPS16_GPREL is used for GP-relative addressing in mips16
+ mode. A typical instruction will have a format like this:
- /* Swap the high- and low-order 16 bits on little-endian
- systems. */
- if (bfd_little_endian (input_bfd))
- x = (((x & 0xffff) << 16)
- | (((x & 0xffff0000) >> 16) & 0xffff));
+ +--------------+--------------------------------+
+ ! EXTEND ! Imm 10:5 ! Imm 15:11 !
+ +--------------+--------------------------------+
+ ! Major ! rx ! ry ! Imm 4:0 !
+ +--------------+--------------------------------+
+
+ EXTEND is the five bit value 11110. Major is the instruction
+ opcode.
+
+ This is handled exactly like R_MIPS_GPREL16, except that the
+ addend is retrieved and stored as shown in this diagram; that
+ is, the Imm fields above replace the V-rel16 field.
- /* Store the value. */
- bfd_put_32 (input_bfd, x, location);
- return;
+ All we need to do here is shuffle the bits appropriately. As
+ above, the two 16-bit halves must be swapped on a
+ little-endian system. */
+ value = (((value & 0x7e0) << 16)
+ | ((value & 0xf800) << 5)
+ | (value & 0x1f));
}
/* Set the field. */
x |= (value & howto->dst_mask);
+ /* Swap the high- and low-order 16 bits on little-endian systems
+ when doing a MIPS16 relocation. */
+ if ((ELF32_R_TYPE (relocation->r_info) == R_MIPS16_GPREL
+ || ELF32_R_TYPE (relocation->r_info) == R_MIPS16_26)
+ && bfd_little_endian (input_bfd))
+ x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
+
/* Put the value into the output. */
bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
}
return false;
addend |= last_hi16_addend;
}
+ else if (r_type == R_MIPS16_GPREL)
+ {
+ /* The addend is scrambled in the object file. See
+ mips_elf_perform_relocation for details on the
+ format. */
+ addend = (((addend & 0x1f0000) >> 5)
+ | ((addend & 0x7e00000) >> 16)
+ | (addend & 0x1f));
+ }
}
else
addend = rel->r_addend;
higher. Therefore, it make sense to put those symbols
that need GOT entries at the end of the symbol table. We
do that here. */
- if (!mips_elf_sort_hash_table (info))
+ if (!mips_elf_sort_hash_table (info, 1))
return false;
i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
+ g->global_gotno = i;
s->_raw_size += i * MIPS_ELF_GOT_SIZE (dynobj);
}
else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)