This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

bad interaction between PIC and linkonce sections on linux/ia86



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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]