When compiling PIC code for the this-adjusting thunk for a method of a class
with two or more base classes, the sparc_output_mi_thunk() function fails to
preserve the pic register itself (%l7) when using load_pic_register() to find
the address of the thunk's target. If the thunk is invoked from a different load
object (with a different GOT) than the one in which the thunk is emitted, the
pic register %l7 of the caller of the thunk is clobbered, causing subsequent use
of the GOT in the caller to fail. In order to make the problem evident, the
source code file below is compiled into two modules, one a shared library which
contains the non-virtual thunk to the destructor of the MI class, and the other
one as the executable main which demonstrates that the pic register has changed
unexpectedly by the call to the destructor via a base class pointer.

% uname -a
SunOS mambo 5.8 Generic_117350-25 sun4u sparc SUNW,Sun-Blade-100

% cat bug.cpp
class B1
{
   public:
   virtual ~B1();
};

class B2
{
   public:
   virtual ~B2();
};

class MI : public B1, public B2
{
   public:
   virtual ~MI();
};

#ifdef LIB
int i = 0;

B1::~B1() {}

B2::~B2() {}

MI::~MI() {}
#endif

#ifdef MAIN
extern int i;

int main(int, char**)
{
   int* ip = &i;

   B2* b2 = new MI;

   delete b2;

   if (ip != &i) return 1;

   return 0;
}
#endif

% gcc -v -shared -fPIC -fno-delayed-branch -DLIB bug.cpp -o libbug.so
Reading specs from 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/specs
Target: sparc-sun-solaris2.8
Configured with: ../gcc-4.0.0/configure --prefix=/usr/local/pkg/gcc-4.0.0
--enable-languages=c,c++ : (reconfigured) ../gcc-4.0.0/configure
--prefix=/usr/local/pkg/gcc-4.0.0
--with-gcc-version-trigger=/usr/local/src/gcc-4.0.0/gcc/version.c
--enable-languages=c,c++ --with-gnu-as
--with-as=/usr/local/pkg/binutils-2.16/bin/as --with-gnu-ld
--with-ld=/usr/local/pkg/binutils-2.16/bin/ld
Thread model: posix
gcc version 4.0.0
 /usr/local/pkg/gcc-4.0.0/libexec/gcc/sparc-sun-solaris2.8/4.0.0/cc1plus -quiet
-v -DLIB bug.cpp -quiet -dumpbase bug.cpp -mcpu=v7 -auxbase bug -version -fPIC
-fno-delayed-branch -o /var/tmp//ccsoPzrP.s
ignoring duplicate directory "/usr/local/pkg/gcc-4.0.0/include"
ignoring nonexistent directory
"/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0/sparc-sun-solaris2.8
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0/backward
 /usr/local/pkg/gcc-4.0.0/include
 /usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/include
 /usr/include
End of search list.
GNU C++ version 4.0.0 (sparc-sun-solaris2.8)
        compiled by GNU C version 4.0.0.
GGC heuristics: --param ggc-min-expand=82 --param ggc-min-heapsize=98304
 /usr/local/pkg/binutils-2.16/bin/as -V -Qy -s -K PIC -xarch=v8 -o
/var/tmp//ccOSt3MC.o /var/tmp//ccsoPzrP.s
GNU assembler version 2.16 (sparc-sun-solaris2.8) using BFD version 2.16
 /usr/local/pkg/gcc-4.0.0/libexec/gcc/sparc-sun-solaris2.8/4.0.0/collect2 -V -G
-dy -z text -Y P,/usr/ccs/lib:/usr/lib -rpath-link /usr/lib -Qy -o libbug.so
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crti.o
/usr/ccs/lib/values-Xa.o
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtbegin.o
-L/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0 -L/usr/ccs/bin
-L/usr/ccs/lib
-L/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../..
/var/tmp//ccOSt3MC.o -lgcc_s -lgcc_s -R/usr/local/pkg/gcc-4.0.0/lib
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtend.o
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtn.o
GNU ld version 2.16
  Supported emulations:
   elf32_sparc
   elf64_sparc

