retitle 274738 [mips, mipsel] Fix dynamic function call stub for big binaries
tags 274738 +patch
thanks

The root of this problem was the stub created for lazily resolved
function references. It uses currently a signed 16 bit immediate
for the symbol index, which wraps to negative values for more than
2^15 symbols, which caused ld.so to load garbage as function pointer.

The appended patch fixes this, and supersedes the current mips-specific
xgot workaround (120_mips_xgot_multigot_workaround.dpatch). This patch
already went in upstream.

Applying this patch is a preliminary to make ghc and gcj work reliably
for mips/mipsel. Applying this patch will also cause all current xgot
users to fail, until the xgot option is removed for them. Those were
some weeks ago:

asterisk-oh323_0.7.3-2
asterisk_1:1.2.4.dfsg-6
enigmail_2:0.93-1
gnugk_2:2.2.3-2-2
inkscape_0.43-4
mozilla-thunderbird_1.0.7-3
mozilla_2:1.7.12-1.1
nvu_1.0final-1
ohphone_1:1.4.5+20060204-1
openam_1.1.18+20050304-2
openh323_1.18.0-1
openmcu_2.1.1-2
pstngw_1.3.1-6
python-qt3_3.15-4
qt-x11-free_3:3.3.6-1
t38modem_0.8.0+20050304-2
thunderbird_1.5-4
vegastrike_0.4.3-4
xulrunner_1.8.0.1-5

plus firefox, for which I did a test build without xgot to verify the
patch. I plan to do patches for them in the next days.


Thiemo


#! /bin/sh -e
## 122_mips_dynsymstub.dpatch
##
## DP: Description: 
## DP: Author: Dave Daney <[EMAIL PROTECTED]>, Richard Sandiford <[EMAIL 
PROTECTED]>, Thiemo Seufer <[EMAIL PROTECTED]>
## DP: Upstream status: In trunk and 2.17 branch.
## DP: Date: 2006-06-12

if [ $# -lt 1 ]; then
    echo "`basename $0`: script expects -patch|-unpatch as argument" >&2
    exit 1
fi

[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}"

case "$1" in
    -patch) patch -p0 ${patch_opts} < $0;;
    -unpatch) patch -R -p0 ${patch_opts} < $0;;
    *)
        echo "`basename $0`: script expects -patch|-unpatch as argument" >&2
        exit 1;;
esac

exit 0

@DPATCH@
Index: bfd/elfxx-mips.c
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.c,v
retrieving revision 1.169
retrieving revision 1.172
diff -u -p -r1.169 -r1.172
--- bfd/elfxx-mips.c    22 May 2006 15:06:22 -0000      1.169
+++ bfd/elfxx-mips.c    11 Jun 2006 08:33:26 -0000      1.172
@@ -335,6 +335,8 @@ struct mips_elf_link_hash_table
   bfd_vma plt_header_size;
   /* The size of a PLT entry in bytes (VxWorks only).  */
   bfd_vma plt_entry_size;
+  /* The size of a function stub entry in bytes.  */
+  bfd_vma function_stub_size;
 };
 
 #define TLS_RELOC_P(r_type) \
@@ -623,20 +625,25 @@ static bfd *reldyn_sorting_bfd;
 #define MIPS_ELF_GOT_MAX_SIZE(INFO) (ELF_MIPS_GP_OFFSET (INFO) + 0x7fff)
 
 /* Instructions which appear in a stub.  */
