or1k: Support large plt_relocs when generating plt entries
authorStafford Horne <shorne@gmail.com>
Thu, 6 May 2021 11:51:25 +0000 (20:51 +0900)
committerStafford Horne <shorne@gmail.com>
Thu, 6 May 2021 11:51:25 +0000 (20:51 +0900)
The current PLT generation code will generate invalid code when the PLT
relocation offset exceeds 64k.  This fixes the issue by detecting large
plt_reloc offsets and generare code sequences to create larger plt
relocations.

The "large" plt code needs 2 extra instructions to create 32-bit offsets.

bfd/ChangeLog:

PR 27746
* elf32-or1k.c (PLT_ENTRY_SIZE_LARGE, PLT_MAX_INSN_COUNT,
OR1K_ADD, OR1K_ORI): New macros to help with plt creation.
(elf_or1k_link_hash_table): New field plt_count.
(elf_or1k_link_hash_entry): New field plt_index.
(elf_or1k_plt_entry_size): New function.
(or1k_write_plt_entry): Update to support variable size PLTs.
(or1k_elf_finish_dynamic_sections): Use new or1k_write_plt_entry
API.
(or1k_elf_finish_dynamic_symbol): Update to write large PLTs
when needed.
(allocate_dynrelocs): Use elf_or1k_plt_entry_size to account for
PLT size.

ld/ChangeLog:

PR 27746
testsuite/ld-or1k/or1k.exp (or1kplttests): Add tests for linking
along with gotha() relocations.
testsuite/ld-or1k/gotha1.dd: New file.
testsuite/ld-or1k/gotha1.s: New file.
testsuite/ld-or1k/gotha2.dd: New file.
testsuite/ld-or1k/gotha2.s: New file
testsuite/ld-or1k/pltlib.s (x): Define size to avoid link
failure.

bfd/ChangeLog
bfd/elf32-or1k.c
ld/ChangeLog
ld/testsuite/ld-or1k/gotha1.dd [new file with mode: 0644]
ld/testsuite/ld-or1k/gotha1.s [new file with mode: 0644]
ld/testsuite/ld-or1k/gotha2.dd [new file with mode: 0644]
ld/testsuite/ld-or1k/gotha2.s [new file with mode: 0644]
ld/testsuite/ld-or1k/or1k.exp
ld/testsuite/ld-or1k/pltlib.s

index 8293e9d7952eabcf8e2f61cfc845b1f5c4337a5f..85084531822de3fa80056374b2e0ea1aac7267b2 100644 (file)
@@ -1,3 +1,19 @@
+2021-05-06  Stafford Horne  <shorne@gmail.com>
+
+       PR 27746
+       * elf32-or1k.c (PLT_ENTRY_SIZE_LARGE, PLT_MAX_INSN_COUNT,
+       OR1K_ADD, OR1K_ORI): New macros to help with plt creation.
+       (elf_or1k_link_hash_table): New field plt_count.
+       (elf_or1k_link_hash_entry): New field plt_index.
+       (elf_or1k_plt_entry_size): New function.
+       (or1k_write_plt_entry): Update to support variable size PLTs.
+       (or1k_elf_finish_dynamic_sections): Use new or1k_write_plt_entry
+       API.
+       (or1k_elf_finish_dynamic_symbol): Update to write large PLTs
+       when needed.
+       (allocate_dynrelocs): Use elf_or1k_plt_entry_size to account for
+       PLT size.
+
 2021-05-06  Stafford Horne  <shorne@gmail.com>
 
        PR 21464
index 5eca4300c4575b8cd529234964339b65dfcec523..4ae7f324d33f6b0af3d0465145da4a52cbb2429f 100644 (file)
 #define N_ONES(X)      (((bfd_vma)2 << (X)) - 1)
 
 #define PLT_ENTRY_SIZE 16
+#define PLT_ENTRY_SIZE_LARGE (6*4)
+#define PLT_MAX_INSN_COUNT 6
 
 #define OR1K_MOVHI(D)          (0x18000000 | (D << 21))
 #define OR1K_ADRP(D)           (0x08000000 | (D << 21))
 #define OR1K_LWZ(D,A)          (0x84000000 | (D << 21) | (A << 16))
