Bug 12372

Summary: Segfault with STT_GNU_IFUNC symbols on x86_64
Product: binutils Reporter: H.J. Lu <hjl.tools>
Component: goldAssignee: Ian Lance Taylor <ian>
Status: RESOLVED FIXED    
Severity: normal CC: hjl.tools, richard.sandiford
Priority: P2    
Version: 2.22   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Bug Depends on: 12366    
Bug Blocks:    
Attachments: A testcase

Description H.J. Lu 2011-01-06 19:01:29 UTC
+++ This bug was initially created as a clone of Bug #12366 +++

The x86 and x86_64 IFUNC code assumes that check_relocs can rely on def_regular being set for all regular definitions.  That's not true, because def_regular is set by the same pass that calls check_relocs, and doesn't therefore take into account regular definitions in later objects.  Everything works if the regular definition comes before the regular use, but not the other way round.
+++

[hjl@gnu-6 pr12366-1]$ make LD=ld.gold
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o main.o main.c
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o bar.o bar.c
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o foo.o foo.c
ld.gold  -shared -o libfoo.so bar.o foo.o
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -o main main.o libfoo.so -Wl,-rpath,.
./main
make: *** [all] Segmentation fault
[hjl@gnu-6 pr12366-1]$ 

GNU linker works:

[hjl@gnu-6 pr12366-1]$ make clean
rm -f a.out *.o *.so
[hjl@gnu-6 pr12366-1]$ make
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o main.o main.c
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o bar.o bar.c
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -fPIC    -c -o foo.o foo.c
./ld  -shared -o libfoo.so bar.o foo.o
/export/build/gnu/gcc/build-x86_64-linux/gcc/xgcc -B/export/build/gnu/gcc/build-x86_64-linux/gcc/ -o main main.o libfoo.so -Wl,-rpath,.
./main
Hello, world!
[hjl@gnu-6 pr12366-1]$
Comment 1 Ian Lance Taylor 2011-07-06 13:30:54 UTC
What is the test case for this bug report?  It doesn't seem to correspond to the test case in PR 12366, which has no main program.  The description of the test case is written in terms of GNU ld; gold works differently and can not have the problem which is described.
Comment 2 H.J. Lu 2011-07-06 13:55:58 UTC
Created attachment 5836 [details]
A testcase
Comment 3 Ian Lance Taylor 2011-07-06 14:42:36 UTC
Thanks.  The problem seems to have to do with the order of the relocs in libfoo.so.  Currently gold is putting the R_X86_64_IRELATIVE reloc first in .rela.dyn.  That means that it runs before the other relocs are run, which means that when it refers to a global variable, that variable has not been initialized.
Comment 4 Sourceware Commits 2011-07-08 22:48:11 UTC
CVSROOT:	/cvs/src
Module name:	src
Changes by:	ian@sourceware.org	2011-07-08 22:48:08

Modified files:
	gold           : ChangeLog i386.cc incremental.cc layout.cc 
	                 output.cc target.h x86_64.cc 
	gold/testsuite : Makefile.am Makefile.in 
Added files:
	gold/testsuite : ifuncvar1.c ifuncvar2.c ifuncvar3.c 