-#define STUB_LW(abfd)                                          \
-  ((ABI_64_P (abfd)                                            \
-    ? 0xdf998010               /* ld t9,0x8010(gp) */          \
-    : 0x8f998010))              /* lw t9,0x8010(gp) */
-#define STUB_MOVE(abfd)                                         \
-   ((ABI_64_P (abfd)                                           \
-     ? 0x03e0782d              /* daddu t7,ra */               \
-     : 0x03e07821))            /* addu t7,ra */
-#define STUB_JALR 0x0320f809   /* jalr t9,ra */
-#define STUB_LI16(abfd)                                         \
-  ((ABI_64_P (abfd)                                            \
-   ? 0x64180000                        /* daddiu t8,zero,0 */          \
-   : 0x24180000))              /* addiu t8,zero,0 */
-#define MIPS_FUNCTION_STUB_SIZE (16)
+#define STUB_LW(abfd)                                                  \
+  ((ABI_64_P (abfd)                                                    \
+    ? 0xdf998010                               /* ld t9,0x8010(gp) */  \
+    : 0x8f998010))                             /* lw t9,0x8010(gp) */
+#define STUB_MOVE(abfd)                                                        
\
+   ((ABI_64_P (abfd)                                                   \
+     ? 0x03e0782d                              /* daddu t7,ra */       \
+     : 0x03e07821))                            /* addu t7,ra */
+#define STUB_LUI(VAL) (0x3c180000 + (VAL))     /* lui t8,VAL */
+#define STUB_JALR 0x0320f809                   /* jalr t9,ra */
+#define STUB_ORI(VAL) (0x37180000 + (VAL))     /* ori t8,t8,VAL */
+#define STUB_LI16U(VAL) (0x34180000 + (VAL))   /* ori t8,zero,VAL unsigned */
+#define STUB_LI16S(abfd, VAL)                                          \
+   ((ABI_64_P (abfd)                                                   \
+    ? (0x64180000 + (VAL))     /* daddiu t8,zero,VAL sign extended */  \
+    : (0x24180000 + (VAL))))   /* addiu t8,zero,VAL sign extended */
+
+#define MIPS_FUNCTION_STUB_NORMAL_SIZE 16
+#define MIPS_FUNCTION_STUB_BIG_SIZE 20
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -4068,6 +4075,8 @@ mips_elf_calculate_relocation (bfd *abfd
        }
 
       symbol = sec->output_section->vma + sec->output_offset;
+      /* The target is 16-bit, but the stub isn't.  */
+      target_is_16_bit_code_p = FALSE;
     }
   /* If this is a 16-bit call to a 32- or 64-bit function with a stub, we
      need to redirect the call to the stub.  */
@@ -6824,7 +6833,9 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
   bfd *dynobj;
   struct mips_elf_link_hash_entry *hmips;
   asection *s;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
 
   /* Make sure we know what is going on here.  */
@@ -6877,7 +6888,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
          h->plt.offset = s->size;
 
          /* Make room for this stub code.  */
-         s->size += MIPS_FUNCTION_STUB_SIZE;
+         s->size += htab->function_stub_size;
 
          /* The last half word of the stub will be filled with the index
             of this symbol in .dynsym section.  */
@@ -7065,6 +7076,32 @@ _bfd_mips_vxworks_adjust_dynamic_symbol 
   return TRUE;
 }
 
+/* Return the number of dynamic section symbols required by OUTPUT_BFD.
+   The number might be exact or a worst-case estimate, depending on how
+   much information is available to elf_backend_omit_section_dynsym at
+   the current linking stage.  */
+
+static bfd_size_type
+count_section_dynsyms (bfd *output_bfd, struct bfd_link_info *info)
+{
+  bfd_size_type count;
+
+  count = 0;
+  if (info->shared || elf_hash_table (info)->is_relocatable_executable)
+    {
+      asection *p;
+      const struct elf_backend_data *bed;
+
+      bed = get_elf_backend_data (output_bfd);
+      for (p = output_bfd->sections; p ; p = p->next)
+       if ((p->flags & SEC_EXCLUDE) == 0
+           && (p->flags & SEC_ALLOC) != 0
+           && !(*bed->elf_backend_omit_section_dynsym) (output_bfd, info, p))
+         ++count;
+    }
+  return count;
+}
+
 /* This function is called after all the input files have been read,
    and the input sections have been assigned to output sections.  We
    check for any mips16 stub sections that we can discard.  */
