I've recently been pointed at PR target/38239, where trivial programs compiled/linked with -pg SEGV on 32-bit Solaris/x86 since _mcount clobbers %ecx. It was initially reported for gcc 4.3, but I couldn't reproduce it there and the testcase works fine up to and including gcc 4.8. Since 4.9, the SEGV reported occurs again.
Upon closer investigation (checking i386.c (x86_function_profiler), final.c (profile_function) and the _mcount implemtations of glibc and BSDs), it turns out that _mcount isn't called like a regular function, but is expected to save and restore call-clobbered registers itself. Unfortunately, there's no specification on how _mcount is expected to behave. The fix is trivial, of course. Bootstrapped without regressions on i386-pc-solaris2.1[012]. Will install on mainline soon, and on the 5 and 4.9 branches in about a week, given that this is a longstanding regression that makes -pg profiling unusable/unreliable. Rainer 2016-03-11 Rainer Orth <r...@cebitec.uni-bielefeld.de> PR target/38239 * config/sol2/gmon.c [__i386__] (_mcount): Save and restore call-clobbered registers. (internal_mcount): Remove __i386__ handling.
# HG changeset patch # Parent 2a1815d64dfb93b0ac1d59c094d4da5347634d38 Save call-clobbered registers in _mcount on 32-bit Solaris/x86 (PR target/38239) diff --git a/libgcc/config/sol2/gmon.c b/libgcc/config/sol2/gmon.c --- a/libgcc/config/sol2/gmon.c +++ b/libgcc/config/sol2/gmon.c @@ -44,11 +44,7 @@ extern void monstartup (char *, char *); extern void _mcleanup (void); -#ifdef __i386__ -static void internal_mcount (void) __attribute__ ((used)); -#else static void internal_mcount (char *, unsigned short *) __attribute__ ((used)); -#endif static void moncontrol (int); struct phdr { @@ -223,8 +219,19 @@ void /* Solaris 2 libraries use _mcount. */ #if defined __i386__ asm(".globl _mcount\n" + " .type _mcount, @function\n" "_mcount:\n" - " jmp internal_mcount\n"); + /* Save and restore the call-clobbered registers. */ + " pushl %eax\n" + " pushl %ecx\n" + " pushl %edx\n" + " movl 12(%esp), %edx\n" + " movl 4(%ebp), %eax\n" + " call internal_mcount\n" + " popl %edx\n" + " popl %ecx\n" + " popl %eax\n" + " ret\n"); #elif defined __x86_64__ /* See GLIBC for additional information about this technique. */ asm(".globl _mcount\n" @@ -299,32 +306,13 @@ asm(".global _mcount\n" #endif static void -#ifdef __i386__ -internal_mcount (void) -#else internal_mcount (char *selfpc, unsigned short *frompcindex) -#endif { struct tostruct *top; struct tostruct *prevtop; long toindex; static char already_setup; -#ifdef __i386__ - char *selfpc; - unsigned short *frompcindex; - - /* Find the return address for mcount and the return address for mcount's - caller. */ - - /* selfpc = pc pushed by mcount call. - This identifies the function that was just entered. */ - selfpc = (void *) __builtin_return_address (0); - /* frompcindex = pc in preceding frame. - This identifies the caller of the function just entered. */ - frompcindex = (void *) __builtin_return_address (1); -#endif - /* Only necessary without the Solaris CRTs or a proper gcrt1.o, otherwise crtpg.o or gcrt1.o take care of that.
# HG changeset patch # Parent bc0ec420e463ad63db543a27592dd7dca577b7ad Save call-clobbered registers in _mcount on 32-bit Solaris/x86 (PR target/38239) diff --git a/libgcc/config/gmon-sol2.c b/libgcc/config/gmon-sol2.c --- a/libgcc/config/gmon-sol2.c +++ b/libgcc/config/gmon-sol2.c @@ -43,11 +43,7 @@ extern void monstartup (char *, char *); extern void _mcleanup (void); -#ifdef __i386__ -static void internal_mcount (void) __attribute__ ((used)); -#else static void internal_mcount (char *, unsigned short *) __attribute__ ((used)); -#endif static void moncontrol (int); struct phdr { @@ -222,8 +218,19 @@ void /* Solaris 2 libraries use _mcount. */ #if defined __i386__ asm(".globl _mcount\n" + " .type _mcount, @function\n" "_mcount:\n" - " jmp internal_mcount\n"); + /* Save and restore the call-clobbered registers. */ + " pushl %eax\n" + " pushl %ecx\n" + " pushl %edx\n" + " movl 12(%esp), %edx\n" + " movl 4(%ebp), %eax\n" + " call internal_mcount\n" + " popl %edx\n" + " popl %ecx\n" + " popl %eax\n" + " ret\n"); #elif defined __x86_64__ /* See GLIBC for additional information about this technique. */ asm(".globl _mcount\n" @@ -298,32 +305,13 @@ asm(".global _mcount\n" #endif static void -#ifdef __i386__ -internal_mcount (void) -#else internal_mcount (char *selfpc, unsigned short *frompcindex) -#endif { struct tostruct *top; struct tostruct *prevtop; long toindex; static char already_setup; -#ifdef __i386__ - char *selfpc; - unsigned short *frompcindex; - - /* Find the return address for mcount and the return address for mcount's - caller. */ - - /* selfpc = pc pushed by mcount call. - This identifies the function that was just entered. */ - selfpc = (void *) __builtin_return_address (0); - /* frompcindex = pc in preceding frame. - This identifies the caller of the function just entered. */ - frompcindex = (void *) __builtin_return_address (1); -#endif - if(!already_setup) { extern char etext[];
-- ----------------------------------------------------------------------------- Rainer Orth, Center for Biotechnology, Bielefeld University