This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
bad interaction between PIC and linkonce sections on linux/ia86
- To: bug-gcc at gnu dot org
- Subject: bad interaction between PIC and linkonce sections on linux/ia86
- From: scott snyder <snyder at fnal dot gov>
- Date: Wed, 01 Mar 2000 16:36:58 CST
hi -
In my continuing attempts to build libstdc++ v3 with a recent version
of g++, i've run into what seems to be a bad interaction between
PIC code and the use of linkonce sections by template instantiation.
This is relevant to the cvs version of g++ (2.96 20000229) on a
i686-pc-linux-gnu platform. Note that this problem depends
on the machine subtype -- it only shows up if TARGET_DEEP_BRANCH_PREDICTION
is on, i.e., i686, k6, and athalon. The build script in the example
below tries to force this by adding `-mpentiumpro' to the compilation.
I've managed to produce a self-contained example which demonstrates
the problem. Since it consists of several files, i've appended
it in shar format. Source the script `build' to produce a libstdc++.so
and the executable x.x. Put the current directory at the head
of LD_LIBRARY_PATH (eg, `export LD_LIBRARY_PATH=.') and run `x.x'
under gdb. It crashes as follows:
Starting program: /home/sss/sobug3/x.x
Program received signal SIGSEGV, Segmentation fault.
malloc_alloc_template<0>::allocate () at ystring.h:9
9 return malloc(20);
(gdb) where
#0 malloc_alloc_template<0>::allocate () at ystring.h:9
#1 0x1373cbf in ?? ()
#2 0x400137a7 in __do_global_ctors_aux () at misc-inst.cc:11
#3 0x40013512 in _init () at ystring.h:9
(gdb) disass
Dump of assembler code for function allocate__t21malloc_alloc_template1i0:
0x40013004 <allocate__t21malloc_alloc_template1i0>: add %eax,(%ecx)
0x40013006 <allocate__t21malloc_alloc_template1i0+2>: add %eax,(%eax)
0x40013008 <allocate__t21malloc_alloc_template1i0+4>: add %al,(%eax)
0x4001300a <allocate__t21malloc_alloc_template1i0+6>: add %al,(%eax)
Note that we seem to be jumping to the very start of the .so:
(gdb) info shared
From To Syms Read Shared Object Library
0x40013000 0x40014a68 Yes /home/sss/sobug3/./libstdc++.so
0x4001c000 0x40037fd8 Yes /lib/libm.so.6
0x40038000 0x4012a8dc Yes /lib/libc.so.6
0x40000000 0x400126b0 Yes /lib/ld-linux.so.2
Here's what i think is going wrong.
It comes about due to the interaction of two features.
First, on this platform, when templates are instantiated, the code
is put in a `linkonce' object section, in each object file where the
template is implicitly instantiated. The linker then combines
all the `linkonce' sections with a given name -- (i think) by keeping
the first one it encounters and discarding the rest. This works
as long as the contents of the linkonce section are identical in
all objects.
Second, for PIC code, we need to get the PC into ebx.
On older processors, this was implemented by a sequence like:
call Ltmp
Ltmp: pop %ebx
But on newer processors -- specifically those with ``deep branch
prediction'' -- this is implemented like this instead:
LPR0: mov (%esp), %ebx
ret
...
call LPR0
An important point is that only one copy of the `LPR0' stub is generated
in a given compilation unit; all functions in a compilation unit reference
the same stub.
The problem is that in some cases, the LPR0 stub may be emitted in a linkonce
section. Further, it may not be emitted in linkonce sections of the
same name in a different compilation unit. I think this is the root of the
problem (although i have not fully understood why the linker behaves like
it does -- changing the LPR0 call to offset 0).
For example, here's an extract of the generated code from misc-inst.cc:
.section .gnu.linkonce.t.allocate__t21malloc_alloc_template1i0,"ax",@progbits
.align 16
.LPR0:
movl (%esp), %ebx
ret
.weak allocate__t21malloc_alloc_template1i0
.type allocate__t21malloc_alloc_template1i0,@function
allocate__t21malloc_alloc_template1i0:
.LFB1:
.LSM0:
.LBB2:
.LSM1:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
pushl %ebx
.LCFI2:
subl $20, %esp
.LCFI3:
call .LPR0
addl $_GLOBAL_OFFSET_TABLE_, %ebx
And here's the start of the section of the same name from locale-inst.s:
.section .gnu.linkonce.t.allocate__t21malloc_alloc_template1i0,"ax",@progbits
.align 16
.weak allocate__t21malloc_alloc_template1i0
.type allocate__t21malloc_alloc_template1i0,@function
allocate__t21malloc_alloc_template1i0:
.LFB2:
.LSM0:
.LBB3:
.LSM1:
pushl %ebp
.LCFI4:
movl %esp, %ebp
.LCFI5:
pushl %ebx
.LCFI6:
subl $20, %esp
.LCFI7:
call .LPR0
addl $_GLOBAL_OFFSET_TABLE_, %ebx
For now, i can work around this problem by simply using -mpentium when
compiling. But perhaps the proper solution is to give each routine in
a linkonce section its own copy of the PIC stub.
thanks,
sss
#!/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 2000-03-01 03:29 CST by <sss@karma>.
# Source directory was `/home/sss/sobug3'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 214 -rw-rw-r-- build
# 79 -rw-rw-r-- locale-inst.cc
# 114 -rw-rw-r-- misc-inst.cc
# 27 -rw-rw-r-- x.cc
# 159 -rw-rw-r-- ystring.h
#
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 _sh12977; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= build ==============
if test -f 'build' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'build' '(file already exists)'
else
$echo 'x -' extracting 'build' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'build' &&
c++ -mpentiumpro -ggdb -c -fPIC misc-inst.cc -o misc-inst.o
c++ -mpentiumpro -ggdb -c -fPIC locale-inst.cc -o locale-inst.o
X
gcc -shared locale-inst.o misc-inst.o -o libstdc++.so
X
g++ -g -o x.x x.cc -L. -lstdc++
SHAR_EOF
(set 20 00 03 01 03 29 02 'build'; eval "$shar_touch") &&
chmod 0664 'build' ||
$echo 'restore of' 'build' '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 'build:' 'MD5 check failed'
37735b41ebad378d0919094ccd6e97ac build
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'build'`"
test 214 -eq "$shar_count" ||
$echo 'build:' 'original size' '214,' 'current size' "$shar_count!"
fi
fi
# ============= locale-inst.cc ==============
if test -f 'locale-inst.cc' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'locale-inst.cc' '(file already exists)'
else
$echo 'x -' extracting 'locale-inst.cc' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'locale-inst.cc' &&
#include "ystring.h"
X
void bar ()
{
X malloc_alloc_template<0>::allocate ();
}
SHAR_EOF
(set 20 00 03 01 03 26 02 'locale-inst.cc'; eval "$shar_touch") &&
chmod 0664 'locale-inst.cc' ||
$echo 'restore of' 'locale-inst.cc' '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 'locale-inst.cc:' 'MD5 check failed'
9c97be54e5d6e15b4480742ab408f149 locale-inst.cc
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'locale-inst.cc'`"
test 79 -eq "$shar_count" ||
$echo 'locale-inst.cc:' 'original size' '79,' 'current size' "$shar_count!"
fi
fi
# ============= misc-inst.cc ==============
if test -f 'misc-inst.cc' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'misc-inst.cc' '(file already exists)'
else
$echo 'x -' extracting 'misc-inst.cc' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'misc-inst.cc' &&
X
#include "ystring.h"
X
template class malloc_alloc_template<0>;
X
class foo
{
public:
X foo() {}
};
foo cfileinit;
SHAR_EOF
(set 20 00 03 01 03 26 02 'misc-inst.cc'; eval "$shar_touch") &&
chmod 0664 'misc-inst.cc' ||
$echo 'restore of' 'misc-inst.cc' '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 'misc-inst.cc:' 'MD5 check failed'
e8c78dca437faaa1739193c83dc25668 misc-inst.cc
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'misc-inst.cc'`"
test 114 -eq "$shar_count" ||
$echo 'misc-inst.cc:' 'original size' '114,' 'current size' "$shar_count!"
fi
fi
# ============= x.cc ==============
if test -f 'x.cc' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'x.cc' '(file already exists)'
else
$echo 'x -' extracting 'x.cc' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'x.cc' &&
int main()
{
X return 0;
}
SHAR_EOF
(set 20 00 03 01 03 26 02 'x.cc'; eval "$shar_touch") &&
chmod 0664 'x.cc' ||
$echo 'restore of' 'x.cc' '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 'x.cc:' 'MD5 check failed'
d1035b543709f23158df094b6d49742b x.cc
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'x.cc'`"
test 27 -eq "$shar_count" ||
$echo 'x.cc:' 'original size' '27,' 'current size' "$shar_count!"
fi
fi
# ============= ystring.h ==============
if test -f 'ystring.h' && test "$first_param" != -c; then
$echo 'x -' SKIPPING 'ystring.h' '(file already exists)'
else
$echo 'x -' extracting 'ystring.h' '(text)'
sed 's/^X//' << 'SHAR_EOF' > 'ystring.h' &&
extern "C" void* malloc (unsigned);
X
template <int __inst>
class malloc_alloc_template {
public:
X
X static void* allocate()
X {
X return malloc(20);
X }
X
};
SHAR_EOF
(set 20 00 03 01 03 26 14 'ystring.h'; eval "$shar_touch") &&
chmod 0664 'ystring.h' ||
$echo 'restore of' 'ystring.h' '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 'ystring.h:' 'MD5 check failed'
724f6162b16b3296fa1a642d184ffdf6 ystring.h
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'ystring.h'`"
test 159 -eq "$shar_count" ||
$echo 'ystring.h:' 'original size' '159,' 'current size' "$shar_count!"
fi
fi
rm -fr _sh12977
exit 0