PR ld/22966: Fix n64 MIPS `.got.plt' range checks
authorMaciej W. Rozycki <macro@mips.com>
Tue, 19 Jun 2018 23:37:51 +0000 (00:37 +0100)
committerMaciej W. Rozycki <macro@mips.com>
Tue, 19 Jun 2018 23:37:51 +0000 (00:37 +0100)
The addressable signed 32-bit range for n64 MIPS `.got.plt' references
is from 0xffffffff7fff8000 to 0x7fff7fff, due to how the composition of
an LUI and an LD instruction works for address calculation in the 64-bit
addressing mode, such as when CP0.Status.UX=1.

We currently have a range check in `mips_finish_exec_plt', however it is
not correct as it verifies that the `.got.plt' start address referred is
between 0xffffffff80000000 and 0x7fffffff.  It is also implemented as an
assertion rather than a proper error message despite that the situation
can be triggered by user input.  Additionally there is no check made for
individual `.got.plt' entries referred even though they can be out of
range while the `.got.plt' start address is not.

Fix all these problems and use the correct range for the check, limiting
it to n64 output files, and then issue a proper error message both in
`mips_finish_exec_plt' and in `_bfd_mips_elf_finish_dynamic_symbol',
suggesting the use of the `-Ttext-segment=...' option that will often
work and with the default linker scripts in particular.  Add suitable
tests covering boundary cases.

bfd/
PR ld/22966
* elfxx-mips.c (_bfd_mips_elf_finish_dynamic_symbol): Verify the
`.got.plt' entry referred is in range.
(mips_finish_exec_plt): Correct the range check for `.got.plt'
start.  Replace the assertion used for that with a proper error
message.

ld/
PR ld/22966
* testsuite/ld-mips-elf/n64-plt-1.dd: New test.
* testsuite/ld-mips-elf/n64-plt-1.gd: New test.
* testsuite/ld-mips-elf/n64-plt-2.ed: New test.
* testsuite/ld-mips-elf/n64-plt-3.ed: New test.
* testsuite/ld-mips-elf/n64-plt-4.dd: New test.
* testsuite/ld-mips-elf/n64-plt-4.gd: New test.
* testsuite/ld-mips-elf/n64-plt-1.ld: New test linker script.
* testsuite/ld-mips-elf/n64-plt-2.ld: New test linker script.
* testsuite/ld-mips-elf/n64-plt-3.ld: New test linker script.
* testsuite/ld-mips-elf/n64-plt-4.ld: New test linker script.
* testsuite/ld-mips-elf/n64-plt.s: New test source.
* testsuite/ld-mips-elf/n64-plt-lib.s: New test source.
* testsuite/ld-mips-elf/mips-elf.exp: Run the new tests.

16 files changed:
bfd/ChangeLog
bfd/elfxx-mips.c
ld/ChangeLog
ld/testsuite/ld-mips-elf/mips-elf.exp
ld/testsuite/ld-mips-elf/n64-plt-1.dd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-1.gd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-1.ld [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-2.ed [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-2.ld [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-3.ed [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-3.ld [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-4.dd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-4.gd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-4.ld [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt-lib.s [new file with mode: 0644]
ld/testsuite/ld-mips-elf/n64-plt.s [new file with mode: 0644]

index b812fdc6e5992d76dee9241fac18a2ec629d26e9..d12dc9942561a255982d58b14037bb10edcc7dd4 100644 (file)
@@ -1,3 +1,12 @@
+2018-06-19  Maciej W. Rozycki  <macro@mips.com>
+
+       PR ld/22966
+       * elfxx-mips.c (_bfd_mips_elf_finish_dynamic_symbol): Verify the
+       `.got.plt' entry referred is in range.
+       (mips_finish_exec_plt): Correct the range check for `.got.plt'
+       start.  Replace the assertion used for that with a proper error
+       message.
+
 2018-06-19  Maciej W. Rozycki  <macro@mips.com>
 
        * elfxx-mips.c (_bfd_mips_elf_finish_dynamic_symbol): Fix
index d25f4ff3d456acfed4addfdba8d6a80f5170452a..844750eb9a15d0a59c74c7e2767f21e253affcb9 100644 (file)
@@ -10602,6 +10602,22 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       got_address_high = ((got_address + 0x8000) >> 16) & 0xffff;
       got_address_low = got_address & 0xffff;
 
+      /* The PLT sequence is not safe for N64 if .got.plt entry's address
+        cannot be loaded in two instructions.  */
+      if (ABI_64_P (output_bfd)
+         && ((got_address + 0x80008000) & ~(bfd_vma) 0xffffffff) != 0)
+       {
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB: `%pA' entry VMA of %#" PRIx64 " outside the 32-bit range "
+              "supported; consider using `-Ttext-segment=...'"),
+            output_bfd,
+            htab->root.sgotplt->output_section,
+            (int64_t) got_address);
+         bfd_set_error (bfd_error_no_error);
+         return FALSE;
+       }
+
       /* Initially point the .got.plt entry at the PLT header.  */
       loc = (htab->root.sgotplt->contents
             + got_index * MIPS_ELF_GOT_SIZE (dynobj));