% gcc -v -fPIC -fno-delayed-branch -DMAIN bug.cpp -L. -R. -lbug -lsupc++ -o bug
Reading specs from 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/specs
Target: sparc-sun-solaris2.8
Configured with: ../gcc-4.0.0/configure --prefix=/usr/local/pkg/gcc-4.0.0
--enable-languages=c,c++ : (reconfigured) ../gcc-4.0.0/configure
--prefix=/usr/local/pkg/gcc-4.0.0
--with-gcc-version-trigger=/usr/local/src/gcc-4.0.0/gcc/version.c
--enable-languages=c,c++ --with-gnu-as
--with-as=/usr/local/pkg/binutils-2.16/bin/as --with-gnu-ld
--with-ld=/usr/local/pkg/binutils-2.16/bin/ld
Thread model: posix
gcc version 4.0.0
 /usr/local/pkg/gcc-4.0.0/libexec/gcc/sparc-sun-solaris2.8/4.0.0/cc1plus -quiet
-v -DMAIN bug.cpp -quiet -dumpbase bug.cpp -mcpu=v7 -auxbase bug -version -fPIC
-fno-delayed-branch -o /var/tmp//ccaIczQW.s
ignoring duplicate directory "/usr/local/pkg/gcc-4.0.0/include"
ignoring nonexistent directory
"/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0/sparc-sun-solaris2.8
 
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../../../include/c++/4.0.0/backward
 /usr/local/pkg/gcc-4.0.0/include
 /usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/include
 /usr/include
End of search list.
GNU C++ version 4.0.0 (sparc-sun-solaris2.8)
        compiled by GNU C version 4.0.0.
GGC heuristics: --param ggc-min-expand=82 --param ggc-min-heapsize=98304
 /usr/local/pkg/binutils-2.16/bin/as -V -Qy -s -K PIC -xarch=v8 -o
/var/tmp//ccwjroiT.o /var/tmp//ccaIczQW.s
GNU assembler version 2.16 (sparc-sun-solaris2.8) using BFD version 2.16
 /usr/local/pkg/gcc-4.0.0/libexec/gcc/sparc-sun-solaris2.8/4.0.0/collect2 -V -R.
-Y P,/usr/ccs/lib:/usr/lib -rpath-link /usr/lib -Qy -o bug
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crt1.o
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crti.o
/usr/ccs/lib/values-Xa.o
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtbegin.o -L.
-L/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0 -L/usr/ccs/bin
-L/usr/ccs/lib
-L/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/../../..
/var/tmp//ccwjroiT.o -lbug -lsupc++ -lgcc -lgcc_eh -lc -lgcc -lgcc_eh -lc
-R/usr/local/pkg/gcc-4.0.0/lib
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtend.o
/usr/local/pkg/gcc-4.0.0/lib/gcc/sparc-sun-solaris2.8/4.0.0/crtn.o
GNU ld version 2.16
  Supported emulations:
   elf32_sparc
   elf64_sparc

% ./bug || echo bad
bad

As shown by the execution of the program above, the apparent address of global
variable 'i' has changed after the destructor is invoked via the thunk. In
non-trivial use, a segv or other nasty is more likely to occur when the wrong
GOT is indexed.

The problem is easily worked around by specifying -fdelayed-branch (or using an
optimization level which does so implicitly) in which case the normal sibcall
instruction sequence is generated.

The problem appears to be that in sparc_output_mi_thunk(), the following code
sequence for a no delayed branch pic thunk protects %o7 (which is clobbered by
the call to the pic helper), but fails to protect the pic register %l7 itself
which is obviously altered by the pic helper. Since the register window has not
been saved at this point, %l7 will be left altered upon the eventual return to
the caller of the thunk. Looks like the code should protect (and restore before
the jump) both %o7 and %l7.

      /* The hoops we have to jump through in order to generate a sibcall
         without using delay slots...  */
      rtx spill_reg, seq, scratch = gen_rtx_REG (Pmode, 1);

      if (flag_pic)
        {
          spill_reg = gen_rtx_REG (word_mode, 15);  /* %o7 */
          start_sequence ();
          /* Delay emitting the PIC helper function because it needs to
             change the section and we are emitting assembly code.  */
          load_pic_register (true);  /* clobbers %o7 */
          scratch = legitimize_pic_address (funexp, Pmode, scratch);
          seq = get_insns ();
          end_sequence ();
          emit_and_preserve (seq, spill_reg);
        }

-- 
           Summary: -fPIC -fno-delayed-branch miscompiles MI this_adjusting
                    thunks
           Product: gcc
           Version: 4.0.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: c++
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: scp at predict dot com
                CC: gcc-bugs at gcc dot gnu dot org
 GCC build triplet: 4.0.0
  GCC host triplet: sparc-sun-solaris2.8
GCC target triplet: sparc-sun-solaris2.8


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22260

Reply via email to