Properly handle indirect symbols

2012-07-03  H.J. Lu  <hongjiu.lu@intel.com>

	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  <hongjiu.lu@intel.com>

	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.
This commit is contained in:
H.J. Lu 2012-07-03 14:44:35 +00:00
parent 3146fac428
commit 90c984fc53
16 changed files with 360 additions and 29 deletions

View file

@ -1,3 +1,19 @@
2012-07-03 H.J. Lu <hongjiu.lu@intel.com>
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 <amodra@gmail.com> 2012-07-03 Alan Modra <amodra@gmail.com>
PR ld/14207 PR ld/14207

View file

@ -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 /* 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 handles the various cases which arise when we find a definition in
a dynamic object, or when there is already a definition in a 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; asection *sec, *oldsec;
struct elf_link_hash_entry *h; struct elf_link_hash_entry *h;
struct elf_link_hash_entry *hi;
struct elf_link_hash_entry *flip; struct elf_link_hash_entry *flip;
int bind; int bind;
bfd *oldbfd; bfd *oldbfd;
@ -958,8 +986,9 @@ _bfd_elf_merge_symbol (bfd *abfd,
if (!(*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec)) if (!(*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
return TRUE; 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 while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning) || h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link; 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 /* We need to remember if a symbol has a definition in a dynamic
object or is weak in all dynamic objects. Internal and hidden object or is weak in all dynamic objects. Internal and hidden
visibility will make it unavailable to dynamic objects. */ visibility will make it unavailable to dynamic objects. */
if (newdyn && !h->dynamic_def) if (newdyn)
{ {
if (!bfd_is_und_section (sec)) _bfd_elf_mark_dynamic_def_weak (h, sec, bind);
h->dynamic_def = 1; if (h != hi)
else _bfd_elf_mark_dynamic_def_weak (hi, sec, bind);
{
/* 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;
}
} }
/* If the old symbol has non-default visibility, we ignore the new /* If the old symbol has non-default visibility, we ignore the new
@ -1163,6 +1180,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
*skip = TRUE; *skip = TRUE;
/* Make sure this symbol is dynamic. */ /* Make sure this symbol is dynamic. */
h->ref_dynamic = 1; h->ref_dynamic = 1;
hi->ref_dynamic = 1;
/* A protected symbol has external availability. Make sure it is /* A protected symbol has external availability. Make sure it is
recorded as dynamic. recorded as dynamic.
@ -1723,6 +1741,7 @@ _bfd_elf_add_default_symbol (bfd *abfd,
if (! dynamic) if (! dynamic)
{ {
if (! info->executable if (! info->executable
|| hi->def_dynamic
|| hi->ref_dynamic) || hi->ref_dynamic)
*dynsym = TRUE; *dynsym = TRUE;
} }
@ -3838,6 +3857,7 @@ error_free_dyn:
flagword flags; flagword flags;
const char *name; const char *name;
struct elf_link_hash_entry *h; struct elf_link_hash_entry *h;
struct elf_link_hash_entry *hi;
bfd_boolean definition; bfd_boolean definition;
bfd_boolean size_change_ok; bfd_boolean size_change_ok;
bfd_boolean type_change_ok; bfd_boolean type_change_ok;
@ -4170,6 +4190,9 @@ error_free_dyn:
goto error_free_vers; goto error_free_vers;
h = *sym_hash; 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 while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning) || h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link; h = (struct elf_link_hash_entry *) h->root.u.i.link;
@ -4358,25 +4381,38 @@ error_free_dyn:
h->ref_dynamic = 1; h->ref_dynamic = 1;
} }
} }
if (! info->executable
|| h->def_dynamic /* If the indirect symbol has been forced local, don't
|| h->ref_dynamic) make the real symbol dynamic. */
if ((h == hi || !hi->forced_local)
&& (! info->executable
|| h->def_dynamic
|| h->ref_dynamic))
dynsym = TRUE; dynsym = TRUE;
} }
else else
{ {
if (! definition) if (! definition)
h->ref_dynamic = 1; {
h->ref_dynamic = 1;
hi->ref_dynamic = 1;
}
else else
{ {
h->def_dynamic = 1; h->def_dynamic = 1;
h->dynamic_def = 1; h->dynamic_def = 1;
hi->def_dynamic = 1;
hi->dynamic_def = 1;
} }
if (h->def_regular
|| h->ref_regular /* If the indirect symbol has been forced local, don't
|| (h->u.weakdef != NULL make the real symbol dynamic. */
&& ! new_weakdef if ((h == hi || !hi->forced_local)
&& h->u.weakdef->dynindx != -1)) && (h->def_regular
|| h->ref_regular
|| (h->u.weakdef != NULL
&& ! new_weakdef
&& h->u.weakdef->dynindx != -1)))
dynsym = TRUE; dynsym = TRUE;
} }
@ -8441,6 +8477,10 @@ elf_link_check_versioned_symbol (struct bfd_link_info *info,
if (!is_elf_hash_table (info->hash)) if (!is_elf_hash_table (info->hash))
return FALSE; 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) switch (h->root.type)
{ {
default: default:
@ -8670,6 +8710,11 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
{ {
bfd *def_bfd; bfd *def_bfd;
const char *msg; 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) if (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL)
msg = _("%B: internal symbol `%s' in %B is referenced by DSO"); 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 else
msg = _("%B: local symbol `%s' in %B is referenced by DSO"); msg = _("%B: local symbol `%s' in %B is referenced by DSO");
def_bfd = flinfo->output_bfd; def_bfd = flinfo->output_bfd;
if (h->root.u.def.section != bfd_abs_section_ptr) if (hi->root.u.def.section != bfd_abs_section_ptr)
def_bfd = h->root.u.def.section->owner; def_bfd = hi->root.u.def.section->owner;
(*_bfd_error_handler) (msg, flinfo->output_bfd, def_bfd, (*_bfd_error_handler) (msg, flinfo->output_bfd, def_bfd,
h->root.root.string); h->root.root.string);
bfd_set_error (bfd_error_bad_value); 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; 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; sym.st_name = h->dynstr_index;
esym = flinfo->dynsym_sec->contents + h->dynindx * bed->s->sizeof_sym; esym = flinfo->dynsym_sec->contents + h->dynindx * bed->s->sizeof_sym;
if (!check_dynsym (flinfo->output_bfd, &sym)) if (!check_dynsym (flinfo->output_bfd, &sym))

View file

@ -1,3 +1,20 @@
2012-07-03 H.J. Lu <hongjiu.lu@intel.com>
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 <mcgrathr@google.com> 2012-07-02 Roland McGrath <mcgrathr@google.com>
* ld/testsuite/ld-i386/tlsbin-nacl.rd: Update for symbol table changes. * ld/testsuite/ld-i386/tlsbin-nacl.rd: Update for symbol table changes.

View file

@ -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

View file

@ -0,0 +1,8 @@
extern void bar (void);
int
start (void)
{
bar ();
return 0;
}

View file

@ -0,0 +1,6 @@
void
foo (void)
{
}
asm (".symver foo,foo@FOO");

View file

@ -0,0 +1,7 @@
extern void foo (void);
void
bar (void)
{
foo ();
}

View file

@ -0,0 +1,9 @@
extern void foo (void);
asm (".symver foo,foo@@@FOO");
void
bar (void)
{
foo ();
}

View file

@ -0,0 +1,2 @@
MAIN
DSO

View file

@ -0,0 +1,10 @@
extern void bar (void);
extern void foo (void);
int
main (void)
{
foo ();
bar ();
return 0;
}

View file

@ -0,0 +1,9 @@
#include <stdio.h>
void
foo (void)
{
printf ("MAIN\n");
}
asm (".symver foo,foo@FOO");

View file

@ -0,0 +1,15 @@
#include <stdio.h>
extern void foo (void);
void
foo (void)
{
printf ("DSO\n");
}
void
bar (void)
{
foo ();
}

View file

@ -0,0 +1,2 @@
MAIN2
MAIN2

View file

@ -0,0 +1,10 @@
extern void bar (void);
extern void foo (void);
int
main (void)
{
foo ();
bar ();
return 0;
}

View file

@ -0,0 +1,17 @@
#include <stdio.h>
void
foo2 (void)
{
printf ("MAIN2\n");
}
asm (".symver foo2,foo@@FOO2");
void
foo1 (void)
{
printf ("MAIN1\n");
}
asm (".symver foo1,foo@FOO1");

View file

@ -0,0 +1,15 @@
#include <stdio.h>
extern void foo (void);
void
foo (void)
{
printf ("DSO\n");
}
void
bar (void)
{
foo ();
}