@@ -11246,8 +11262,19 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
 
   /* The PLT sequence is not safe for N64 if .got.plt's address can
      not be loaded in two instructions.  */
-  BFD_ASSERT ((gotplt_value & ~(bfd_vma) 0x7fffffff) == 0
-             || ~(gotplt_value | 0x7fffffff) == 0);
+  if (ABI_64_P (output_bfd)
+      && ((gotplt_value + 0x80008000) & ~(bfd_vma) 0xffffffff) != 0)
+    {
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%pB: `%pA' start VMA of %#" PRIx64 " outside the 32-bit range "
+          "supported; consider using `-Ttext-segment=...'"),
+        output_bfd,
+        htab->root.sgotplt->output_section,
+        (int64_t) gotplt_value);
+      bfd_set_error (bfd_error_no_error);
+      return FALSE;
+    }
 
   /* Install the PLT header.  */
   loc = htab->root.splt->contents;
index 495eeef384c2fc360fb5d668ab5110cad38766d7..235fa514574f315f0b476a897ae8ca0b1a70e9b6 100644 (file)
@@ -1,3 +1,20 @@
+2018-06-19  Maciej W. Rozycki  <macro@mips.com>
+
+       PR ld/22966
+       * testsuite/ld-mips-elf/n64-plt-1.dd: New test.
+       * testsuite/ld-mips-elf/n64-plt-1.gd: New test.
+       * testsuite/ld-mips-elf/n64-plt-2.ed: New test.
+       * testsuite/ld-mips-elf/n64-plt-3.ed: New test.
+       * testsuite/ld-mips-elf/n64-plt-4.dd: New test.
+       * testsuite/ld-mips-elf/n64-plt-4.gd: New test.
+       * testsuite/ld-mips-elf/n64-plt-1.ld: New test linker script.
+       * testsuite/ld-mips-elf/n64-plt-2.ld: New test linker script.
+       * testsuite/ld-mips-elf/n64-plt-3.ld: New test linker script.
+       * testsuite/ld-mips-elf/n64-plt-4.ld: New test linker script.
+       * testsuite/ld-mips-elf/n64-plt.s: New test source.
+       * testsuite/ld-mips-elf/n64-plt-lib.s: New test source.
+       * testsuite/ld-mips-elf/mips-elf.exp: Run the new tests.
+
 2018-06-19  Maciej W. Rozycki  <macro@mips.com>
 
        * testsuite/ld-elf/shared.exp: XFAIL DT_TEXTREL map file warning
index 29e227445762a0d50e8033ad4588693cdd2c61bd..1631d855dfe5982e27326c93dfd8e08f8f65cf23 100644 (file)
@@ -1167,6 +1167,50 @@ if { $linux_gnu } {
     }
 }
 