Log message:
	PR gold/12372
	* target.h (Target::plt_address_for_global): New function.
	(Target::plt_address_for_local): New function.
	(Target::plt_section_for_global): Remove.
	(Target::plt_section_for_local): Remove.
	(Target::do_plt_address_for_global): New virtual function.
	(Target::do_plt_address_for_local): New virtual function.
	(Target::do_plt_section_for_global): Remove.
	(Target::do_plt_section_for_local): Remove.
	(Target::register_global_plt_entry): Add Symbol_table and Layout
	parameters.
	* output.cc (Output_data_got::Got_entry::write): Use
	plt_address_for_global and plt_address_for_local.
	* layout.cc (Layout::add_target_dynamic_tags): Use size and
	address of output section.
	* i386.cc (class Output_data_plt_i386): Add irelative_rel_,
	got_irelative_, and irelative_count_ fields.  Update
	declarations.
	(Output_data_plt_i386::has_irelative_section): New function.
	(Output_data_plt_i386::entry_count): Add irelative_count_.
	(Output_data_plt_i386::set_final_data_size): Likewise.
	(class Target_i386): Add got_irelative_ and rel_irelative_
	fields.  Update declarations.
	(Target_i386::Target_i386): Initialize new fields.
	(Target_i386::do_plt_address_for_global): New function replacing
	do_plt_section_for_global.
	(Target_i386::do_plt_address_for_local): New function replacing
	do_plt_section_for_local.
	(Target_i386::got_section): Create got_irelative_.
	(Target_i386::rel_irelative_section): New function.
	(Output_data_plt_i386::Output_data_plt_i386): Initialize new
	fields.  Don't define __rel_iplt_{start,end}.
	(Output_data_plt_i386::add_entry): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_i386::add_local_ifunc_entry): Add symtab and
	layout parameters.  Change all callers.  Use different PLT and
	GOT.
	(Output_data_plt_i386::rel_tls_desc): Fix formatting.
	(Output_data_plt_i386::rel_irelative): New function.
	(Output_data_plt_i386::address_for_global): New function.
	(Output_data_plt_i386::address_for_local): New function.
	(Output_data_plt_i386::do_write): Write out IRELATIVE area.  Use
	IRELATIVE GOT when changing IFUNC GOT entries.
	(Target_i386::Scan::global): Use IRELATIVE GOT for IRELATIVE
	reloc.
	(Target_i386::do_finalize_sections): Create the __rel_iplt symbols
	if we didn't create an IRELATIVE GOT.
	(Target_i386::Relocate::relocate): Use plt_address_for_global and
	plt_address_for_local.
	(Target_i386::do_dynsym_value): Use plt_address_for_global.
	* x86_64.cc (class Output_data_plt_x86_64): Add irelative_rel_,
	got_irelative_, and irelative_count_ fields.  Update
	declarations.
	(Output_data_plt_x86_64::Output_data_plt_x86_64) [both versions]:
	Initialize new fields.  Remove symtab parameter.  Change all
	callers.
	(Output_data_plt_x86_64::get_tlsdesc_plt_offset): Add
	irelative_count_.
	(Output_data_plt_x86_64::has_irelative_section): New function.
	(Output_data_plt_x86_64::entry_count): Add irelative_count_.
	(class Target_x86_64): Add got_irelative_ and rel_irelative_
	fields.  Update declarations.
	(Target_x86_64::Target_x86_64): Initialize new fields.
	(Target_x86_64::do_plt_address_for_global): New function replacing
	do_plt_section_for_global.
	(Target_x86_64::do_plt_address_for_local): New function replacing
	do_plt_section_for_local.
	(Target_x86_64::got_section): Create got_irelative_.
	(Target_x86_64::rela_irelative_section): New function.
	(Output_data_plt_x86_64::init): Remove symtab parameter.  Change
	all callers.  Don't create __rel_iplt_{start,end}.
	(Output_data_plt_x86_64::add_entry): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_x86_64::add_local_ifunc_entry): Add symtab and
	layout parameters.  Change all callers.  Use different PLT and
	GOT.
	(Output_data_plt_x86_64::add_relocation): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_x86_64::rela_tlsdesc): Fix formatting.
	(Output_data_plt_x86_64::rela_irelative): New function.
	(Output_data_plt_x86_64::address_for_global): New function.
	(Output_data_plt_x86_64::address_for_local): New function.
	(Output_data_plt_x86_64::set_final_data_size): Likewise.
	(Output_data_plt_x86_64::do_write): Write out IRELATIVE area.
	(Target_x86_64::init_got_plt_for_update): Create got_irelative_.
	(Target_x86_64::register_global_plt_entry): Add symtab and layout
	parameters.
	(Target_x86_64::Scan::global): Use IRELATIVE GOT for IRELATIVE
	reloc.
	(Target_x86_64::do_finalize_sections): Create the __rela_iplt
	symbols if we didn't create an IRELATIVE GOT.
	(Target_x86_64::Relocate::relocate): Use plt_address_for_global and
	plt_address_for_local.
	(Target_x86_64::do_dynsym_value): Use plt_address_for_global.
	* testsuite/ifuncvar1.c: New test file.
	* testsuite/ifuncvar2.c: New test file.
	* testsuite/ifuncvar3.c: New test file.
	* testsuite/Makefile.am (check_PROGRAMS): Add ifuncvar.
	(ifuncvar1_pic.o, ifuncvar2_pic.o, ifuncvar.so): New targets.
	(ifuncvar_SOURCES, ifuncvar_DEPENDENCIES): New variables.
	(ifuncvar_LDFLAGS, ifuncvar_LDADD): New variables.
	* testsuite/Makefile.in: Rebuild.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/ChangeLog.diff?cvsroot=src&r1=1.796&r2=1.797
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/i386.cc.diff?cvsroot=src&r1=1.135&r2=1.136
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/incremental.cc.diff?cvsroot=src&r1=1.43&r2=1.44
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/layout.cc.diff?cvsroot=src&r1=1.212&r2=1.213
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/output.cc.diff?cvsroot=src&r1=1.154&r2=1.155
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/target.h.diff?cvsroot=src&r1=1.61&r2=1.62
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/x86_64.cc.diff?cvsroot=src&r1=1.133&r2=1.134
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/testsuite/ifuncvar1.c.diff?cvsroot=src&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/testsuite/ifuncvar2.c.diff?cvsroot=src&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/testsuite/ifuncvar3.c.diff?cvsroot=src&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/testsuite/Makefile.am.diff?cvsroot=src&r1=1.175&r2=1.176
http://sourceware.org/cgi-bin/cvsweb.cgi/src/gold/testsuite/Makefile.in.diff?cvsroot=src&r1=1.184&r2=1.185
Comment 5 Ian Lance Taylor 2011-07-08 22:50:52 UTC
Fixed by putting IRELATIVE relocs after JUMP_SLOT relocs in the PLT.

Thanks for testing gold and reporting this.