+#define OR1K_ADD(D,A,B)                (0xE0000000 | (D << 21) | (A << 16) | (B << 11))
+#define OR1K_ORI(D,A)          (0xA8000000 | (D << 21) | (A << 16))
 #define OR1K_ORI0(D)           (0xA8000000 | (D << 21))
 #define OR1K_JR(B)             (0x44000000 | (B << 11))
 #define OR1K_NOP               0x15000000
@@ -907,6 +911,8 @@ struct elf_or1k_link_hash_entry
 {
   struct elf_link_hash_entry root;
 
+  /* For calculating PLT size.  */
+  bfd_vma plt_index;
   /* Track type of TLS access.  */
   unsigned char tls_type;
 };
@@ -931,9 +937,20 @@ struct elf_or1k_link_hash_table
 {
   struct elf_link_hash_table root;
 
+  bfd_vma plt_count;
   bool saw_plta;
 };
 
+static size_t
+elf_or1k_plt_entry_size (bfd_vma plt_index)
+{
+  bfd_vma plt_reloc;
+
+  plt_reloc = plt_index * sizeof (Elf32_External_Rela);
+
+  return (plt_reloc > 0xffff) ? PLT_ENTRY_SIZE_LARGE : PLT_ENTRY_SIZE;
+}
+
 /* Get the ELF linker hash table from a link_info structure.  */
 #define or1k_elf_hash_table(p) \
   ((is_elf_hash_table ((p)->hash)                                      \
@@ -2222,33 +2239,46 @@ or1k_elf_check_relocs (bfd *abfd,
 }
 
 static void
-or1k_write_plt_entry (bfd *output_bfd, bfd_byte *contents, unsigned insn1,
-                     unsigned insn2, unsigned insn3, unsigned insnj)
+or1k_write_plt_entry (bfd *output_bfd, bfd_byte *contents, unsigned insnj,
+                     unsigned insns[], size_t insn_count)
 {
   unsigned nodelay = elf_elfheader (output_bfd)->e_flags & EF_OR1K_NODELAY;
-  unsigned insn4;
+  unsigned output_insns[PLT_MAX_INSN_COUNT];
+
+  /* Copy instructions into the output buffer.  */
+  for (size_t i = 0; i < insn_count; i++)
+    output_insns[i] = insns[i];
 
   /* Honor the no-delay-slot setting.  */
-  if (insn3 == OR1K_NOP)
+  if (insns[insn_count-1] == OR1K_NOP)
     {
-      insn4 = insn3;
+      unsigned slot1, slot2;
+
       if (nodelay)
-       insn3 = insnj;
+       slot1 = insns[insn_count-2], slot2 = insnj;
       else
-       insn3 = insn2, insn2 = insnj;
+       slot1 = insnj, slot2 = insns[insn_count-2];
+
+      output_insns[insn_count-2] = slot1;
+      output_insns[insn_count-1] = slot2;
+      output_insns[insn_count]   = OR1K_NOP;
     }
   else
     {
+      unsigned slot1, slot2;
+
       if (nodelay)
-       insn4 = insnj;
+       slot1 = insns[insn_count-1], slot2 = insnj;
       else
-       insn4 = insn3, insn3 = insnj;
+       slot1 = insnj, slot2 = insns[insn_count-1];
+
+      output_insns[insn_count-1] = slot1;
+      output_insns[insn_count]   = slot2;
     }
 
-  bfd_put_32 (output_bfd, insn1, contents);
-  bfd_put_32 (output_bfd, insn2, contents + 4);
-  bfd_put_32 (output_bfd, insn3, contents + 8);
-  bfd_put_32 (output_bfd, insn4, contents + 12);
+  /* Write out the output buffer.  */
+  for (size_t i = 0; i < (insn_count+1); i++)
+    bfd_put_32 (output_bfd, output_insns[i], contents + (i*4));
 }
 
 /* Finish up the dynamic sections.  */
@@ -2315,7 +2345,8 @@ or1k_elf_finish_dynamic_sections (bfd *output_bfd,
       splt = htab->root.splt;
       if (splt && splt->size > 0)
        {
-         unsigned plt0, plt1, plt2;
+         unsigned plt[PLT_MAX_INSN_COUNT];
+         size_t plt_insn_count = 3;
          bfd_vma got_addr = sgot->output_section->vma + sgot->output_offset;
 
          /* Note we force 16 byte alignment on the .got, so that
@@ -2326,27 +2357,27 @@ or1k_elf_finish_dynamic_sections (bfd *output_bfd,
              bfd_vma pc = splt->output_section->vma + splt->output_offset;
              unsigned pa = ((got_addr >> 13) - (pc >> 13)) & 0x1fffff;
              unsigned po = got_addr & 0x1fff;
-             plt0 = OR1K_ADRP(12) | pa;
-             plt1 = OR1K_LWZ(15,12) | (po + 8);
-             plt2 = OR1K_LWZ(12,12) | (po + 4);
+             plt[0] = OR1K_ADRP(12) | pa;
+             plt[1] = OR1K_LWZ(15,12) | (po + 8);
+             plt[2] = OR1K_LWZ(12,12) | (po + 4);
            }
          else if (bfd_link_pic (info))
            {
-             plt0 = OR1K_LWZ(15, 16) | 8;      /* .got+8 */
-             plt1 = OR1K_LWZ(12, 16) | 4;      /* .got+4 */
-             plt2 = OR1K_NOP;
+             plt[0] = OR1K_LWZ(15, 16) | 8;    /* .got+8 */
+             plt[1] = OR1K_LWZ(12, 16) | 4;    /* .got+4 */
+             plt[2] = OR1K_NOP;
            }
          else
            {
              unsigned ha = ((got_addr + 0x8000) >> 16) & 0xffff;
              unsigned lo = got_addr & 0xffff;
-             plt0 = OR1K_MOVHI(12) | ha;
-             plt1 = OR1K_LWZ(15,12) | (lo + 8);
-             plt2 = OR1K_LWZ(12,12) | (lo + 4);
+             plt[0] = OR1K_MOVHI(12) | ha;
+             plt[1] = OR1K_LWZ(15,12) | (lo + 8);
+             plt[2] = OR1K_LWZ(12,12) | (lo + 4);
            }
 
-         or1k_write_plt_entry (output_bfd, splt->contents,
-                               plt0, plt1, plt2, OR1K_JR(15));
+         or1k_write_plt_entry (output_bfd, splt->contents, OR1K_JR(15),
+                               plt, plt_insn_count);
 
          elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
        }
@@ -2389,7 +2420,8 @@ or1k_elf_finish_dynamic_symbol (bfd *output_bfd,
 
   if (h->plt.offset != (bfd_vma) -1)
     {
-      unsigned int plt0, plt1, plt2;
+      unsigned int plt[PLT_MAX_INSN_COUNT];
+      size_t plt_insn_count = 3;
       asection *splt;
       asection *sgot;
       asection *srela;
@@ -2401,6 +2433,7 @@ or1k_elf_finish_dynamic_symbol (bfd *output_bfd,
       bfd_vma got_offset;
       bfd_vma got_addr;
       Elf_Internal_Rela rela;
+      bool large_plt_entry;
 
       /* This symbol has an entry in the procedure linkage table.  Set
         it up.  */
@@ -2418,10 +2451,13 @@ or1k_elf_finish_dynamic_symbol (bfd *output_bfd,
         corresponds to this symbol.  This is the index of this symbol
         in all the symbols for which we are making plt entries.  The
         first entry in the procedure linkage table is reserved.  */
-      plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
+      plt_index = ((struct elf_or1k_link_hash_entry *) h)->plt_index;
       plt_addr = plt_base_addr + h->plt.offset;
       plt_reloc = plt_index * sizeof (Elf32_External_Rela);
 
+      large_plt_entry = (elf_or1k_plt_entry_size (plt_index)
+                        == PLT_ENTRY_SIZE_LARGE);
+
       /* Get the offset into the .got table of the entry that
        corresponds to this function.  Each .got entry is 4 bytes.
        The first three are reserved.  */
@@ -2433,27 +2469,57 @@ or1k_elf_finish_dynamic_symbol (bfd *output_bfd,
        {
          unsigned pa = ((got_addr >> 13) - (plt_addr >> 13)) & 0x1fffff;
          unsigned po = (got_addr & 0x1fff);
-         plt0 = OR1K_ADRP(12) | pa;
-         plt1 = OR1K_LWZ(12,12) | po;
-         plt2 = OR1K_ORI0(11) | plt_reloc;
+         plt[0] = OR1K_ADRP(12) | pa;
+         plt[1] = OR1K_LWZ(12,12) | po;
+         plt[2] = OR1K_ORI0(11) | plt_reloc;
        }
       else if (bfd_link_pic (info))
        {
-         plt0 = OR1K_LWZ(12,16) | got_offset;
-         plt1 = OR1K_ORI0(11) | plt_reloc;
-         plt2 = OR1K_NOP;
+         if (large_plt_entry)
+           {
+             unsigned gotha = ((got_offset + 0x8000) >> 16) & 0xffff;
+             unsigned got = got_offset & 0xffff;
+             unsigned pltrelhi = (plt_reloc >> 16) & 0xffff;
+             unsigned pltrello = plt_reloc & 0xffff;
+
+             plt[0] = OR1K_MOVHI(12) | gotha;
+             plt[1] = OR1K_ADD(12,12,16);
+             plt[2] = OR1K_LWZ(12,12) | got;
+             plt[3] = OR1K_MOVHI(11) | pltrelhi;
+             plt[4] = OR1K_ORI(11,11) | pltrello;
+             plt_insn_count = 5;
+           }
+         else
+           {
+             plt[0] = OR1K_LWZ(12,16) | got_offset;
+             plt[1] = OR1K_ORI0(11) | plt_reloc;
+             plt[2] = OR1K_NOP;
+           }
        }
       else
        {
          unsigned ha = ((got_addr + 0x8000) >> 16) & 0xffff;
          unsigned lo = got_addr & 0xffff;
-         plt0 = OR1K_MOVHI(12) | ha;
-         plt1 = OR1K_LWZ(12,12) | lo;
-         plt2 = OR1K_ORI0(11) | plt_reloc;
+         plt[0] = OR1K_MOVHI(12) | ha;
+         plt[1] = OR1K_LWZ(12,12) | lo;
+         plt[2] = OR1K_ORI0(11) | plt_reloc;
+       }
+
+      /* For large code model we fixup the non-PIC PLT relocation instructions
+        here.  */
+      if (large_plt_entry && !bfd_link_pic (info))
+       {
+         unsigned pltrelhi = (plt_reloc >> 16) & 0xffff;
+         unsigned pltrello = plt_reloc & 0xffff;
+
+         plt[2] = OR1K_MOVHI(11) | pltrelhi;
+         plt[3] = OR1K_ORI(11,11) | pltrello;
+         plt[4] = OR1K_NOP;
+         plt_insn_count = 5;
        }
 
       or1k_write_plt_entry (output_bfd, splt->contents + h->plt.offset,
-                           plt0, plt1, plt2, OR1K_JR(12));
+                           OR1K_JR(12), plt, plt_insn_count);
 
       /* Fill in the entry in the global offset table.  We initialize it to
         point to the top of the plt.  This is done to lazy lookup the actual
@@ -2777,11 +2843,16 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
        {
          asection *splt = htab->root.splt;
+         bfd_vma plt_index;
+
+         /* Track the index of our plt entry for use in calculating size.  */
+         plt_index = htab->plt_count++;
+         ((struct elf_or1k_link_hash_entry *) h)->plt_index = plt_index;
 
          /* If this is the first .plt entry, make room for the special
             first entry.  */
          if (splt->size == 0)
-           splt->size = PLT_ENTRY_SIZE;
+           splt->size = elf_or1k_plt_entry_size (plt_index);
 
          h->plt.offset = splt->size;
 
@@ -2798,7 +2869,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
            }
 
          /* Make room for this entry.  */
-         splt->size += PLT_ENTRY_SIZE;
+         splt->size += elf_or1k_plt_entry_size (plt_index);
 
          /* We also need to make an entry in the .got.plt section, which
             will be placed in the .got section by the linker script.  */
index 1ddf61d1bd39efe7cebfd4ee5ac83c2359d08872..6f6acdfb58a051150d12d06736557a335a4c5ca7 100644 (file)
@@ -1,3 +1,15 @@
+2021-05-06  Stafford Horne  <shorne@gmail.com>
+
+       PR 27746
+       testsuite/ld-or1k/or1k.exp (or1kplttests): Add tests for linking
+       along with gotha() relocations.
+       testsuite/ld-or1k/gotha1.dd: New file.
+       testsuite/ld-or1k/gotha1.s: New file.
+       testsuite/ld-or1k/gotha2.dd: New file.
+       testsuite/ld-or1k/gotha2.s: New file
+       testsuite/ld-or1k/pltlib.s (x): Define size to avoid link
+       failure.
+
 2021-05-06  Nick Alcock  <nick.alcock@oracle.com>
 
        * testsuite/ld-ctf/ctf.exp: Use -gctf, not -gt.
diff --git a/ld/testsuite/ld-or1k/gotha1.dd b/ld/testsuite/ld-or1k/gotha1.dd
new file mode 100644 (file)
index 0000000..0ad1f8f
--- /dev/null
@@ -0,0 +1,34 @@
+
+.*\.x:     file format elf32-or1k
+
+
+Disassembly of section \.plt:
+
+[0-9a-f]+ <\.plt>:
+ +[0-9a-f]+:   19 80 00 00     l\.movhi r12,0x0
+ +[0-9a-f]+:   85 ec [0-9a-f]+ [0-9a-f]+       l\.lwz r15,[0-9]+\(r12\)
+ +[0-9a-f]+:   44 00 78 00     l\.jr r15
+ +[0-9a-f]+:   85 8c [0-9a-f]+ [0-9a-f]+       l\.lwz r12,[0-9]+\(r12\)
+ +[0-9a-f]+:   19 80 00 00     l\.movhi r12,0x0
+ +[0-9a-f]+:   85 8c [0-9a-f]+ [0-9a-f]+       l\.lwz r12,[0-9]+\(r12\)
+ +[0-9a-f]+:   44 00 60 00     l\.jr r12
+ +[0-9a-f]+:   a9 60 00 00     l\.ori r11,r0,0x0
+
+Disassembly of section \.text:
+
+[0-9a-f]+ <_start>:
+ +[0-9a-f]+:   9c 21 ff fc     l\.addi r1,r1,-4
+ +[0-9a-f]+:   d4 01 48 00     l\.sw 0\(r1\),r9
+ +[0-9a-f]+:   04 00 00 02     l\.jal [0-9a-f]+ <_start\+0x10>
+ +[0-9a-f]+:   1a 60 00 00     l\.movhi r19,0x0
+ +[0-9a-f]+:   aa 73 [0-9a-f]+ [0-9a-f]+       l\.ori r19,r19,0x[0-9a-f]+
+ +[0-9a-f]+:   e2 73 48 00     l\.add r19,r19,r9
+ +[0-9a-f]+:   1a 20 00 00     l\.movhi r17,0x0
+ +[0-9a-f]+:   e2 31 98 00     l\.add r17,r17,r19
+ +[0-9a-f]+:   86 31 00 10     l\.lwz r17,16\(r17\)
+ +[0-9a-f]+:   84 71 00 00     l\.lwz r3,0\(r17\)
+ +[0-9a-f]+:   07 ff ff f2     l\.jal [0-9a-f]+ <\.plt\+0x10>
+ +[0-9a-f]+:   15 00 00 00     l\.nop 0x0
+ +[0-9a-f]+:   85 21 00 00     l\.lwz r9,0\(r1\)
+ +[0-9a-f]+:   44 00 48 00     l\.jr r9
+ +[0-9a-f]+:   9c 21 00 04     l\.addi r1,r1,4
diff --git a/ld/testsuite/ld-or1k/gotha1.s b/ld/testsuite/ld-or1k/gotha1.s
new file mode 100644 (file)
index 0000000..42b16db
--- /dev/null
@@ -0,0 +1,24 @@
+       .data
+       .p2align 16
+
+       .text
+       .globl  _start
+_start:
+       l.addi  r1, r1, -4
+       l.sw    0(r1), r9
+
+       l.jal   8
+        l.movhi        r19, gotpchi(_GLOBAL_OFFSET_TABLE_-4)
+       l.ori   r19, r19, gotpclo(_GLOBAL_OFFSET_TABLE_+0)
+       l.add   r19, r19, r9
+
+       l.movhi r17, gotha(x)
+       l.add   r17, r17, r19
+       l.lwz   r17, got(x)(r17)
+       l.lwz   r3, 0(r17)
+
+       l.jal   plt(func)
+        l.nop
+       l.lwz   r9, 0(r1)
+       l.jr    r9
+        l.addi r1, r1, 4
diff --git a/ld/testsuite/ld-or1k/gotha2.dd b/ld/testsuite/ld-or1k/gotha2.dd
new file mode 100644 (file)
index 0000000..fe09da5
--- /dev/null
@@ -0,0 +1,21 @@
+
+.*\.x:     file format elf32-or1k
+
+
+Disassembly of section \.text:
+
+[0-9a-f]+ <test>:
+ +[0-9a-f]+:   9c 21 ff f8     l\.addi r1,r1,-8
+ +[0-9a-f]+:   d4 01 80 00     l\.sw 0\(r1\),r16
+ +[0-9a-f]+:   d4 01 48 04     l\.sw 4\(r1\),r9
+ +[0-9a-f]+:   04 00 [0-9a-f]+ [0-9a-f]+       l\.jal [0-9a-f]+ <test\+0x14>
+ +[0-9a-f]+:   1a 00 00 00     l\.movhi r16,0x0
+ +[0-9a-f]+:   aa 10 [0-9a-f]+ [0-9a-f]+       l\.ori r16,r16,0x[0-9a-f]+
+ +[0-9a-f]+:   e2 10 48 00     l\.add r16,r16,r9
+ +[0-9a-f]+:   1a 20 00 00     l\.movhi r17,0x0
+ +[0-9a-f]+:   e2 31 80 00     l\.add r17,r17,r16
+ +[0-9a-f]+:   86 31 00 0c     l\.lwz r17,12\(r17\)
+ +[0-9a-f]+:   85 21 00 04     l\.lwz r9,4\(r1\)
+ +[0-9a-f]+:   86 01 00 00     l\.lwz r16,0\(r1\)
+ +[0-9a-f]+:   44 00 48 00     l\.jr r9
+ +[0-9a-f]+:   9c 21 00 08     l\.addi r1,r1,8
diff --git a/ld/testsuite/ld-or1k/gotha2.s b/ld/testsuite/ld-or1k/gotha2.s
new file mode 100644 (file)
index 0000000..164b282
--- /dev/null
@@ -0,0 +1,22 @@
+       .section        .text
+       .align 4
+       .global test
+       .type   test, @function
+test:
+       l.addi  r1, r1, -8
+       l.sw    0(r1), r16
+       l.sw    4(r1), r9
+
+       l.jal   8
+        l.movhi        r16, gotpchi(_GLOBAL_OFFSET_TABLE_-4)
+       l.ori   r16, r16, gotpclo(_GLOBAL_OFFSET_TABLE_+0)
+       l.add   r16, r16, r9
+
+       l.movhi r17, gotha(i)
+       l.add   r17, r17, r16
+       l.lwz   r17, got(i)(r17)
+
+       l.lwz   r9, 4(r1)
+       l.lwz   r16, 0(r1)
+       l.jr    r9
+        l.addi r1, r1, 8
index 8bb943aacc98a3217a7621dfde5fb84c430e087f..f10949e89aae1e18174f8605bf15b7116580111a 100644 (file)
@@ -53,6 +53,14 @@ set or1kplttests {
      "" {plt1.s}
      {{objdump -dr plt1.x.dd}}
      "plt1.x"}
+    {"gotha exec plt" "tmpdir/libpltlib.so" ""
+     "" {gotha1.s}
+     {{objdump -dr gotha1.dd}}
+     "gotha1.x"}
+    {"gotha -fpic -shared" "-fpic -shared" ""
+     "" {gotha2.s}
+     {{objdump -dr gotha2.dd}}
+     "gotha2.x"}
 }
 
 # Not implemented yet
index baf76ca1af77192db5682d55b1d22cd8363b82f9..8b4d7ba48fddd7faf24c9f68f37f78df73267c64 100644 (file)
@@ -1,5 +1,6 @@
        .section .data
        .globl x, y
+       .size x, 4
 x:     .long 33
 y:     .long 44