+# Verify graceful handling of n64 PLT 32-bit range overflows.  Given
+# that the alignment of `.got.plt' is 8 the highest usable positive
+# address is 0x7fff7ff8 and the lowest usable negative address is
+# 0xffffffff7fff8000.
+if { $linux_gnu } {
+    run_ld_link_tests [list \
+       [list "Shared library for MIPS n64 PLT 32-bit range tests" \
+             "-shared $abi_ldflags(n64)" "" \
+             "$abi_asflags(n64)" \
+             { n64-plt-lib.s } \
+             {} \
+             "n64-plt-lib.so"] \
+       [list "MIPS n64 PLT 32-bit range test 1" \
+             "$abi_ldflags(n64) -T n64-plt-1.ld -e foo" \
+             "tmpdir/n64-plt-lib.so" \
+             "$abi_asflags(n64)" \
+             { n64-plt.s } \
+             { { objdump -d n64-plt-1.dd } \
+               { readelf -A n64-plt-1.gd } } \
+             "n64-plt-1"] \
+       [list "MIPS n64 PLT 32-bit range test 2" \
+             "$abi_ldflags(n64) -T n64-plt-2.ld -e foo" \
+             "tmpdir/n64-plt-lib.so" \
+             "$abi_asflags(n64)" \
+             { n64-plt.s } \
+             { { ld n64-plt-2.ed } } \
+             "n64-plt-2"] \
+       [list "MIPS n64 PLT 32-bit range test 3" \
+             "$abi_ldflags(n64) -T n64-plt-3.ld -e foo" \
+             "tmpdir/n64-plt-lib.so" \
+             "$abi_asflags(n64)" \
+             { n64-plt.s } \
+             { { ld n64-plt-3.ed } } \
+             "n64-plt-3"] \
+       [list "MIPS n64 PLT 32-bit range test 4" \
+             "$abi_ldflags(n64) -T n64-plt-4.ld -e foo" \
+             "tmpdir/n64-plt-lib.so" \
+             "$abi_asflags(n64)" \
+             { n64-plt.s } \
+             { { objdump -d n64-plt-4.dd } \
+               { readelf -A n64-plt-4.gd } } \
+             "n64-plt-4"]]
+}
+
 # PR ld/19908 export class tests.
 if { $linux_gnu } {
     run_ld_link_tests [list \
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-1.dd b/ld/testsuite/ld-mips-elf/n64-plt-1.dd
new file mode 100644 (file)
index 0000000..4c24a9a
--- /dev/null
@@ -0,0 +1,26 @@
+.*: +file format .*mips.*
+
+Disassembly of section \.plt:
+
+0000000010000280 <_PROCEDURE_LINKAGE_TABLE_>:
+    10000280:  3c0e7fff        lui     t2,0x7fff
+    10000284:  ddd97fe8        ld      t9,32744\(t2\)
+    10000288:  25ce7fe8        addiu   t2,t2,32744
+    1000028c:  030ec023        subu    t8,t8,t2
+    10000290:  03e07825        move    t3,ra
+    10000294:  0018c0c2        srl     t8,t8,0x3
+    10000298:  0320f809        jalr    t9
+    1000029c:  2718fffe        addiu   t8,t8,-2
+
+00000000100002a0 <bar@plt>:
+    100002a0:  3c0f7fff        lui     t3,0x7fff
+    100002a4:  ddf97ff8        ld      t9,32760\(t3\)
+    100002a8:  03200008        jr      t9
+    100002ac:  25f87ff8        addiu   t8,t3,32760
+
+Disassembly of section \.text:
+
+00000000100002b0 <foo>:
+    100002b0:  080000a8        j       100002a0 <bar@plt>
+    100002b4:  00000000        nop
+       \.\.\.
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-1.gd b/ld/testsuite/ld-mips-elf/n64-plt-1.gd
new file mode 100644 (file)
index 0000000..5804e66
--- /dev/null
@@ -0,0 +1,18 @@
+Primary GOT:
+ Canonical gp value: 000000007ffffff0
+
+ Reserved entries:
+           Address     Access          Initial Purpose
+  000000007fff8000 -32752\(gp\) 0000000000000000 Lazy resolver
+  000000007fff8008 -32744\(gp\) 8000000000000000 Module pointer \(GNU extension\)
+
+PLT GOT:
+
+ Reserved entries:
+           Address          Initial Purpose
+  000000007fff7fe8 0000000000000000 PLT lazy resolver
+  000000007fff7ff0 0000000000000000 Module pointer
+
+ Entries:
+           Address          Initial         Sym\.Val\. Type    Ndx Name
+  000000007fff7ff8 0000000010000280 0000000000000000 FUNC    UND bar
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-1.ld b/ld/testsuite/ld-mips-elf/n64-plt-1.ld
new file mode 100644 (file)
index 0000000..1ef3106
--- /dev/null
@@ -0,0 +1,22 @@
+MEMORY
+{
+  text (rx) : ORIGIN = 0x10000000, LENGTH = 0x10000
+  data (w) : ORIGIN = 0x7fff7fe8, LENGTH = 0x10000
+}
+SECTIONS
+{
+  .dynamic : { *(.dynamic) } >text
+  .hash : { *(.hash) } >text
+  .dynsym : { *(.dynsym) } >text
+  .dynstr : { *(.dynstr) } >text
+  .rel.plt : { *(.rel.plt) } >text
+  .plt : { *(.plt) } >text
+  .text : { *(.text) } >text
+  .interp : { *(.interp) } >text
+  .got.plt : { *(.got.plt) } >data
+  .got : { *(.got) } >data
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  .shstrtab : { *(.shstrtab) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-2.ed b/ld/testsuite/ld-mips-elf/n64-plt-2.ed
new file mode 100644 (file)
index 0000000..4a42ec1
--- /dev/null
@@ -0,0 +1 @@
+[^\n]*: `\.got\.plt' entry VMA of 0x7fff8000 outside the 32-bit range supported; consider using `-Ttext-segment=\.\.\.'
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-2.ld b/ld/testsuite/ld-mips-elf/n64-plt-2.ld
new file mode 100644 (file)
index 0000000..4d2ab48
--- /dev/null
@@ -0,0 +1,23 @@
+MEMORY
+{
+  text (rx) : ORIGIN = 0x10000000, LENGTH = 0x10000
+  data (w) : ORIGIN = 0x7fff7ff0, LENGTH = 0x10000
+}
+SECTIONS
+{
+  .dynamic : { *(.dynamic) } >text
+  .hash : { *(.hash) } >text
+  .dynsym : { *(.dynsym) } >text
+  .dynstr : { *(.dynstr) } >text
+  .rel.plt : { *(.rel.plt) } >text
+  .plt : { *(.plt) } >text
+  .text : { *(.text) } >text
+  .interp : { *(.interp) } >text
+  .got.plt : { *(.got.plt) } >data
+  .rld.map : { *(.rld.map) } >data
+  .got : { *(.got) } >data
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  .shstrtab : { *(.shstrtab) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-3.ed b/ld/testsuite/ld-mips-elf/n64-plt-3.ed
new file mode 100644 (file)
index 0000000..29a346b
--- /dev/null
@@ -0,0 +1 @@
+[^\n]*: `\.got\.plt' start VMA of 0xffffffff7fff7ff0 outside the 32-bit range supported; consider using `-Ttext-segment=\.\.\.'
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-3.ld b/ld/testsuite/ld-mips-elf/n64-plt-3.ld
new file mode 100644 (file)
index 0000000..be88c49
--- /dev/null
@@ -0,0 +1,23 @@
+MEMORY
+{
+  text (rx) : ORIGIN = 0xffffffff10000000, LENGTH = 0x10000
+  data (w) : ORIGIN = 0xffffffff7fff7ff0, LENGTH = 0x10000
+}
+SECTIONS
+{
+  .dynamic : { *(.dynamic) } >text
+  .hash : { *(.hash) } >text
+  .dynsym : { *(.dynsym) } >text
+  .dynstr : { *(.dynstr) } >text
+  .rel.plt : { *(.rel.plt) } >text
+  .plt : { *(.plt) } >text
+  .text : { *(.text) } >text
+  .interp : { *(.interp) } >text
+  .got.plt : { *(.got.plt) } >data
+  .rld.map : { *(.rld.map) } >data
+  .got : { *(.got) } >data
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  .shstrtab : { *(.shstrtab) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-4.dd b/ld/testsuite/ld-mips-elf/n64-plt-4.dd
new file mode 100644 (file)
index 0000000..01c5025
--- /dev/null
@@ -0,0 +1,26 @@
+.*: +file format .*mips.*
+
+Disassembly of section \.plt:
+
+ffffffff10000280 <_PROCEDURE_LINKAGE_TABLE_>:
+ffffffff10000280:      3c0e8000        lui     t2,0x8000
+ffffffff10000284:      ddd98000        ld      t9,-32768\(t2\)
+ffffffff10000288:      25ce8000        addiu   t2,t2,-32768
+ffffffff1000028c:      030ec023        subu    t8,t8,t2
+ffffffff10000290:      03e07825        move    t3,ra
+ffffffff10000294:      0018c0c2        srl     t8,t8,0x3
+ffffffff10000298:      0320f809        jalr    t9
+ffffffff1000029c:      2718fffe        addiu   t8,t8,-2
+
+ffffffff100002a0 <bar@plt>:
+ffffffff100002a0:      3c0f8000        lui     t3,0x8000
+ffffffff100002a4:      ddf98010        ld      t9,-32752\(t3\)
+ffffffff100002a8:      03200008        jr      t9
+ffffffff100002ac:      25f88010        addiu   t8,t3,-32752
+
+Disassembly of section \.text:
+
+ffffffff100002b0 <foo>:
+ffffffff100002b0:      080000a8        j       ffffffff100002a0 <bar@plt>
+ffffffff100002b4:      00000000        nop
+       \.\.\.
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-4.gd b/ld/testsuite/ld-mips-elf/n64-plt-4.gd
new file mode 100644 (file)
index 0000000..f9d1ac9
--- /dev/null
@@ -0,0 +1,18 @@
+Primary GOT:
+ Canonical gp value: ffffffff80000010
+
+ Reserved entries:
+           Address     Access          Initial Purpose
+  ffffffff7fff8020 -32752\(gp\) 0000000000000000 Lazy resolver
+  ffffffff7fff8028 -32744\(gp\) 8000000000000000 Module pointer \(GNU extension\)
+
+PLT GOT:
+
+ Reserved entries:
+           Address          Initial Purpose
+  ffffffff7fff8000 0000000000000000 PLT lazy resolver
+  ffffffff7fff8008 0000000000000000 Module pointer
+
+ Entries:
+           Address          Initial         Sym\.Val\. Type    Ndx Name
+  ffffffff7fff8010 ffffffff10000280 0000000000000000 FUNC    UND bar
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-4.ld b/ld/testsuite/ld-mips-elf/n64-plt-4.ld
new file mode 100644 (file)
index 0000000..e1f602d
--- /dev/null
@@ -0,0 +1,23 @@
+MEMORY
+{
+  text (rx) : ORIGIN = 0xffffffff10000000, LENGTH = 0x10000
+  data (w) : ORIGIN = 0xffffffff7fff8000, LENGTH = 0x10000
+}
+SECTIONS
+{
+  .dynamic : { *(.dynamic) } >text
+  .hash : { *(.hash) } >text
+  .dynsym : { *(.dynsym) } >text
+  .dynstr : { *(.dynstr) } >text
+  .rel.plt : { *(.rel.plt) } >text
+  .plt : { *(.plt) } >text
+  .text : { *(.text) } >text
+  .interp : { *(.interp) } >text
+  .got.plt : { *(.got.plt) } >data
+  .rld.map : { *(.rld.map) } >data
+  .got : { *(.got) } >data
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  .shstrtab : { *(.shstrtab) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-mips-elf/n64-plt-lib.s b/ld/testsuite/ld-mips-elf/n64-plt-lib.s
new file mode 100644 (file)
index 0000000..40409af
--- /dev/null
@@ -0,0 +1,11 @@
+       .abicalls
+       .text
+
+       .globl  bar
+       .ent    bar
+bar:
+       .frame  $sp, 0, $31
+       .mask   0x00000000, 0
+       .fmask  0x00000000, 0
+       jr      $31
+       .end    bar
diff --git a/ld/testsuite/ld-mips-elf/n64-plt.s b/ld/testsuite/ld-mips-elf/n64-plt.s
new file mode 100644 (file)
index 0000000..9732044
--- /dev/null
@@ -0,0 +1,9 @@
+       .abicalls
+       .option pic0
+       .text
+
+       .globl  foo
+       .ent    foo
+foo:
+       j       bar
+       .end    foo