From a8bc481f352d61bb2962e983a1c1c1fdad0230b7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Cl=C3=A9ment=20Chigot?= Date: Tue, 23 Nov 2021 15:45:28 +0100 Subject: [PATCH] ld: add hidden and internal visibility support for XCOFF 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 | 50 ++++++++- ld/testsuite/ld-vsb/define.s | 6 ++ ld/testsuite/ld-vsb/main.c | 8 +- ld/testsuite/ld-vsb/sh1.c | 10 -- ld/testsuite/ld-vsb/visibility-1-xcoff-32.d | 21 ++++ ld/testsuite/ld-vsb/visibility-1-xcoff-64.d | 21 ++++ ld/testsuite/ld-vsb/visibility-2-xcoff-32.d | 16 +++ ld/testsuite/ld-vsb/visibility-2-xcoff-64.d | 16 +++ ld/testsuite/ld-vsb/vsb.exp | 112 +++++++++++++++++--- ld/testsuite/ld-vsb/xcoffvsb.dat | 24 +++++ 10 files changed, 254 insertions(+), 30 deletions(-) create mode 100644 ld/testsuite/ld-vsb/visibility-1-xcoff-32.d create mode 100644 ld/testsuite/ld-vsb/visibility-1-xcoff-64.d create mode 100644 ld/testsuite/ld-vsb/visibility-2-xcoff-32.d create mode 100644 ld/testsuite/ld-vsb/visibility-2-xcoff-64.d create mode 100644 ld/testsuite/ld-vsb/xcoffvsb.dat diff --git a/bfd/xcofflink.c b/bfd/xcofflink.c index ba07ba7f045..6e994720f6d 100644 --- a/bfd/xcofflink.c +++ b/bfd/xcofflink.c @@ -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); diff --git a/ld/testsuite/ld-vsb/define.s b/ld/testsuite/ld-vsb/define.s index b38e3e0c053..3dc25ed7185 100644 --- a/ld/testsuite/ld-vsb/define.s +++ b/ld/testsuite/ld-vsb/define.s @@ -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: diff --git a/ld/testsuite/ld-vsb/main.c b/ld/testsuite/ld-vsb/main.c index c2c92911344..b0359c0d815 100644 --- a/ld/testsuite/ld-vsb/main.c +++ b/ld/testsuite/ld-vsb/main.c @@ -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", diff --git a/ld/testsuite/ld-vsb/sh1.c b/ld/testsuite/ld-vsb/sh1.c index 1aba63c7541..193eca2943b 100644 --- a/ld/testsuite/ld-vsb/sh1.c +++ b/ld/testsuite/ld-vsb/sh1.c @@ -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 index 00000000000..19cff64827f --- /dev/null +++ b/ld/testsuite/ld-vsb/visibility-1-xcoff-32.d @@ -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 index 00000000000..a63dcf03819 --- /dev/null +++ b/ld/testsuite/ld-vsb/visibility-1-xcoff-64.d @@ -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 index 00000000000..54beedd4a60 --- /dev/null +++ b/ld/testsuite/ld-vsb/visibility-2-xcoff-32.d @@ -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 index 00000000000..b607a5325de --- /dev/null +++ b/ld/testsuite/ld-vsb/visibility-2-xcoff-64.d @@ -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 +.* diff --git a/ld/testsuite/ld-vsb/vsb.exp b/ld/testsuite/ld-vsb/vsb.exp index 52a7d6b5f74..bc2a9b8a836 100644 --- a/ld/testsuite/ld-vsb/vsb.exp +++ b/ld/testsuite/ld-vsb/vsb.exp @@ -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 index 00000000000..aa75969583c --- /dev/null +++ b/ld/testsuite/ld-vsb/xcoffvsb.dat @@ -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 -- 2.30.2