This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
IA-64 gp problem with PROVIDE and linker relaxation conflict
- To: binutils at sources dot redhat dot com
- Subject: IA-64 gp problem with PROVIDE and linker relaxation conflict
- From: Jim Wilson <wilson at cygnus dot com>
- Date: Mon, 12 Feb 2001 20:37:18 -0800
- cc: wilson at cygnus dot com
We are having problems with the gp value getting set incorrectly when GNU ld
links objects produced by the Intel compiler. I got a pointer from Intel
about where the problem is, and was able to produce a testcase that
demonstrates the problem.
If I create a program that explicitly refers to the symbol __gp, and has
such a large text section that there are out-of-range calls and thus
requires -relax, then the value of __gp ends up wrong after the link.
The IA-64 linker script uses PROVIDE to set the value of __gp. There is code
in file ld/ldexp.c function exp_fold_tree case etree_provide that tries to
make sure that we don't set the value if an object file already set the value.
The net effect however is that the value can only be set once.
When relaxation is enabled, there is code that repeatedly calls
lang_size_sections and lang_do_assignments. This is in file ld/ldlang.c
function lang_process, in the command_line.relax if statement. The first
time we call lang_size_sections, we give __gp a value. Then we make the
text section 48 bytes bigger to handle each out-of-range branch. This in turn
increases the start address of the data section which immediately follows
text section mod the max page size (or something like that). Next time we
compute a value for __gp, it is bigger, but we don't use the value because
__gp has already been assigned to once already, and a provided value can only
be assigned to once. Now __gp is wrong.
If you have a big enough gp section, and/or enough out-of-range calls, then
after linker is done the gp value won't cover the small data section. The
Intel testcases are very large commercial applications, and this is IA-64
code we are talking about, which makes them even larger.
I think we need some way to tell whether a provided value was defined by an
object file or defined by an earlier call to exp_fold_tree. In the latter
case, we do want to replace the old value with the new value. In the former
case, we do not want to change the value.
Or perhaps we can defer assigning a value until we are done relaxing.
I don't know if this is safe though, since if symbol values can affect section
sizes then it won't work.
Here is a small testcase. When I run this on an ia64-linux machine I get
objdump -x tmp | grep .got
16 .got 00000078 6000000000008400 6000000000008400 01f58400 2**3
6000000000008400 l d .got 0000000000000000
6000000000008400 g O .got 0000000000000000 _GLOBAL_OFFSET_TABLE_
objdump -x tmp | grep __gp
60000000002083d0 g O *ABS* 0000000000000000 __gp
Notice that the __gp value is 48 bytes too small, because the linker script
sets it to .got + 0x200000.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2001-02-12 20:28 PST by <wilson@bletchleypark.cygnus.com>.
# Source directory was `/blp/wilson/tmp2'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 205 -rw-rw-r-- Makefile
# 229 -rw-rw-r-- gen.c
# 52 -rw-rw-r-- tmp.c
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
set `$dir/gettext --version 2>&1`
if test "$3" = GNU
then
gettext_dir=$dir
fi
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
if mkdir _sh20963; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= Makefile ==============
if test -f 'Makefile' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'Makefile' '(file already exists)'
else
$echo 'x -' extracting 'Makefile' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
all: tmp
X objdump -x tmp | grep .got
X objdump -x tmp | grep __gp
X
gen: gen.c
X
tmp2.c: gen
X ./gen > tmp2.c
X
tmp: tmp.o tmp2.o
X gcc -o tmp -Wl,-relax tmp.o tmp2.o
X
clean:
X rm -rf tmp.o tmp2.o tmp2.c tmp gen
SHAR_EOF
(set 20 01 02 12 20 16 10 'Makefile'; eval "$shar_touch") &&
chmod 0664 'Makefile' ||
$echo 'restore of' 'Makefile' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'Makefile:' 'MD5 check failed'
24758ae25fc8e607896f2f9d3364e061 Makefile
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'Makefile'`"
test 205 -eq "$shar_count" ||
$echo 'Makefile:' 'original size' '205,' 'current size' "$shar_count!"
fi
fi
# ============= gen.c ==============
if test -f 'gen.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'gen.c' '(file already exists)'
else
$echo 'x -' extracting 'gen.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'gen.c' &&
main()
{
X int i;
X
X printf ("\
main()\n\
{\n\
X int i = 0;\n");
X
X for (i = 0; i < 1000; i++)
X {
X printf (" asm (\".align 32768\");\n");
X printf (" i++;\n");
X }
X
X printf ("\
X sub ();\n\
}\n");
X
X return 0;
}
SHAR_EOF
(set 20 01 02 12 20 16 17 'gen.c'; eval "$shar_touch") &&
chmod 0664 'gen.c' ||
$echo 'restore of' 'gen.c' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'gen.c:' 'MD5 check failed'
7671dae38416ba3f026f0d9502357385 gen.c
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'gen.c'`"
test 229 -eq "$shar_count" ||
$echo 'gen.c:' 'original size' '229,' 'current size' "$shar_count!"
fi
fi
# ============= tmp.c ==============
if test -f 'tmp.c' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'tmp.c' '(file already exists)'
else
$echo 'x -' extracting 'tmp.c' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'tmp.c' &&
extern int __gp;
int gb;
X
sub ()
{
X return __gp;
}
SHAR_EOF
(set 20 01 02 12 19 51 54 'tmp.c'; eval "$shar_touch") &&
chmod 0664 'tmp.c' ||
$echo 'restore of' 'tmp.c' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo 'tmp.c:' 'MD5 check failed'
c63c0cc00850497bc8d41d1d2f935bf9 tmp.c
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'tmp.c'`"
test 52 -eq "$shar_count" ||
$echo 'tmp.c:' 'original size' '52,' 'current size' "$shar_count!"
fi
fi
rm -fr _sh20963
exit 0
Jim