@@ -7081,6 +7118,7 @@ _bfd_mips_elf_always_size_sections (bfd 
   int i;
   bfd_size_type loadable_size = 0;
   bfd_size_type local_gotno;
+  bfd_size_type dynsymcount;
   bfd *sub;
   struct mips_elf_count_tls_arg count_tls_arg;
   struct mips_elf_link_hash_table *htab;
@@ -7139,10 +7177,22 @@ _bfd_mips_elf_always_size_sections (bfd 
        relocations, then GLOBAL_GOTSYM will be NULL.  */
     i = 0;
 
+  /* Get a worst-case estimate of the number of dynamic symbols needed.
+     At this point, dynsymcount does not account for section symbols
+     and count_section_dynsyms may overestimate the number that will
+     be needed.  */
+  dynsymcount = (elf_hash_table (info)->dynsymcount
+                + count_section_dynsyms (output_bfd, info));
+
+  /* Determine the size of one stub entry.  */
+  htab->function_stub_size = (dynsymcount > 0x10000
+                             ? MIPS_FUNCTION_STUB_BIG_SIZE
+                             : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+
   /* In the worst case, we'll get one stub per dynamic symbol, plus
      one to account for the dummy entry at the end required by IRIX
      rld.  */
-  loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1);
+  loadable_size += htab->function_stub_size * (i + 1);
 
   if (htab->is_vxworks)
     /* There's no need to allocate page entries for VxWorks; R_MIPS_GOT16
@@ -7359,14 +7409,14 @@ _bfd_mips_elf_size_dynamic_sections (bfd
       else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
        {
          /* IRIX rld assumes that the function stub isn't at the end
-            of .text section. So put a dummy. XXX  */
-         s->size += MIPS_FUNCTION_STUB_SIZE;
+            of .text section.  So put a dummy.  XXX  */
+         s->size += htab->function_stub_size;
        }
       else if (! info->shared
               && ! mips_elf_hash_table (info)->use_rld_obj_head
               && strncmp (name, ".rld_map", 8) == 0)
        {
-         /* We add a room for __rld_map. It will be filled in by the
+         /* We add a room for __rld_map.  It will be filled in by the
             rtld to contain a pointer to the _r_debug structure.  */
          s->size += 4;
        }
@@ -7997,13 +8047,16 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
   asection *sgot;
   struct mips_got_info *g, *gg;
   const char *name;
+  int idx;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
 
   if (h->plt.offset != MINUS_ONE)
     {
       asection *s;
-      bfd_byte stub[MIPS_FUNCTION_STUB_SIZE];
+      bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
 
       /* This symbol has a stub.  Set it up.  */
 
@@ -8013,18 +8066,42 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
                                   MIPS_ELF_STUB_SECTION_NAME (dynobj));
       BFD_ASSERT (s != NULL);
 
-      /* FIXME: Can h->dynindx be more than 64K?  */
-      if (h->dynindx & 0xffff0000)
+      BFD_ASSERT ((htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+                  || (h->dynindx <= 0xffff));
+
+      /* Values up to 2^31 - 1 are allowed.  Larger values would cause
+        sign extension at runtime in the stub, resulting in a negative
+        index value.  */
+      if (h->dynindx & ~0x7fffffff)
        return FALSE;
 
       /* Fill the stub.  */
-      bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub);
-      bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + 4);
-      bfd_put_32 (output_bfd, STUB_JALR, stub + 8);
-      bfd_put_32 (output_bfd, STUB_LI16 (output_bfd) + h->dynindx, stub + 12);
+      idx = 0;
+      bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub + idx);
+      idx += 4;
+      bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + idx);
+      idx += 4;
+      if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+        {
+          bfd_put_32 (output_bfd, STUB_LUI ((h->dynindx >> 16) & 0x7fff),
+                      stub + idx);
+          idx += 4;
+        }
+      bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
+      idx += 4;
+
+      /* If a large stub is not required and sign extension is not a
+         problem, then use legacy code in the stub.  */
+      if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+       bfd_put_32 (output_bfd, STUB_ORI (h->dynindx & 0xffff), stub + idx);
+      else if (h->dynindx & ~0x7fff)
+        bfd_put_32 (output_bfd, STUB_LI16U (h->dynindx & 0xffff), stub + idx);
+      else
+        bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
+                   stub + idx);
 
       BFD_ASSERT (h->plt.offset <= s->size);
