* elf32-mips.c (mips_elf_obtain_contents): Swap 16-bit halves of
[deliverable/binutils-gdb.git] / bfd / elf32-mips.c
index 281f485a2457321456c6068ba8e66547f608c807..8f45fe114c99d5876b7f75e9b02f12505cec37ff 100644 (file)
@@ -51,6 +51,8 @@ struct mips_got_info
   /* 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.  */
@@ -177,7 +179,7 @@ static boolean mips_elf_assign_gp PARAMS ((bfd *, bfd_vma *));
 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 **));
@@ -923,10 +925,7 @@ static reloc_howto_type elf_mips16_jump_howto =
         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 */
@@ -938,8 +937,8 @@ static reloc_howto_type elf_mips16_gprel_howto =
         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 */
 
 
@@ -4320,6 +4319,30 @@ _bfd_mips_elf_final_link (abfd, info)
       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
@@ -5305,11 +5328,15 @@ mips_elf_sort_hash_table_f (h, data)
 
 /* 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;
@@ -5319,7 +5346,7 @@ mips_elf_sort_hash_table (info)
 
   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, 
@@ -5887,7 +5914,7 @@ mips_elf_calculate_relocation (abfd,
     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:
@@ -5944,6 +5971,11 @@ mips_elf_calculate_relocation (abfd,
 
       /* 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;
@@ -6044,11 +6076,6 @@ mips_elf_calculate_relocation (abfd,
       /* 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;
@@ -6071,36 +6098,15 @@ mips_elf_obtain_contents (howto, relocation, input_bfd, contents)
   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;
 }
@@ -6203,23 +6209,43 @@ mips_elf_perform_relocation (info, howto, relocation, value,
                 | ((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);
 }
@@ -6321,6 +6347,15 @@ _bfd_mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                    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;
@@ -7595,10 +7630,11 @@ _bfd_mips_elf_size_dynamic_sections (output_bfd, info)
             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)
This page took 0.026735 seconds and 4 git commands to generate.