Referencing a function's address on PowerPC64 ELFv2
authorAlan Modra <amodra@gmail.com>
Tue, 25 Mar 2014 04:42:48 +0000 (15:12 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 26 Mar 2014 14:19:38 +0000 (00:49 +1030)
ELFv2 needs to create plt entries in a non-PIC executable for an
address reference to a function defined in a shared object.  It's
possible that an object file has no features that distinguish it as
ELFv1 or ELFv2, eg. an object only containing data.  Such files need
to be handled like those that are known to be ELFv2.
However, this unnecessarily creates plt entries for the analogous
ELFv1 case, so arrange to set output abi version earlier, and use the
output abi version to further distinguish ambiguous input files.

bfd/
* elf64-ppc.c (ppc64_elf_check_relocs): Account for possibly
needed plt entries when taking the address of functions for
abiversion == 0 (ie. unknown) as well as abiversion == 2.
Move opd setup and abiversion checks to..
(ppc64_elf_before_check_relocs): ..here.  Renamed from
ppc64_elf_process_dot_syms.  Set output abiversion from input and
input abiversion from output, if either is not set.
(ppc64_elf_merge_private_bfd_data): Don't merge flags here.
(elf_backend_check_directives): Update.
ld/testsuite/
* ld-powerpc/startv1.s, * ld-powerpc/startv2.s, * ld-powerpc/funref.s,
* ld-powerpc/funv1.s, * ld-powerpc/funv2.s,
* ld-powerpc/ambiguousv1.d, * ld-powerpc/ambiguousv2.d: New test files.
* ld-powerpc/powerpc.exp: Run new tests.

bfd/ChangeLog
bfd/elf64-ppc.c
ld/testsuite/ChangeLog
ld/testsuite/ld-powerpc/ambiguousv1.d [new file with mode: 0644]
ld/testsuite/ld-powerpc/ambiguousv2.d [new file with mode: 0644]
ld/testsuite/ld-powerpc/funref.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/funv1.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/funv2.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/powerpc.exp
ld/testsuite/ld-powerpc/startv1.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/startv2.s [new file with mode: 0644]

index c719d24140cbcc8be42239013e18a81d16f5a982..01935536abfc03d3fb7798ef38409976beedcb44 100644 (file)
@@ -1,3 +1,15 @@
+2014-03-26  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (ppc64_elf_check_relocs): Account for possibly
+       needed plt entries when taking the address of functions for
+       abiversion == 0 (ie. unknown) as well as abiversion == 2.
+       Move opd setup and abiversion checks to..
+       (ppc64_elf_before_check_relocs): ..here.  Renamed from
+       ppc64_elf_process_dot_syms.  Set output abiversion from input and
+       input abiversion from output, if either is not set.
+       (ppc64_elf_merge_private_bfd_data): Don't merge flags here.
+       (elf_backend_check_directives): Update.
+
 2014-03-25  Will Newton  <will.newton@linaro.org>
 
         * elfnn-aarch64.c (elfNN_aarch64_finish_dynamic_sections):
index 74c6e25e54e1aec2f50b5c914da70a61c2a3b4b2..8d965f04d68a8cda9e387132e23d45b062b13751 100644 (file)
@@ -95,7 +95,7 @@ static bfd_vma opd_entry_value
 #define elf_backend_create_dynamic_sections   ppc64_elf_create_dynamic_sections
 #define elf_backend_copy_indirect_symbol      ppc64_elf_copy_indirect_symbol
 #define elf_backend_add_symbol_hook          ppc64_elf_add_symbol_hook
-#define elf_backend_check_directives         ppc64_elf_process_dot_syms
+#define elf_backend_check_directives         ppc64_elf_before_check_relocs
 #define elf_backend_notice_as_needed         ppc64_elf_notice_as_needed
 #define elf_backend_archive_symbol_lookup     ppc64_elf_archive_symbol_lookup
 #define elf_backend_check_relocs             ppc64_elf_check_relocs
@@ -3953,7 +3953,7 @@ struct ppc_link_hash_table
   /* Set on error.  */
   unsigned int stub_error:1;
 
-  /* Temp used by ppc64_elf_process_dot_syms.  */
+  /* Temp used by ppc64_elf_before_check_relocs.  */
   unsigned int twiddled_syms:1;
 
   /* Incremented every time we size stubs.  */
@@ -4930,10 +4930,11 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info)
   return TRUE;
 }
 
