ld: add hidden and internal visibility support for XCOFF
authorClément Chigot <clement.chigot@atos.net>
Tue, 23 Nov 2021 14:45:28 +0000 (15:45 +0100)
committerClément Chigot <clement.chigot@atos.net>
Wed, 12 Jan 2022 08:08:25 +0000 (09:08 +0100)
This patch adds a primary support for hidden and internal visibility in
GNU linker for XCOFF format.
The protected visibility isn't yet supported.

PR 22085

bfd/ChangeLog:

* xcofflink.c (xcoff_dynamic_definition_p): Add hidden
  and internal visibility support.
(xcoff_link_add_symbols): Likewise.
(xcoff_auto_export_p): Likewise.
(bfd_xcoff_export_symbol): Likewise.
(xcoff_link_input_bfd): Likewise.

ld/ChangeLog:

* testsuite/ld-vsb/main.c: Adapt for XCOFF.
* testsuite/ld-vsb/sh1.c: Likewse.
* testsuite/ld-vsb/vsb.exp: Likewise.
* testsuite/ld-vsb/visibility-1-xcoff-32.d: New test.
* testsuite/ld-vsb/visibility-1-xcoff-64.d: New test.
* testsuite/ld-vsb/visibility-2-xcoff-32.d: New test.
* testsuite/ld-vsb/visibility-2-xcoff-64.d: New test.
* testsuite/ld-vsb/xcoffvsb.dat: New test.

bfd/xcofflink.c
ld/testsuite/ld-vsb/define.s
ld/testsuite/ld-vsb/main.c
ld/testsuite/ld-vsb/sh1.c
ld/testsuite/ld-vsb/visibility-1-xcoff-32.d [new file with mode: 0644]
ld/testsuite/ld-vsb/visibility-1-xcoff-64.d [new file with mode: 0644]
ld/testsuite/ld-vsb/visibility-2-xcoff-32.d [new file with mode: 0644]
ld/testsuite/ld-vsb/visibility-2-xcoff-64.d [new file with mode: 0644]
ld/testsuite/ld-vsb/vsb.exp
ld/testsuite/ld-vsb/xcoffvsb.dat [new file with mode: 0644]

index ba07ba7f04546c0bce16441baf3c09634e202cf7..6e994720f6dfcff1ce567aa7b1e6eec97b8651fd 100644 (file)
@@ -798,10 +798,14 @@ xcoff_dynamic_definition_p (struct xcoff_link_hash_entry *h,
          || h->root.type == bfd_link_hash_undefweak))
     return true;
 
-  /* If H is currently undefined, LDSYM defines it.  */
+  /* If H is currently undefined, LDSYM defines it.
+     However, if H has a hidden visibility, LDSYM must not
+     define it.  */
   if ((h->flags & XCOFF_DEF_DYNAMIC) == 0
       && (h->root.type == bfd_link_hash_undefined
-         || h->root.type == bfd_link_hash_undefweak))
+         || h->root.type == bfd_link_hash_undefweak)
+      && (h->visibility != SYM_V_HIDDEN
+         && h->visibility != SYM_V_INTERNAL))
     return true;
 
   return false;
@@ -1243,6 +1247,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
     bfd_byte *linenos;
   } *reloc_info = NULL;
   bfd_size_type amt;
+  unsigned short visibility;
 
   keep_syms = obj_coff_keep_syms (abfd);
 
@@ -1480,6 +1485,9 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
            }
        }
 
+      /* Record visibility.  */
+      visibility = sym.n_type & SYM_V_MASK;
+
       /* Pick up the csect auxiliary information.  */
       if (sym.n_numaux == 0)
        {
@@ -2058,6 +2066,22 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
                  /* Try not to give this error too many times.  */
                  (*sym_hash)->flags &= ~XCOFF_MULTIPLY_DEFINED;
                }
