NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

gprof SEGV issue for GCC-4.5.X toolchain.



Dear All,

I have build the NetBSD-5.1 library by compiling it for ARMv7a (Cortex
architecture)
using the GNU GCC-4.5 toolchain.

Now when I tried to build a sample scenario "hello.c" with profiling
options "-pg" for gprof analysis.
The following undefined reference error was noticed.

$ cat hello.c

{{{
 void hello(int i)
 {

 }

 int main(void)
 {
        hello(1);
 }


}}}

$ gcc -march=armv7-a -pg hello.c -o hello.exe -lgprof
{{{
/tmp/ccyXENx3.o: In function `hello':
hello.c:(.text+0x8): undefined reference to `__gnu_mcount_nc'
/tmp/ccyXENx3.o: In function `main':
hello.c:(.text+0x30): undefined reference to `__gnu_mcount_nc'
collect2: ld returned 1 exit status

}}}


Initial investigation shows that GCC now uses a new EABI-compatible
profiling interface for EABI targets.
This requires a function "__gnu_mcount_nc".

I have checked that GCC 4.3.3 does not use the implementaiton of
"__gnu_mcount_nc", it uses old "_mcount" implementation.
But in gcc 4.4.x and later the name and calling convention for
function profiling on ARM changed.
Now the new implementation uses "__gnu_mcount_nc".
So the solution to resolve this issue is to add this new profiling
hook "__gnu_mcount_nc" into NetBSD library.

The fix is:
$ cat src/sys/arch/arm/include/profile.h
{{{
-#define MCOUNT_ASM_NAME "mcount"
+#define MCOUNT_ASM_NAME "__gnu_mcount_nc"

}}}

With this fix "hello.c" was built and the build issue is resolved:

$ gcc -march=armv7-a -pg hello.c -o hello.exe -lgprof

Execution on Target (BeagleBoard) gives SEGV as follows:

{{{
-bash-3.2# gdb ./hello
(gdb) r
Starting program: /home/amol/hello

Program received signal SIGSEGV, Segmentation fault.
0x00010648 in _GLOBAL_OFFSET_TABLE_ ()
(gdb) bt
#0  0x00010648 in _GLOBAL_OFFSET_TABLE_ ()
#1  0x00010648 in _GLOBAL_OFFSET_TABLE_ ()
}}}

I have further investigated this issue as follows:

