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