+
+
+             /* If the symbol is hidden or internal, completely undo
+                any dynamic link state.  */
+             if ((*sym_hash)->flags & XCOFF_DEF_DYNAMIC
+                 && (visibility == SYM_V_HIDDEN
+                     || visibility == SYM_V_INTERNAL))
+                 (*sym_hash)->flags &= ~XCOFF_DEF_DYNAMIC;
+             else
+               {
+                 /* Keep the most constraining visibility.  */
+                 unsigned short hvis = (*sym_hash)->visibility;
+                 if (visibility && ( !hvis || visibility < hvis))
+                   (*sym_hash)->visibility = visibility;
+               }
+
            }
 
          /* _bfd_generic_link_add_one_symbol may call the linker to
@@ -2650,6 +2674,11 @@ xcoff_auto_export_p (struct bfd_link_info *info,
   if (h->root.root.string[0] == '.')
     return false;
 
+  /* Don't export hidden or internal symbols.  */
+  if (h->visibility == SYM_V_HIDDEN
+      || h->visibility == SYM_V_INTERNAL)
+    return false;
+
   /* We don't export a symbol which is being defined by an object
      included from an archive which contains a shared object.  The
      rationale is that if an archive contains both an unshared and
@@ -3248,6 +3277,19 @@ bfd_xcoff_export_symbol (bfd *output_bfd,
   if (bfd_get_flavour (output_bfd) != bfd_target_xcoff_flavour)
     return true;
 
+  /* As AIX linker, symbols exported with hidden visibility are
+     silently ignored.  */
+  if (h->visibility == SYM_V_HIDDEN)
+    return true;
+
+  if (h->visibility == SYM_V_INTERNAL)
+    {
+      _bfd_error_handler (_("%pB: cannot export internal symbol `%s`."),
+                         output_bfd, h->root.root.string);
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
   h->flags |= XCOFF_EXPORT;
 
   /* FIXME: I'm not at all sure what syscall is supposed to mean, so
@@ -4572,6 +4614,10 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *flinfo,
                               - (*csectpp)->vma);
            }
 
+         /* Update visibility.  */
+         isym.n_type &= ~SYM_V_MASK;
+         isym.n_type |= (*sym_hash)->visibility;
+
          /* Output the symbol.  */
          bfd_coff_swap_sym_out (output_bfd, (void *) &isym, (void *) outsym);
 
index b38e3e0c05387b284ab7f77c2c48121bf2eefa7f..3dc25ed718519570cad73d39884275d3d33de3fa 100644 (file)
@@ -1,10 +1,16 @@
        .data
        .globl protected
+       .ifndef XCOFF_TEST
        .type protected,"object"
+       .endif
 protected:
        .globl hidden
+       .ifndef XCOFF_TEST
        .type hidden,"object"
+       .endif
 hidden:
        .globl internal
+       .ifndef XCOFF_TEST
        .type internal,"object"
+       .endif
 internal:
index c2c92911344f7156926eedf0e4eeba54eb0138c9..b0359c0d815f6fb3aa9c199e444ac35e637c1ca2 100644 (file)
@@ -236,8 +236,8 @@ main (void)
   printf ("mainvar == %d\n", mainvar);
   printf ("overriddenvar == %d\n", overriddenvar);
   printf ("shlibvar1 == %d\n", shlibvar1);
-#ifndef XCOFF_TEST
   printf ("shlib_mainvar () == %d\n", shlib_mainvar ());
+#ifndef XCOFF_TEST
   printf ("shlib_overriddenvar () == %d\n", shlib_overriddenvar ());
 #endif
   printf ("shlib_shlibvar1 () == %d\n", shlib_shlibvar1 ());
@@ -245,15 +245,13 @@ main (void)
   printf ("shlib_shlibcall () == %d\n", shlib_shlibcall ());
 #ifndef XCOFF_TEST
   printf ("shlib_shlibcall2 () == %d\n", shlib_shlibcall2 ());
-  printf ("shlib_maincall () == %d\n", shlib_maincall ());
 #endif
+  printf ("shlib_maincall () == %d\n", shlib_maincall ());
   printf ("main_called () == %d\n", main_called ());
   printf ("shlib_checkfunptr1 (shlib_shlibvar1) == %d\n",
          shlib_checkfunptr1 (shlib_shlibvar1));
-#ifndef XCOFF_TEST
   printf ("shlib_checkfunptr2 (main_called) == %d\n",
          shlib_checkfunptr2 (main_called));
-#endif
   p = shlib_getfunptr1 ();
   printf ("shlib_getfunptr1 () ");
   if (p == shlib_shlibvar1)
@@ -261,7 +259,6 @@ main (void)
   else
     printf ("!=");
   printf (" shlib_shlibvar1\n");
-#ifndef XCOFF_TEST
   p = shlib_getfunptr2 ();
   printf ("shlib_getfunptr2 () ");
   if (p == main_called)
@@ -269,7 +266,6 @@ main (void)
   else
     printf ("!=");
   printf (" main_called\n");
-#endif
   printf ("shlib_check () == %d\n", shlib_check ());
   printf ("visibility_check () == %d\n", visibility_check ());
   printf ("visibility_checkfunptr () == %d\n",
index 1aba63c75412fc94dc240494f6e0e89d6b79fd33..193eca2943bbd9034b963421a7917890714da2e1 100644 (file)
@@ -6,9 +6,7 @@
    of a shared library.  */
 
 /* This variable is supplied by the main program.  */
-#ifndef XCOFF_TEST
 extern int mainvar;
-#endif
 
 /* This variable is defined in the shared library, and overridden by
    the main program.  */
@@ -33,13 +31,11 @@ extern int shlibvar2;
 /* These functions return the values of the above variables as seen in
    the shared library.  */
 
-#ifndef XCOFF_TEST
 int
 shlib_mainvar ()
 {
   return mainvar;
 }
-#endif
 
 #ifndef XCOFF_TEST
 int
@@ -95,7 +91,6 @@ shlib_overriddencall2 ()
 
 /* This function calls a function defined by the main program.  */
 
-#ifndef XCOFF_TEST
 extern int main_called ();
 
 int
@@ -103,7 +98,6 @@ shlib_maincall ()
 {
   return main_called ();
 }
-#endif
 
 /* This function is passed a function pointer to shlib_mainvar.  It
    confirms that the pointer compares equally.  */
@@ -118,14 +112,12 @@ shlib_checkfunptr1 (p)
 /* This function is passed a function pointer to main_called.  It
    confirms that the pointer compares equally.  */
 
-#ifndef XCOFF_TEST
 int
 shlib_checkfunptr2 (p)
      int (*p) ();
 {
   return p == main_called;
 }
-#endif
 
 /* This function returns a pointer to shlib_mainvar.  */
 
@@ -137,13 +129,11 @@ int
 
 /* This function returns a pointer to main_called.  */
 
-#ifndef XCOFF_TEST
 int
 (*shlib_getfunptr2 ()) ()
 {
   return main_called;
 }
-#endif
 
 /* This function makes sure that constant data and local functions
    work.  */
diff --git a/ld/testsuite/ld-vsb/visibility-1-xcoff-32.d b/ld/testsuite/ld-vsb/visibility-1-xcoff-32.d
new file mode 100644 (file)
index 0000000..19cff64
--- /dev/null
@@ -0,0 +1,21 @@
+#source: define.s
+#source: undef.s
+#as: -a32 --defsym XCOFF_TEST=1
+#ld: -b32 -r
+#objdump: -t
+
+.*
+
+SYMBOL TABLE:
+.*
+.*
+.*
+.*
+\[  4\]\(sec  2\).*\(ty 3000\).*protected
+.*
+\[  6\]\(sec  2\).*\(ty 2000\).*hidden
+.*
+\[  8\]\(sec  2\).*\(ty 1000\).*internal
+.*
+.*
+.*
diff --git a/ld/testsuite/ld-vsb/visibility-1-xcoff-64.d b/ld/testsuite/ld-vsb/visibility-1-xcoff-64.d
new file mode 100644 (file)
index 0000000..a63dcf0
--- /dev/null
@@ -0,0 +1,21 @@
+#source: define.s
+#source: undef.s
+#as: -a64 --defsym XCOFF_TEST=1
+#ld: -b64 -r
+#objdump: -t
+
+.*
+
+SYMBOL TABLE:
+.*
+.*
+.*
+.*
+\[  4\]\(sec  2\).*\(ty 3000\).*protected
+.*
+\[  6\]\(sec  2\).*\(ty 2000\).*hidden
+.*
+\[  8\]\(sec  2\).*\(ty 1000\).*internal
+.*
+.*
+.*
diff --git a/ld/testsuite/ld-vsb/visibility-2-xcoff-32.d b/ld/testsuite/ld-vsb/visibility-2-xcoff-32.d
new file mode 100644 (file)
index 0000000..54beedd
--- /dev/null
@@ -0,0 +1,16 @@
+#source: undef.s
+#as: -a32 --defsym XCOFF_TEST=1
+#ld: -b32 -r
+#objdump: -t
+
+.*
+
+SYMBOL TABLE:
+.*
+.*
+\[  2\]\(sec  0\).*\(ty 3000\).*protected
+.*
+\[  4\]\(sec  0\).*\(ty 2000\).*hidden
+.*
+\[  6\]\(sec  0\).*\(ty 1000\).*internal
+.*
diff --git a/ld/testsuite/ld-vsb/visibility-2-xcoff-64.d b/ld/testsuite/ld-vsb/visibility-2-xcoff-64.d
new file mode 100644 (file)
index 0000000..b607a53
--- /dev/null
@@ -0,0 +1,16 @@
+#source: undef.s
+#as: -a64 --defsym XCOFF_TEST=1
+#ld: -b64 -r
+#objdump: -t
+
+.*
+
+SYMBOL TABLE:
+.*
+.*
+\[  2\]\(sec  0\).*\(ty 3000\).*protected
+.*
+\[  4\]\(sec  0\).*\(ty 2000\).*hidden
+.*
+\[  6\]\(sec  0\).*\(ty 1000\).*internal
+.*
index 52a7d6b5f7430b0a48a9f416cc585c47a5180d62..bc2a9b8a836f08a4098d093e67e5416f26447a92 100644 (file)
@@ -29,7 +29,8 @@ if { ![check_compiler_available] } {
     return
 }
 
-# This test can only be run on a couple of ELF platforms.
+# This test can only be run on a couple of ELF platforms or with
+# XCOFF formats.
 # Square bracket expressions seem to confuse istarget.
 if { ![istarget hppa*64*-*-hpux*] \
      && ![istarget hppa*-*-linux*] \
@@ -45,11 +46,17 @@ if { ![istarget hppa*64*-*-hpux*] \
      && ![istarget sparc*-*-linux*] \
      && ![istarget s390*-*-linux*] \
      && ![istarget sh\[34\]*-*-linux*] \
-     && ![istarget x86_64-*-linux*] } {
+     && ![istarget x86_64-*-linux*] \
+     && ![is_xcoff_format] } {
     return
 }
 
-set test_list [lsort [glob -nocomplain $srcdir/$subdir/*-elf.d]]
+if [is_xcoff_format] {
+    set test_list [lsort [glob -nocomplain $srcdir/$subdir/*-xcoff*.d]]
+} else {
+    set test_list [lsort [glob -nocomplain $srcdir/$subdir/*-elf.d]]
+}
+
 foreach t $test_list {
     # We need to strip the ".d", but can leave the dirname.
     verbose [file rootname $t]
@@ -61,6 +68,28 @@ set SHCFLAG ""
 set shared_needs_pic "no"
 set COMPRESS_LDFLAG "-Wl,--compress-debug-sections=zlib-gabi"
 
+if { [is_xcoff_format] } {
+    # Not all the useful features are available with AIX shared
+    # libraries by default.
+    # We can manage to simulate some of them with export/import
+    # files but the overriding of shared library functions or
+    # variables by the main program doesn't seem possible.
+    # We avoid testing those features.
+    set SHCFLAG "-DXCOFF_TEST"
+
+    # In order to avoid listing every symbols in an export file,
+    # the export will be done with -bexpall flag.
+    # However for imports, we must create the import file.
+    set file [open $tmpdir/xcoff-shared.imp w]
+    puts $file "#! ."
+    puts $file mainvar
+    puts $file main_called
+    close $file
+
+    # XCOFF doesn't yet support debug sections compresion.
+    set COMPRESS_LDFLAG ""
+}
+
 if [istarget arm*-*-linux*] {
     # On ARM section anchors can change the symbol pre-emptability for
     # non-PIC shared libraries, causing these tests to fail.  Turn section
@@ -108,6 +137,10 @@ proc visibility_test { visibility progname testname main sh1 sh2 dat args } {
 
     # Build the shared library.
     set shared -shared
+    if { [is_xcoff_format] } {
+       # On AIX, setup imports and exports.
+       append shared " -Wl,-bexpall -Wl,-bI:$tmpdir/xcoff-shared.imp"
+    }
     if { [is_elf_format] && [check_shared_lib_support] } {
        append shared " -Wl,-z,notext"
     }
@@ -128,8 +161,15 @@ proc visibility_test { visibility progname testname main sh1 sh2 dat args } {
 
     # Link against the shared library.  Use -rpath so that the
     # dynamic linker can locate the shared library at runtime.
+    # On AIX, we must include /lib in -rpath, as otherwise the loader
+    # can not find -lc.
     set rpath $tmpdir
-    if ![ld_link $CC_FOR_TARGET $tmpdir/$progname "-Wl,-rpath,$rpath $tmpdir/$main $tmpdir/$progname.so"] {
+    set exportflag ""
+    if { [is_xcoff_format] } {
+       set rpath /lib:$tmpdir
+       set exportflag " -Wl,-bexpall"
+    }
+    if ![ld_link $CC_FOR_TARGET $tmpdir/$progname "-Wl,-rpath,$rpath $tmpdir/$main $tmpdir/$progname.so $exportflag"] {
        if { [ string match $visibility "hidden" ]
             && [regexp "undefined reference to \`\.?visibility\'" $link_output]
             && [regexp "undefined reference to \`visibility_var\'" $link_output] } {
@@ -225,6 +265,14 @@ proc visibility_run {visibility} {
     # tests below.
     remote_file host delete $tmpdir/sh1p.o $tmpdir/sh2p.o $tmpdir/sh1np.o $tmpdir/sh2np.o
 
+    set datfile elfvsb
+    if { [is_xcoff_format] } {
+       # As explained above, XCOFF shared libraries doesn't support
+       # all the ELF features. Thus, the output of the tests are
+       # a bit different.
+       set datfile xcoffvsb
+    }
+
     if { [istarget powerpc*-*-linux*] \
         || ( [istarget mips*-*-linux*] && [at_least_gcc_version 4 3] )} {
        # Testing non-PIC libraries is a waste of effort on any target.
@@ -287,7 +335,15 @@ proc visibility_run {visibility} {
                setup_xfail "arm*-*-linux*"
            }
 
-           visibility_test $visibility vnp "visibility ($visibility) (non PIC)" mainnp.o sh1np.o sh2np.o elfvsb
+           # XCOFF format doesn't know how to handle weak undefined symbols
+           # in shared objects.
+           if { [ string match $visibility "hidden_weak" ]
+                || [ string match $visibility "protected_weak" ] } {
+               setup_xfail "*-*-aix*"
+               setup_xfail "*-*-beos*"
+           }
+
+           visibility_test $visibility vnp "visibility ($visibility) (non PIC)" mainnp.o sh1np.o sh2np.o $datfile
 
            # Test ELF shared library relocations with a non-zero load
            # address for the library.  Near as I can tell, the R_*_RELATIVE
@@ -328,9 +384,19 @@ proc visibility_run {visibility} {
                setup_xfail "arm*-*-linux*"
            }
 
-           visibility_test $visibility vnp "visibility ($visibility) (non PIC, load offset)" \
-               mainnp.o sh1np.o sh2np.o elfvsb \
-               "-Wl,-T,$srcdir/$subdir/elf-offset.ld,--hash-style=sysv"
+           # XCOFF format doesn't know how to handle weak undefined symbols
+           # in shared objects.
+           if { [ string match $visibility "hidden_weak" ]
+                || [ string match $visibility "protected_weak" ] } {
+               setup_xfail "*-*-aix*"
+               setup_xfail "*-*-beos*"
+           }
+
+           if { ![is_xcoff_format] } {
+               visibility_test $visibility vnp "visibility ($visibility) (non PIC, load offset)" \
+                   mainnp.o sh1np.o sh2np.o $datfile \
+                   "-Wl,-T,$srcdir/$subdir/elf-offset.ld,--hash-style=sysv"
+           }
        }
 
        # Now compile the code using -fpic.
@@ -345,7 +411,7 @@ proc visibility_run {visibility} {
                    setup_xfail $target_triplet
                }
            }
-           visibility_test $visibility vp "visibility ($visibility)" mainnp.o sh1p.o sh2p.o elfvsb $COMPRESS_LDFLAG
+           visibility_test $visibility vp "visibility ($visibility)" mainnp.o sh1p.o sh2p.o $datfile $COMPRESS_LDFLAG
        }
     }}
 
@@ -392,7 +458,15 @@ proc visibility_run {visibility} {
                setup_xfail "arm*-*-linux*"
            }
 
-           visibility_test $visibility vmpnp "visibility ($visibility) (PIC main, non PIC so)" mainp.o sh1np.o sh2np.o elfvsb
+           # XCOFF format doesn't know how to handle weak undefined symbols
+           # in shared objects.
+           if { [ string match $visibility "hidden_weak" ]
+                || [ string match $visibility "protected_weak" ] } {
+               setup_xfail "*-*-aix*"
+               setup_xfail "*-*-beos*"
+           }
+
+           visibility_test $visibility vmpnp "visibility ($visibility) (PIC main, non PIC so)" mainp.o sh1np.o sh2np.o $datfile
        } else {
            unsupported "visibility (PIC main, non PIC so)"
        }
@@ -405,7 +479,16 @@ proc visibility_run {visibility} {
                    setup_xfail $target_triplet
                }
            }
-           visibility_test $visibility vmpp "visibility ($visibility) (PIC main)" mainp.o sh1p.o sh2p.o elfvsb
+
+           # XCOFF format doesn't know how to handle weak undefined symbols
+           # in shared objects.
+           if { [ string match $visibility "hidden_weak" ]
+                || [ string match $visibility "protected_weak" ] } {
+               setup_xfail "*-*-aix*"
+               setup_xfail "*-*-beos*"
+           }
+
+           visibility_test $visibility vmpp "visibility ($visibility) (PIC main)" mainp.o sh1p.o sh2p.o $datfile
        } else {
            unsupported "visibility ($visibility) (PIC main)"
        }
@@ -461,7 +544,12 @@ if { ![ld_compile "$CC_FOR_TARGET -g $NOSANITIZE_CFLAGS $NOLTO_CFLAGS" $srcdir/$
    if { ![ld_compile "$CC_FOR_TARGET -g $NOSANITIZE_CFLAGS $NOLTO_CFLAGS -DSHARED $picflag" $srcdir/$subdir/sh3.c tmpdir/sh3.o] } {
        unsupported "weak hidden symbol"
     } else {
-       if ![ld_link $ld tmpdir/sh3.so "-shared tmpdir/sh3.o"] {
+       set shared "-shared"
+       if { [is_xcoff_format] } {
+           # On AIX, setup imports and exports.
+           append shared " -bexpall"
+       }
+       if ![ld_link $ld tmpdir/sh3.so "$shared tmpdir/sh3.o"] {
            fail "weak hidden symbol"
        } else {
            if ![ld_link $ld tmpdir/weak "tmpdir/test.o tmpdir/sh3.so"] {
diff --git a/ld/testsuite/ld-vsb/xcoffvsb.dat b/ld/testsuite/ld-vsb/xcoffvsb.dat
new file mode 100644 (file)
index 0000000..aa75969
--- /dev/null
@@ -0,0 +1,24 @@
+mainvar == 1
+overriddenvar == 2
+shlibvar1 == 3
+shlib_mainvar () == 1
+shlib_shlibvar1 () == 3
+shlib_shlibvar2 () == 4
+shlib_shlibcall () == 5
+shlib_maincall () == 6
+main_called () == 6
+shlib_checkfunptr1 (shlib_shlibvar1) == 1
+shlib_checkfunptr2 (main_called) == 1
+shlib_getfunptr1 () == shlib_shlibvar1
+shlib_getfunptr2 () == main_called
+shlib_check () == 1
+visibility_check () == 1
+visibility_checkfunptr () == 1
+main_visibility_check () == 1
+visibility_checkvar () == 1
+visibility_checkvarptr () == 1
+main_visibility_checkvar () == 1
+main_visibility_checkcom () == 1
+shlib_visibility_checkcom () == 1
+main_visibility_checkweak () == 1
+shlib_visibility_checkweak () == 1