-/* Process list of dot-symbols we made in link_hash_newfunc.  */
+/* Set up opd section info and abiversion for IBFD, and process list
+   of dot-symbols we made in link_hash_newfunc.  */
 
 static bfd_boolean
-ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info)
+ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
 {
   struct ppc_link_hash_table *htab;
   struct ppc_link_hash_entry **p, *eh;
@@ -4946,6 +4947,57 @@ ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info)
 
   if (is_ppc64_elf (ibfd))
     {
+      asection *opd = bfd_get_section_by_name (ibfd, ".opd");
+
+      if (opd != NULL && opd->size != 0)
+       {
+         if (abiversion (ibfd) == 0)
+           set_abiversion (ibfd, 1);
+         else if (abiversion (ibfd) == 2)
+           {
+             info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
+                                       " version %d\n"),
+                                     ibfd, abiversion (ibfd));
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
+
+         if ((ibfd->flags & DYNAMIC) == 0
+             && (opd->flags & SEC_RELOC) != 0
+             && opd->reloc_count != 0
+             && !bfd_is_abs_section (opd->output_section))
+           {
+             /* Garbage collection needs some extra help with .opd sections.
+                We don't want to necessarily keep everything referenced by
+                relocs in .opd, as that would keep all functions.  Instead,
+                if we reference an .opd symbol (a function descriptor), we
+                want to keep the function code symbol's section.  This is
+                easy for global symbols, but for local syms we need to keep
+                information about the associated function section.  */
+             bfd_size_type amt;
+             asection **opd_sym_map;
+
+             amt = opd->size * sizeof (*opd_sym_map) / 8;
+             opd_sym_map = bfd_zalloc (ibfd, amt);
+             if (opd_sym_map == NULL)
+               return FALSE;
+             ppc64_elf_section_data (opd)->u.opd.func_sec = opd_sym_map;
+             BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
+             ppc64_elf_section_data (opd)->sec_type = sec_opd;
+           }
+       }
+
+      /* For input files without an explicit abiversion in e_flags
+        we should have flagged any with symbol st_other bits set
+        as ELFv1 and above flagged those with .opd as ELFv2.
+        Set the output abiversion if not yet set, and for any input
+        still ambiguous, take its abiversion from the output.
+        Differences in ABI are reported later.  */
+      if (abiversion (info->output_bfd) == 0)
+       set_abiversion (info->output_bfd, abiversion (ibfd));
+      else if (abiversion (ibfd) == 0)
+       set_abiversion (ibfd, abiversion (info->output_bfd));
+
       p = &htab->dot_syms;
       while ((eh = *p) != NULL)
        {
@@ -5149,34 +5201,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   sym_hashes = elf_sym_hashes (abfd);
   sreloc = NULL;
   opd_sym_map = NULL;
-  if (strcmp (sec->name, ".opd") == 0)
-    {
-      /* Garbage collection needs some extra help with .opd sections.
-        We don't want to necessarily keep everything referenced by
-        relocs in .opd, as that would keep all functions.  Instead,
-        if we reference an .opd symbol (a function descriptor), we
-        want to keep the function code symbol's section.  This is
-        easy for global symbols, but for local syms we need to keep
-        information about the associated function section.  */
-      bfd_size_type amt;
-
-      if (abiversion (abfd) == 0)
-       set_abiversion (abfd, 1);
-      else if (abiversion (abfd) == 2)
-       {
-         info->callbacks->einfo (_("%P: .opd not allowed in ABI version %d\n"),
-                                 abiversion (abfd));
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
-      amt = sec->size * sizeof (*opd_sym_map) / 8;
-      opd_sym_map = bfd_zalloc (abfd, amt);
-      if (opd_sym_map == NULL)
-       return FALSE;
-      ppc64_elf_section_data (sec)->u.opd.func_sec = opd_sym_map;
-      BFD_ASSERT (ppc64_elf_section_data (sec)->sec_type == sec_normal);
-      ppc64_elf_section_data (sec)->sec_type = sec_opd;
-    }
+  if (ppc64_elf_section_data (sec) != NULL
+      && ppc64_elf_section_data (sec)->sec_type == sec_opd)
+    opd_sym_map = ppc64_elf_section_data (sec)->u.opd.func_sec;
 
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
@@ -5350,7 +5377,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          /* We may also need a plt entry if the symbol turns out to be
             an ifunc.  */
-         if (h != NULL && !info->shared && abiversion (abfd) == 2)
+         if (h != NULL && !info->shared && abiversion (abfd) != 1)
            {
              if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
                return FALSE;
@@ -5634,7 +5661,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_ADDR16_HIGHESTA:
        case R_PPC64_ADDR16_LO:
        case R_PPC64_ADDR16_LO_DS:
-         if (h != NULL && !info->shared && abiversion (abfd) == 2
+         if (h != NULL && !info->shared && abiversion (abfd) != 1
              && rel->r_addend == 0)
            {
              /* We may need a .plt entry if this reloc refers to a
@@ -5808,21 +5835,14 @@ ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   iflags = elf_elfheader (ibfd)->e_flags;
   oflags = elf_elfheader (obfd)->e_flags;
 
-  if (!elf_flags_init (obfd) || oflags == 0)
-    {
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = iflags;
-    }
-  else if (iflags == oflags || iflags == 0)
-    ;
-  else if (iflags & ~EF_PPC64_ABI)
+  if (iflags & ~EF_PPC64_ABI)
     {
       (*_bfd_error_handler)
        (_("%B uses unknown e_flags 0x%lx"), ibfd, iflags);
       bfd_set_error (bfd_error_bad_value);
       return FALSE;
     }
-  else
+  else if (iflags != oflags && iflags != 0)
     {
       (*_bfd_error_handler)
        (_("%B: ABI version %ld is not compatible with ABI version %ld output"),
index d2002777c35312a52f0adafdb1e4068c949c6923..279958e91226ebb995dc3cf4ce11d8518ba2e553 100644 (file)
@@ -1,3 +1,10 @@
+2014-03-26  Alan Modra  <amodra@gmail.com>
+
+       * ld-powerpc/startv1.s, * ld-powerpc/startv2.s, * ld-powerpc/funref.s,
+       * ld-powerpc/funv1.s, * ld-powerpc/funv2.s,
+       * ld-powerpc/ambiguousv1.d, * ld-powerpc/ambiguousv2.d: New test files.
+       * ld-powerpc/powerpc.exp: Run new tests.
+
 2014-03-25  Will Newton  <will.newton@linaro.org>
 
         * ld-aarch64/aarch64-elf.exp: Add relasz dump test.
diff --git a/ld/testsuite/ld-powerpc/ambiguousv1.d b/ld/testsuite/ld-powerpc/ambiguousv1.d
new file mode 100644 (file)
index 0000000..73beab9
--- /dev/null
@@ -0,0 +1,12 @@
+#source: startv1.s
+#source: funref.s
+#as: -a64
+#ld: -melf64ppc
+#ld_after_inputfiles: tmpdir/funv1.so
+#readelf: -r --wide
+# check that we do the right thing with funref.s that doesn't have
+# anything to mark it as ELFv1 or ELFv2
+
+Relocation section .* contains 1 entries:
+.*
+.* R_PPC64_ADDR64 +0+ my_func \+ 0
diff --git a/ld/testsuite/ld-powerpc/ambiguousv2.d b/ld/testsuite/ld-powerpc/ambiguousv2.d
new file mode 100644 (file)
index 0000000..5cf047b
--- /dev/null
@@ -0,0 +1,12 @@
+#source: startv2.s
+#source: funref.s
+#as: -a64
+#ld: -melf64ppc
+#ld_after_inputfiles: tmpdir/funv2.so
+#readelf: -r --wide
+# check that we do the right thing with funref.s that doesn't have
+# anything to mark it as ELFv1 or ELFv2
+
+Relocation section .*contains 1 entries:
+.*
+.* R_PPC64_JMP_SLOT .* my_func \+ 0
diff --git a/ld/testsuite/ld-powerpc/funref.s b/ld/testsuite/ld-powerpc/funref.s
new file mode 100644 (file)
index 0000000..3f7de47
--- /dev/null
@@ -0,0 +1,4 @@
+ .data
+ .globl func_tab
+func_tab:
+ .dc.a my_func
diff --git a/ld/testsuite/ld-powerpc/funv1.s b/ld/testsuite/ld-powerpc/funv1.s
new file mode 100644 (file)
index 0000000..e79009d
--- /dev/null
@@ -0,0 +1,10 @@
+ .globl my_func
+ .type my_func,@function
+ .section .opd,"aw",@progbits
+my_func:
+ .quad .Lmy_func, .TOC.@tocbase
+
+ .text
+.Lmy_func:
+ blr
+ .size my_func,.-.Lmy_func
diff --git a/ld/testsuite/ld-powerpc/funv2.s b/ld/testsuite/ld-powerpc/funv2.s
new file mode 100644 (file)
index 0000000..eaff0b3
--- /dev/null
@@ -0,0 +1,6 @@
+ .abiversion 2
+ .globl my_func
+ .type my_func,@function
+my_func:
+ blr
+ .size my_func,.-my_func
index 8a60eb7e2cf43e1444a58a7bd55fdcd32fca34c1..1eaedaaf1638b8fd1429794447c1296422803c14 100644 (file)
@@ -212,6 +212,8 @@ set ppc64elftests {
         {tocopt4a.s tocopt4b.s} {{objdump -s tocopt4.d}} "tocopt4"}
     {"TOC opt5" "-melf64ppc" "" "-a64"  {tocopt5.s}
        {{objdump -s tocopt5.d}} "tocopt5"}
+    {"ambig shared v1" "-shared -melf64ppc" "" "-a64" {funv1.s} {} "funv1.so"}
+    {"ambig shared v2" "-shared -melf64ppc" "" "-a64" {funv2.s} {} "funv2.so"}
 }
 
 set ppceabitests {
@@ -272,6 +274,8 @@ if [ supports_ppc64 ] then {
     run_dump_test "elfv2exe"
     run_dump_test "elfv2-2so"
     run_dump_test "elfv2-2exe"
+    run_dump_test "ambiguousv1"
+    run_dump_test "ambiguousv2"
 }
 
 if { [istarget "powerpc*-eabi*"] } {
diff --git a/ld/testsuite/ld-powerpc/startv1.s b/ld/testsuite/ld-powerpc/startv1.s
new file mode 100644 (file)
index 0000000..c54e1b0
--- /dev/null
@@ -0,0 +1,8 @@
+ .globl _start
+ .section .opd,"aw",@progbits
+_start:
+ .quad .L_start, .TOC.@tocbase
+
+ .text
+.L_start:
+ b _start
diff --git a/ld/testsuite/ld-powerpc/startv2.s b/ld/testsuite/ld-powerpc/startv2.s
new file mode 100644 (file)
index 0000000..7187aa5
--- /dev/null
@@ -0,0 +1,5 @@
+ .abiversion 2
+ .text
+ .globl _start
+_start:
+ b _start