-      memcpy (s->contents + h->plt.offset, stub, MIPS_FUNCTION_STUB_SIZE);
+      memcpy (s->contents + h->plt.offset, stub, htab->function_stub_size);
 
       /* Mark the symbol as undefined.  plt.offset != -1 occurs
         only for the referenced symbol.  */
@@ -8827,10 +8904,10 @@ _bfd_mips_elf_finish_dynamic_sections (b
              {
                file_ptr dummy_offset;
 
-               BFD_ASSERT (s->size >= MIPS_FUNCTION_STUB_SIZE);
-               dummy_offset = s->size - MIPS_FUNCTION_STUB_SIZE;
+               BFD_ASSERT (s->size >= htab->function_stub_size);
+               dummy_offset = s->size - htab->function_stub_size;
                memset (s->contents + dummy_offset, 0,
-                       MIPS_FUNCTION_STUB_SIZE);
+                       htab->function_stub_size);
              }
          }
       }
@@ -9942,6 +10019,7 @@ _bfd_mips_elf_link_hash_table_create (bf
   ret->splt = NULL;
   ret->plt_header_size = 0;
   ret->plt_entry_size = 0;
+  ret->function_stub_size = 0;
 
   return &ret->root.root;
 }
@@ -10017,18 +10095,7 @@ _bfd_mips_elf_final_link (bfd *abfd, str
         we count the sections after (possibly) removing the .options
         section above.  */
 
-      dynsecsymcount = 0;
-      if (info->shared)
-       {
-         asection * p;
-
-         for (p = abfd->sections; p ; p = p->next)
-           if ((p->flags & SEC_EXCLUDE) == 0
-               && (p->flags & SEC_ALLOC) != 0
-               && !(*bed->elf_backend_omit_section_dynsym) (abfd, info, p))
-             ++ dynsecsymcount;
-       }
-
+      dynsecsymcount = count_section_dynsyms (abfd, info);
       if (! mips_elf_sort_hash_table (info, dynsecsymcount + 1))
        return FALSE;
 
Index: ld/testsuite/ld-mips-elf/mips-elf.exp
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-mips-elf/mips-elf.exp,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -p -r1.35 -r1.36
--- ld/testsuite/ld-mips-elf/mips-elf.exp       27 Mar 2006 11:30:54 -0000      
1.35
+++ ld/testsuite/ld-mips-elf/mips-elf.exp       11 Jun 2006 08:33:27 -0000      
1.36
@@ -137,6 +137,33 @@ if $has_newabi {
     run_dump_test "emit-relocs-1"
 }
 
+if {[istarget mips*-*-linux*]} {
+     # The number of symbols that are always included in the symbol table
+     # for these tests.  The 5 are:
+     #
+     #     the null symbol entry
+     #     the .MIPS.stubs section symbol
+     #     the .text section symbol
+     #     _gp
+     #     _GLOBAL_OFFSET_TABLE_
+     set base_syms 5
+     foreach dynsym { 7fff 8000 fff0 10000 2fe80 } {
+        run_ld_link_tests \
+            [list [list \
+                       "Stub for dynsym 0x$dynsym" \
+                       "-shared -melf32btsmip -T stub-dynsym-1.ld" \
+                       [concat \
+                            "-EB -march=mips1 -32 -KPIC" \
+                            "--defsym base_syms=$base_syms" \
+                            "--defsym dynsym=0x$dynsym"] \
+                       [list "stub-dynsym-1.s"] \
+                       [list [list \
+                                  "objdump" "-dz" \
+                                  "stub-dynsym-1-$dynsym.d"]] \
+                       "stub-dynsym-1-$dynsym"]]
+     }
+ }
+
 # For tests which may involve multiple files, use run_ld_link_tests.
 
 # List contains test-items with 3 items followed by 2 lists:
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d      11 Jun 2006 08:33:27 
-0000      1.1
@@ -0,0 +1,18 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section \.MIPS\.stubs:
+
+.* <\.MIPS.stubs>:
+.*:    8f998010        lw      t9,-32752\(gp\)
+.*:    03e07821        move    t7,ra
+.*:    3c180001        lui     t8,0x1
+.*:    0320f809        jalr    t9
+.*:    37180000        ori     t8,t8,0x0
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+Disassembly of section .text:
+#pass
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d      11 Jun 2006 08:33:27 
-0000      1.1
@@ -0,0 +1,18 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section \.MIPS\.stubs:
+
+.* <\.MIPS.stubs>:
+.*:    8f998010        lw      t9,-32752\(gp\)
+.*:    03e07821        move    t7,ra
+.*:    3c180002        lui     t8,0x2
+.*:    0320f809        jalr    t9
+.*:    3718fe80        ori     t8,t8,0xfe80
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+Disassembly of section .text:
+#pass
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d       11 Jun 2006 08:33:27 
-0000      1.1
@@ -0,0 +1,16 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section \.MIPS\.stubs:
+
+.* <\.MIPS.stubs>:
+.*:    8f998010        lw      t9,-32752\(gp\)
+.*:    03e07821        move    t7,ra
+.*:    0320f809        jalr    t9
+.*:    24187fff        li      t8,32767
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+Disassembly of section .text:
+#pass
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d       11 Jun 2006 08:33:27 
-0000      1.1
@@ -0,0 +1,16 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section \.MIPS\.stubs:
+
+.* <\.MIPS.stubs>:
+.*:    8f998010        lw      t9,-32752\(gp\)
+.*:    03e07821        move    t7,ra
+.*:    0320f809        jalr    t9
+.*:    34188000        li      t8,0x8000
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+Disassembly of section .text:
+#pass
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d       11 Jun 2006 08:33:27 
-0000      1.1
@@ -0,0 +1,16 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section \.MIPS\.stubs:
+
+.* <\.MIPS.stubs>:
+.*:    8f998010        lw      t9,-32752\(gp\)
+.*:    03e07821        move    t7,ra
+.*:    0320f809        jalr    t9
+.*:    3418fff0        li      t8,0xfff0
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+.*:    00000000        nop
+Disassembly of section .text:
+#pass
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1.ld
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1.ld
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1.ld
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1.ld   11 Jun 2006 08:33:27 -0000      
1.1
@@ -0,0 +1,17 @@
+SECTIONS
+{
+  . = 0x80000;
+  .interp : { *(.interp) }
+  .hash : { *(.hash) }
+  .dynsym : { *(.dynsym) }
+  .dynstr : { *(.dynstr) }
+  .rel.dyn : { *(.rel.dyn) }
+  .MIPS.stubs : { *(.MIPS.stubs) }
+  .text : { *(.text) }
+
+  . = ALIGN (0x10000);
+  _gp = . + 0x7ff0;
+  .got : { *(.got) }
+
+  /DISCARD/ : { *(.reginfo) }
+}
Index: ld/testsuite/ld-mips-elf/stub-dynsym-1.s
===================================================================
RCS file: ld/testsuite/ld-mips-elf/stub-dynsym-1.s
diff -N ld/testsuite/ld-mips-elf/stub-dynsym-1.s
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/stub-dynsym-1.s    11 Jun 2006 08:33:27 -0000      
1.1
@@ -0,0 +1,10 @@
+       .macro  decl
+       .global exported\@
+       .equ    exported\@,\@
+       .endm
+
+       .rept   dynsym - base_syms
+       decl
+       .endr
+
+       lw      $25,%call16(foo)($gp)


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to