From 90c984fc53ae23dcda0cba309ad4b51a9dd5d9ee Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 3 Jul 2012 14:44:35 +0000 Subject: [PATCH] Properly handle indirect symbols 2012-07-03 H.J. Lu PR ld/3351 * elflink.c (_bfd_elf_update_dynamic_flags): New. (_bfd_elf_merge_symbol): Update both real and indirect symbol dynamic flags. (_bfd_elf_add_default_symbol): Make the real symbol dynamic if the indirect symbol is defined in a shared library. (elf_link_add_object_symbols): Likewise. If the indirect symbol has been forced local, don't make the real symbol dynamic. (elf_link_check_versioned_symbol): Check indirect symbol. (elf_link_output_extsym): Use real symbol definition when reporting indirect symbol error. Check version info for dynamic versioned symbol. 2012-07-03 H.J. Lu PR ld/3351 * ld-elf/indirect.exp: New file. * ld-elf/indirect1a.c: Likewise. * ld-elf/indirect1b.c: Likewise. * ld-elf/indirect1c.c: Likewise. * ld-elf/indirect2.c: Likewise. * ld-elf/indirect3.out: Likewise. * ld-elf/indirect3a.c: Likewise. * ld-elf/indirect3b.c: Likewise. * ld-elf/indirect3c.c: Likewise. * ld-elf/indirect4.out: Likewise. * ld-elf/indirect4a.c: Likewise. * ld-elf/indirect4b.c: Likewise. * ld-elf/indirect4c.c: Likewise. --- bfd/ChangeLog | 16 ++++ bfd/elflink.c | 120 +++++++++++++++++++++------- ld/testsuite/ChangeLog | 17 ++++ ld/testsuite/ld-elf/indirect.exp | 126 ++++++++++++++++++++++++++++++ ld/testsuite/ld-elf/indirect1a.c | 8 ++ ld/testsuite/ld-elf/indirect1b.c | 6 ++ ld/testsuite/ld-elf/indirect1c.c | 7 ++ ld/testsuite/ld-elf/indirect2.c | 9 +++ ld/testsuite/ld-elf/indirect3.out | 2 + ld/testsuite/ld-elf/indirect3a.c | 10 +++ ld/testsuite/ld-elf/indirect3b.c | 9 +++ ld/testsuite/ld-elf/indirect3c.c | 15 ++++ ld/testsuite/ld-elf/indirect4.out | 2 + ld/testsuite/ld-elf/indirect4a.c | 10 +++ ld/testsuite/ld-elf/indirect4b.c | 17 ++++ ld/testsuite/ld-elf/indirect4c.c | 15 ++++ 16 files changed, 360 insertions(+), 29 deletions(-) create mode 100644 ld/testsuite/ld-elf/indirect.exp create mode 100644 ld/testsuite/ld-elf/indirect1a.c create mode 100644 ld/testsuite/ld-elf/indirect1b.c create mode 100644 ld/testsuite/ld-elf/indirect1c.c create mode 100644 ld/testsuite/ld-elf/indirect2.c create mode 100644 ld/testsuite/ld-elf/indirect3.out create mode 100644 ld/testsuite/ld-elf/indirect3a.c create mode 100644 ld/testsuite/ld-elf/indirect3b.c create mode 100644 ld/testsuite/ld-elf/indirect3c.c create mode 100644 ld/testsuite/ld-elf/indirect4.out create mode 100644 ld/testsuite/ld-elf/indirect4a.c create mode 100644 ld/testsuite/ld-elf/indirect4b.c create mode 100644 ld/testsuite/ld-elf/indirect4c.c diff --git a/bfd/ChangeLog b/bfd/ChangeLog index fc9c38632c..2b1e9e316c 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,19 @@ +2012-07-03 H.J. Lu + + PR ld/3351 + * elflink.c (_bfd_elf_update_dynamic_flags): New. + (_bfd_elf_merge_symbol): Update both real and indirect symbol + dynamic flags. + (_bfd_elf_add_default_symbol): Make the real symbol dynamic if + the indirect symbol is defined in a shared library. + (elf_link_add_object_symbols): Likewise. If the indirect + symbol has been forced local, don't make the real symbol + dynamic. + (elf_link_check_versioned_symbol): Check indirect symbol. + (elf_link_output_extsym): Use real symbol definition when + reporting indirect symbol error. Check version info for + dynamic versioned symbol. + 2012-07-03 Alan Modra PR ld/14207 diff --git a/bfd/elflink.c b/bfd/elflink.c index d9e1abe0dd..7679b9a755 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -892,6 +892,33 @@ elf_merge_st_other (bfd *abfd, struct elf_link_hash_entry *h, } } +/* Mark if a symbol has a definition in a dynamic object or is + weak in all dynamic objects. */ + +static void +_bfd_elf_mark_dynamic_def_weak (struct elf_link_hash_entry *h, + asection *sec, int bind) +{ + if (!h->dynamic_def) + { + if (!bfd_is_und_section (sec)) + h->dynamic_def = 1; + else + { + /* Check if this symbol is weak in all dynamic objects. If it + is the first time we see it in a dynamic object, we mark + if it is weak. Otherwise, we clear it. */ + if (!h->ref_dynamic) + { + if (bind == STB_WEAK) + h->dynamic_weak = 1; + } + else if (bind != STB_WEAK) + h->dynamic_weak = 0; + } + } +} + /* This function is called when we want to define a new symbol. It handles the various cases which arise when we find a definition in a dynamic object, or when there is already a definition in a @@ -920,6 +947,7 @@ _bfd_elf_merge_symbol (bfd *abfd, { asection *sec, *oldsec; struct elf_link_hash_entry *h; + struct elf_link_hash_entry *hi; struct elf_link_hash_entry *flip; int bind; bfd *oldbfd; @@ -958,8 +986,9 @@ _bfd_elf_merge_symbol (bfd *abfd, if (!(*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec)) return TRUE; - /* For merging, we only care about real symbols. */ - + /* For merging, we only care about real symbols. But we need to make + sure that indirect symbol dynamic flags are updated. */ + hi = h; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; @@ -1135,23 +1164,11 @@ _bfd_elf_merge_symbol (bfd *abfd, /* We need to remember if a symbol has a definition in a dynamic object or is weak in all dynamic objects. Internal and hidden visibility will make it unavailable to dynamic objects. */ - if (newdyn && !h->dynamic_def) + if (newdyn) { - if (!bfd_is_und_section (sec)) - h->dynamic_def = 1; - else - { - /* Check if this symbol is weak in all dynamic objects. If it - is the first time we see it in a dynamic object, we mark - if it is weak. Otherwise, we clear it. */ - if (!h->ref_dynamic) - { - if (bind == STB_WEAK) - h->dynamic_weak = 1; - } - else if (bind != STB_WEAK) - h->dynamic_weak = 0; - } + _bfd_elf_mark_dynamic_def_weak (h, sec, bind); + if (h != hi) + _bfd_elf_mark_dynamic_def_weak (hi, sec, bind); } /* If the old symbol has non-default visibility, we ignore the new @@ -1163,6 +1180,7 @@ _bfd_elf_merge_symbol (bfd *abfd, *skip = TRUE; /* Make sure this symbol is dynamic. */ h->ref_dynamic = 1; + hi->ref_dynamic = 1; /* A protected symbol has external availability. Make sure it is recorded as dynamic. @@ -1723,6 +1741,7 @@ _bfd_elf_add_default_symbol (bfd *abfd, if (! dynamic) { if (! info->executable + || hi->def_dynamic || hi->ref_dynamic) *dynsym = TRUE; } @@ -3838,6 +3857,7 @@ error_free_dyn: flagword flags; const char *name; struct elf_link_hash_entry *h; + struct elf_link_hash_entry *hi; bfd_boolean definition; bfd_boolean size_change_ok; bfd_boolean type_change_ok; @@ -4170,6 +4190,9 @@ error_free_dyn: goto error_free_vers; h = *sym_hash; + /* We need to make sure that indirect symbol dynamic flags are + updated. */ + hi = h; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; @@ -4358,25 +4381,38 @@ error_free_dyn: h->ref_dynamic = 1; } } - if (! info->executable - || h->def_dynamic - || h->ref_dynamic) + + /* If the indirect symbol has been forced local, don't + make the real symbol dynamic. */ + if ((h == hi || !hi->forced_local) + && (! info->executable + || h->def_dynamic + || h->ref_dynamic)) dynsym = TRUE; } else { if (! definition) - h->ref_dynamic = 1; + { + h->ref_dynamic = 1; + hi->ref_dynamic = 1; + } else { h->def_dynamic = 1; h->dynamic_def = 1; + hi->def_dynamic = 1; + hi->dynamic_def = 1; } - if (h->def_regular - || h->ref_regular - || (h->u.weakdef != NULL - && ! new_weakdef - && h->u.weakdef->dynindx != -1)) + + /* If the indirect symbol has been forced local, don't + make the real symbol dynamic. */ + if ((h == hi || !hi->forced_local) + && (h->def_regular + || h->ref_regular + || (h->u.weakdef != NULL + && ! new_weakdef + && h->u.weakdef->dynindx != -1))) dynsym = TRUE; } @@ -8441,6 +8477,10 @@ elf_link_check_versioned_symbol (struct bfd_link_info *info, if (!is_elf_hash_table (info->hash)) return FALSE; + /* Check indirect symbol. */ + while (h->root.type == bfd_link_hash_indirect) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + switch (h->root.type) { default: @@ -8670,6 +8710,11 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data) { bfd *def_bfd; const char *msg; + struct elf_link_hash_entry *hi = h; + + /* Check indirect symbol. */ + while (hi->root.type == bfd_link_hash_indirect) + hi = (struct elf_link_hash_entry *) hi->root.u.i.link; if (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL) msg = _("%B: internal symbol `%s' in %B is referenced by DSO"); @@ -8678,8 +8723,8 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data) else msg = _("%B: local symbol `%s' in %B is referenced by DSO"); def_bfd = flinfo->output_bfd; - if (h->root.u.def.section != bfd_abs_section_ptr) - def_bfd = h->root.u.def.section->owner; + if (hi->root.u.def.section != bfd_abs_section_ptr) + def_bfd = hi->root.u.def.section->owner; (*_bfd_error_handler) (msg, flinfo->output_bfd, def_bfd, h->root.root.string); bfd_set_error (bfd_error_bad_value); @@ -8929,6 +8974,23 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data) { bfd_byte *esym; + /* Since there is no version information in the dynamic string, + if there is no version info in symbol version section, we will + have a run-time problem. */ + if (h->verinfo.verdef == NULL) + { + char *p = strrchr (h->root.root.string, ELF_VER_CHR); + + if (p && p [1] != '\0') + { + (*_bfd_error_handler) + (_("%B: No symbol version section for versioned symbol `%s'"), + flinfo->output_bfd, h->root.root.string); + eoinfo->failed = TRUE; + return FALSE; + } + } + sym.st_name = h->dynstr_index; esym = flinfo->dynsym_sec->contents + h->dynindx * bed->s->sizeof_sym; if (!check_dynsym (flinfo->output_bfd, &sym)) diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index da4657490b..c1cfb341fa 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,20 @@ +2012-07-03 H.J. Lu + + PR ld/3351 + * ld-elf/indirect.exp: New file. + * ld-elf/indirect1a.c: Likewise. + * ld-elf/indirect1b.c: Likewise. + * ld-elf/indirect1c.c: Likewise. + * ld-elf/indirect2.c: Likewise. + * ld-elf/indirect3.out: Likewise. + * ld-elf/indirect3a.c: Likewise. + * ld-elf/indirect3b.c: Likewise. + * ld-elf/indirect3c.c: Likewise. + * ld-elf/indirect4.out: Likewise. + * ld-elf/indirect4a.c: Likewise. + * ld-elf/indirect4b.c: Likewise. + * ld-elf/indirect4c.c: Likewise. + 2012-07-02 Roland McGrath * ld/testsuite/ld-i386/tlsbin-nacl.rd: Update for symbol table changes. diff --git a/ld/testsuite/ld-elf/indirect.exp b/ld/testsuite/ld-elf/indirect.exp new file mode 100644 index 0000000000..165f89ac5a --- /dev/null +++ b/ld/testsuite/ld-elf/indirect.exp @@ -0,0 +1,126 @@ +# Expect script for various indirect symbol tests. +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. +# + +# +# Written by H.J. Lu (hongjiu.lu@intel.com) +# + +# Exclude non-ELF targets. + +if ![is_elf_format] { + return +} + +# Check if compiler works +if { [which $CC] == 0 } { + return +} + +proc check_link_message { cmd string testname } { + send_log "$cmd\n" + verbose "$cmd" + catch "exec $cmd" exec_output + send_log "$exec_output\n" + verbose "$exec_output" + + foreach str $string { + if [string match "*$str*" $exec_output] { + pass "$testname: $str" + } else { + fail "$testname: $str" + } + } +} + +if { ![ld_compile $CC $srcdir/$subdir/indirect1a.c tmpdir/indirect1a.o] + || ![ld_compile $CC $srcdir/$subdir/indirect1b.c tmpdir/indirect1b.o] + || ![ld_compile "$CC -fPIC" $srcdir/$subdir/indirect2.c tmpdir/indirect2.o] + || ![ld_compile $CC $srcdir/$subdir/indirect3a.c tmpdir/indirect3a.o] + || ![ld_compile $CC $srcdir/$subdir/indirect3b.c tmpdir/indirect3b.o] + || ![ld_compile $CC $srcdir/$subdir/indirect4a.c tmpdir/indirect4a.o] + || ![ld_compile $CC $srcdir/$subdir/indirect4b.c tmpdir/indirect4b.o] } { + unresolved "Indirect symbol tests" + return +} + +set build_tests { + {"Build libindirect1c.so" + "-shared" "-fPIC" + {indirect1c.c} {} "libindirect1c.so"} + {"Build libindirect3c.so" + "-shared" "-fPIC" + {indirect3c.c} {} "libindirect3c.so"} + {"Build libindirect4c.so" + "-shared" "-fPIC" + {indirect4c.c} {} "libindirect4c.so"} +} + +run_cc_link_tests $build_tests + +global ld + +set string ": final link failed: Bad value" +set string1 ": local symbol \`foo\' in tmpdir/indirect1b.o is referenced by DSO" + +set testname "Indirect symbol 1a" +set cmd "$ld -e start -o tmpdir/indirect1 tmpdir/indirect1a.o tmpdir/indirect1b.o tmpdir/libindirect1c.so" +check_link_message "$cmd" [list $string1 $string] "$testname" + +set testname "Indirect symbol 1b" +set cmd "$ld -e start -o tmpdir/indirect1 tmpdir/indirect1a.o tmpdir/libindirect1c.so tmpdir/indirect1b.o" +check_link_message "$cmd" [list $string1 $string] "$testname" + +set string ": final link failed: Nonrepresentable section on output" +set string2 ": No symbol version section for versioned symbol \`foo@FOO\'" +set testname "Indirect symbol 2" +set cmd "$ld -shared -o tmpdir/indirect2.so tmpdir/indirect2.o" +check_link_message "$cmd" [list $string2 $string] "$testname" + +# The following tests require running the executable generated by ld. +if ![isnative] { + return +} + +set run_tests { + {"Run with libindirect3c.so 1" + "tmpdir/indirect3a.o tmpdir/indirect3b.o tmpdir/libindirect3c.so" "" + {dummy.c} "indirect3a" "indirect3.out"} + {"Run with libindirect3c.so 2" + "tmpdir/indirect3a.o tmpdir/libindirect3c.so tmpdir/indirect3b.o" "" + {dummy.c} "indirect3b" "indirect3.out"} + {"Run with libindirect3c.so 3" + "tmpdir/indirect3b.o tmpdir/libindirect3c.so tmpdir/indirect3a.o" "" + {dummy.c} "indirect3c" "indirect3.out"} + {"Run with libindirect3c.so 4" + "tmpdir/libindirect3c.so tmpdir/indirect3b.o tmpdir/indirect3a.o" "" + {dummy.c} "indirect3d" "indirect3.out"} + {"Run with libindirect4c.so 1" + "tmpdir/indirect4a.o tmpdir/indirect4b.o tmpdir/libindirect4c.so" "" + {dummy.c} "indirect4a" "indirect4.out"} + {"Run with libindirect4c.so 2" + "tmpdir/indirect4a.o tmpdir/libindirect4c.so tmpdir/indirect4b.o" "" + {dummy.c} "indirect4b" "indirect4.out"} + {"Run with libindirect4c.so 3" + "tmpdir/indirect4b.o tmpdir/libindirect4c.so tmpdir/indirect4a.o" "" + {dummy.c} "indirect4c" "indirect4.out"} + {"Run with libindirect4c.so 4" + "tmpdir/libindirect4c.so tmpdir/indirect4b.o tmpdir/indirect4a.o" "" + {dummy.c} "indirect4d" "indirect4.out"} +} + +run_ld_link_exec_tests [] $run_tests diff --git a/ld/testsuite/ld-elf/indirect1a.c b/ld/testsuite/ld-elf/indirect1a.c new file mode 100644 index 0000000000..693154268a --- /dev/null +++ b/ld/testsuite/ld-elf/indirect1a.c @@ -0,0 +1,8 @@ +extern void bar (void); + +int +start (void) +{ + bar (); + return 0; +} diff --git a/ld/testsuite/ld-elf/indirect1b.c b/ld/testsuite/ld-elf/indirect1b.c new file mode 100644 index 0000000000..51740f9078 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect1b.c @@ -0,0 +1,6 @@ +void +foo (void) +{ +} + +asm (".symver foo,foo@FOO"); diff --git a/ld/testsuite/ld-elf/indirect1c.c b/ld/testsuite/ld-elf/indirect1c.c new file mode 100644 index 0000000000..eae278df96 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect1c.c @@ -0,0 +1,7 @@ +extern void foo (void); + +void +bar (void) +{ + foo (); +} diff --git a/ld/testsuite/ld-elf/indirect2.c b/ld/testsuite/ld-elf/indirect2.c new file mode 100644 index 0000000000..6df29bed2d --- /dev/null +++ b/ld/testsuite/ld-elf/indirect2.c @@ -0,0 +1,9 @@ +extern void foo (void); + +asm (".symver foo,foo@@@FOO"); + +void +bar (void) +{ + foo (); +} diff --git a/ld/testsuite/ld-elf/indirect3.out b/ld/testsuite/ld-elf/indirect3.out new file mode 100644 index 0000000000..482e98115f --- /dev/null +++ b/ld/testsuite/ld-elf/indirect3.out @@ -0,0 +1,2 @@ +MAIN +DSO diff --git a/ld/testsuite/ld-elf/indirect3a.c b/ld/testsuite/ld-elf/indirect3a.c new file mode 100644 index 0000000000..0f6ddc8b71 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect3a.c @@ -0,0 +1,10 @@ +extern void bar (void); +extern void foo (void); + +int +main (void) +{ + foo (); + bar (); + return 0; +} diff --git a/ld/testsuite/ld-elf/indirect3b.c b/ld/testsuite/ld-elf/indirect3b.c new file mode 100644 index 0000000000..dbb37c3a80 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect3b.c @@ -0,0 +1,9 @@ +#include + +void +foo (void) +{ + printf ("MAIN\n"); +} + +asm (".symver foo,foo@FOO"); diff --git a/ld/testsuite/ld-elf/indirect3c.c b/ld/testsuite/ld-elf/indirect3c.c new file mode 100644 index 0000000000..b52cb956f1 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect3c.c @@ -0,0 +1,15 @@ +#include + +extern void foo (void); + +void +foo (void) +{ + printf ("DSO\n"); +} + +void +bar (void) +{ + foo (); +} diff --git a/ld/testsuite/ld-elf/indirect4.out b/ld/testsuite/ld-elf/indirect4.out new file mode 100644 index 0000000000..3b34ee414a --- /dev/null +++ b/ld/testsuite/ld-elf/indirect4.out @@ -0,0 +1,2 @@ +MAIN2 +MAIN2 diff --git a/ld/testsuite/ld-elf/indirect4a.c b/ld/testsuite/ld-elf/indirect4a.c new file mode 100644 index 0000000000..0f6ddc8b71 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect4a.c @@ -0,0 +1,10 @@ +extern void bar (void); +extern void foo (void); + +int +main (void) +{ + foo (); + bar (); + return 0; +} diff --git a/ld/testsuite/ld-elf/indirect4b.c b/ld/testsuite/ld-elf/indirect4b.c new file mode 100644 index 0000000000..b8db9d0807 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect4b.c @@ -0,0 +1,17 @@ +#include + +void +foo2 (void) +{ + printf ("MAIN2\n"); +} + +asm (".symver foo2,foo@@FOO2"); + +void +foo1 (void) +{ + printf ("MAIN1\n"); +} + +asm (".symver foo1,foo@FOO1"); diff --git a/ld/testsuite/ld-elf/indirect4c.c b/ld/testsuite/ld-elf/indirect4c.c new file mode 100644 index 0000000000..b52cb956f1 --- /dev/null +++ b/ld/testsuite/ld-elf/indirect4c.c @@ -0,0 +1,15 @@ +#include + +extern void foo (void); + +void +foo (void) +{ + printf ("DSO\n"); +} + +void +bar (void) +{ + foo (); +} -- 2.34.1