-bash-3.2# gdb ./hello
{{{
(gdb) b main
Breakpoint 1 at 0x85a0
(gdb) b hello
Breakpoint 2 at 0x857c
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/amol/work_armv7a
Breakpoint 1, 0x000085a0 in main ()
(gdb) c
Continuing.

Breakpoint 2, 0x0000857c in hello ()
(gdb) si
0x00008580 in hello ()
(gdb)
0x00008584 in hello ()
(gdb) disassemble
Dump of assembler code for function hello:
   0x0000857c <+0>:     push    {r11}           ; (str r11, [sp, #-4]!)
   0x00008580 <+4>:     add     r11, sp, #0
=> 0x00008584 <+8>:     sub     sp, sp, #12
   0x00008588 <+12>:    push    {lr}
   0x0000858c <+16>:    bl      0x83bc <__gnu_mcount_nc>
   0x00008590 <+20>:    str     r0, [r11, #-8]
   0x00008594 <+24>:    add     sp, r11, #0
   0x00008598 <+28>:    pop     {r11}
   0x0000859c <+32>:    bx      lr
End of assembler dump.

(gdb)
0x4003f45c in __gnu_mcount_nc () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb) disassemble
Dump of assembler code for function __gnu_mcount_nc:
=> 0x4003f45c <+0>:     push    {r0, r1, r2, r3, r12, lr}
   0x4003f460 <+4>:     teq     r0, r0
   0x4003f464 <+8>:     teq     pc, pc
   0x4003f468 <+12>:    ldr     r0, [r11, #-4]
   0x4003f46c <+16>:    moveq   r1, lr
   0x4003f470 <+20>:    bicne   r1, lr, #-67108861      ; 0xfc000003
   0x4003f474 <+24>:    bl      0x4003ea70
   0x4003f478 <+28>:    pop     {r0, r1, r2, r3, lr, pc}
(gdb) si
0x4003f460 in __gnu_mcount_nc () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb) i r
r0             0x1      1
r1             0xbe9c2da4       -1097060956
r2             0xbe9c2dac       -1097060948
r3             0x85a0   34208
r4             0xbe9c2da4       -1097060956
r5             0xbe9c2dac       -1097060948
r6             0x1      1
r7             0x84f0   34032
r8             0x40039000       1073975296
r9             0x0      0
r10            0x40031f40       1073946432
r11            0xbe9c2d58       -1097061032
r12            0x106d8  67288
sp             0xbe9c2d30       0xbe9c2d30
lr             0x8590   34192
pc             0x4003f460       0x4003f460 <__gnu_mcount_nc+4>
cpsr           0xa0000010       -1610612720
(gdb) x/8xw $sp
0xbe9c2d30:     0x00000001      0xbe9c2da4      0xbe9c2dac      0x000085a0
0xbe9c2d40:     0x000106d8      0x00008590      0x000085b8      0xbe9c2dac
(gdb) si
(gdb)
0x4003ea70 in ?? () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb)
0x4003ea74 in ?? () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb)
0x4003ea78 in ?? () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb)
0x4003f47c in _mcount () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb)
0x4003f478 in __gnu_mcount_nc () from /home/amol/TEST_GPROF/lib/libgprof.so
(gdb) disassemble
Dump of assembler code for function __gnu_mcount_nc:
   0x4003f45c <+0>:     push    {r0, r1, r2, r3, r12, lr}
   0x4003f460 <+4>:     teq     r0, r0
   0x4003f464 <+8>:     teq     pc, pc
   0x4003f468 <+12>:    ldr     r0, [r11, #-4]
   0x4003f46c <+16>:    moveq   r1, lr
   0x4003f470 <+20>:    bicne   r1, lr, #-67108861      ; 0xfc000003
   0x4003f474 <+24>:    bl      0x4003ea70
=> 0x4003f478 <+28>:    pop     {r0, r1, r2, r3, lr, pc}
(gdb) x/8xw $sp
0xbe9c2d30:     0x00000001      0xbe9c2da4      0xbe9c2dac      0x000085a0
0xbe9c2d40:     0x000106d8      0x00008590      0x000085b8      0xbe9c2dac
(gdb) x 0x000085a0
0x85a0 <main>:  0xe92d4800
  ===> return address of main() function
(gdb) x 0x000106d8
0x106d8 <_GLOBAL_OFFSET_TABLE_+20>:     0x4003f45c  ===> GOT address
(gdb) x 0x00008590
0x8590 <hello+20>:      0xe50b0008
 ===> return address of hello() function
(gdb) si
0x00008590 in hello ()
(gdb) i r
r0             0x1      1
r1             0xbe9c2da4       -1097060956
r2             0xbe9c2dac       -1097060948
r3             0x85a0   34208
r4             0xbe9c2da4       -1097060956
r5             0xbe9c2dac       -1097060948
r6             0x1      1
r7             0x84f0   34032
r8             0x40039000       1073975296
r9             0x0      0
r10            0x40031f40       1073946432
r11            0xbe9c2d58       -1097061032
r12            0x4004843c       1074037820
sp             0xbe9c2d48       0xbe9c2d48
lr             0x106d8  67288
pc             0x8590   0x8590 <hello+20>
cpsr           0x20000010       536870928
(gdb) si
0x00008594 in hello ()
(gdb) si
0x00008598 in hello ()
(gdb) si
0x0000859c in hello ()
(gdb) disassemble
Dump of assembler code for function hello:
   0x0000857c <+0>:     push    {r11}           ; (str r11, [sp, #-4]!)
   0x00008580 <+4>:     add     r11, sp, #0
   0x00008584 <+8>:     sub     sp, sp, #12
   0x00008588 <+12>:    push    {lr}
   0x0000858c <+16>:    bl      0x83bc <__gnu_mcount_nc>
   0x00008590 <+20>:    str     r0, [r11, #-8]
   0x00008594 <+24>:    add     sp, r11, #0
   0x00008598 <+28>:    pop     {r11}
=> 0x0000859c <+32>:    bx      lr
(gdb) x $lr
0x106d8 <_GLOBAL_OFFSET_TABLE_+20>:     0x4003f45c
(gdb) si
0x000106d8 in _GLOBAL_OFFSET_TABLE_ ()
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
0x000106d8 in _GLOBAL_OFFSET_TABLE_ ()
}}}


From the above debug log it is confirmed that “lr” contains
“_GLOBAL_OFFSET_TABLE_” address
instead of return address of main() function. And when hello()
function returns by calling "bx  lr",
"PC" points to GOT, causes SEGV.

FIX:
-----

To resolve this issue the MCOUNT implementation needs to be modify as follows.

$ cat src/sys/arch/arm/include/profile.h

{{{
@@ -38,7 +38,7 @@
  * prologue.
  */


-#define MCOUNT_ASM_NAME "__mcount"
+#define MCOUNT_ASM_NAME "__gnu_mcount_nc"
 #ifdef PIC
 #define        PLTSYM          "(PLT)"
 #endif
@@ -56,7 +56,7 @@
        /*                                                              \
         * Preserve registers that are trashed during mcount            \
         */                                                             \
-       __asm("stmfd    sp!, {r0-r3, ip, lr}");                         \
+       __asm("stmfd    sp!, {r0-r3, lr}");                             \
        /* Check what mode we're in.  EQ => 32, NE => 26 */             \
        __asm("teq      r0, r0");                                       \
        __asm("teq      pc, r15");                                      \
@@ -80,7 +80,8 @@
        /*                                                              \
         * Restore registers that were trashed during mcount            \
         */                                                             \
-       __asm("ldmfd    sp!, {r0-r3, lr, pc}");
+       __asm("ldmfd    sp!, {r0-r3, ip, lr}");                         \
+       __asm("bx ip");


 #ifdef _KERNEL
 #ifdef __PROG26
}}}


In MCOUNT , “r12” is not required to save on stack. Since after return
from "_mcount", it should be
pop with return address i.e. the value of “lr” register which was
pushed before call to "_mcount".
Similarly, “ lr” needs to poped with the return address of main() function.
Then “r12” register contains move into “pc” for returning  back to
hello() function.

With this fix patch the gmon profiling working fine without any issue.

Please review the fix patch and let me know if there are any issues.

Thank You,
Amol Pise


Home | Main Index | Thread Index | Old Index