Recently some of our code started misbehaving only when compiled with the GCC compiler. Our post mortem stacktrace and data collection tools didn’t deal with this trap very gracefully, and dealing with that (or even understanding it) is a different story.
What I see in the debugger once I find the guilty thread is:
(gdb) thread 12
[Switching to thread 12 (Thread 46970517317952 (LWP 30316))]#0 0x00002ab824438ec1 in __gxx_personality_v0 ()
at ../../../../gcc-4.2.2/libstdc++-v3/libsupc++/eh_personality.cc:351
351 ../../../../gcc-4.2.2/libstdc++-v3/libsupc++/eh_personality.cc: No such file or directory.
in ../../../../gcc-4.2.2/libstdc++-v3/libsupc++/eh_personality.cc
(gdb) where
#0 0x00002ab824438ec1 in __gxx_personality_v0 ()
at ../../../../gcc-4.2.2/libstdc++-v3/libsupc++/eh_personality.cc:351
#1 0x00002ab824438cc9 in sleep () from /lib64/libc.so.6
#2 0x00002ab8203090ee in sqloEDUSleepHandler (signum=20, sigcode=0x2ab82cffa0c0, scp=0x2ab82cff9f90)
at sqloinst.C:283
#3
#4 0x00002ab81cf03231 in __gxx_personality_v0 ()
at ../../../../gcc-4.2.2/libstdc++-v3/libsupc++/eh_personality.cc:351
#5 0x00002ab823b9b745 in ossSleep () from /home/hotel74/peeterj/sqllib/lib64/libdb2osse.so.1
#6 0x00002ab821206992 in pdInvokeCalloutScript () at /view/peeterj_m19/vbs/engn/include/sqluDMSort_inlines.h:158
#7 0x00002ab82030fe99 in sqloEDUCodeTrapHandler (signum=4, sigcode=0x2ab82cffcc60, scp=0x2ab82cffcb30)
at sqloedu.C:4476
#8
#9 0x00002ab821393257 in sqluInitLoadEDU (pPrivateACBIn=0x2059e0080, ppPrivateACBOut=0x2ab82cffd320,
puchAuthID=0x2ab8fcef19b8 "PEETERJ ", pNLSACB=0x2ab8fceea168, pComCB=0x2ab8fceea080, pMemPool=0x2ab8fccca2d0)
at sqluedus.C:1696
#10 0x00002ab8212d34c2 in sqluldat (pArgs=0x2ab82cffdef0 "", argsSize=96) at sqluldat.C:737
#11 0x00002ab820310ced in sqloEDUEntry (parms=0x2ab82f3e9680) at sqloedu.C:3438
#12 0x00002ab81cefc143 in start_thread () from /lib64/libpthread.so.0
#13 0x00002ab82446674d in clone () from /lib64/libc.so.6
#14 0x0000000000000000 in ?? ()
Observe that there are two sets of ” frames. One from the original SIGILL, and another one that our “main” thread ends up sending to all the rest of the threads as part of our process for freezing things to be able to take a peek and see what’s up.
Looking at the siginfo_t for the SIGILL handler we have:
(gdb) frame 7
#7 0x00002ab82030fe99 in sqloEDUCodeTrapHandler (signum=4, sigcode=0x2ab82cffcc60, scp=0x2ab82cffcb30)
at sqloedu.C:4476
4476 sqloedu.C: No such file or directory.
in sqloedu.C
(gdb) p *sigcode
$4 = {si_signo = 4, si_errno = 0, si_code = 2, _sifields = {_pad = {557396567, 10936, 0, 0, 1, 16777216,
-1170923664, 10936, 754961616, 10936, 599153081, 10936, 0, 0, 15711488, 10752, 4, 0, -1170923664, 10936, 1, 0,
0, 0, 754961680, 10936, 4292335, 0}, _kill = {si_pid = 557396567, si_uid = 10936}, _timer = {
si_tid = 557396567, si_overrun = 10936, si_sigval = {sival_int = 0, sival_ptr = 0x0}}, _rt = {
si_pid = 557396567, si_uid = 10936, si_sigval = {sival_int = 0, sival_ptr = 0x0}}, _sigchld = {
si_pid = 557396567, si_uid = 10936, si_status = 0, si_utime = 72057594037927937, si_stime = 46972886392688},
_sigfault = {si_addr = 0x2ab821393257}, _sigpoll = {si_band = 46970319745623, si_fd = 0}}}
(gdb) p /x *sigcode
$5 = {si_signo = 0x4, si_errno = 0x0, si_code = 0x2, _sifields = {_pad = {0x21393257, 0x2ab8, 0x0, 0x0, 0x1,
0x1000000, 0xba351f70, 0x2ab8, 0x2cffccd0, 0x2ab8, 0x23b659b9, 0x2ab8, 0x0, 0x0, 0xefbd00, 0x2a00, 0x4, 0x0,
0xba351f70, 0x2ab8, 0x1, 0x0, 0x0, 0x0, 0x2cffcd10, 0x2ab8, 0x417eef, 0x0}, _kill = {si_pid = 0x21393257,
si_uid = 0x2ab8}, _timer = {si_tid = 0x21393257, si_overrun = 0x2ab8, si_sigval = {sival_int = 0x0,
sival_ptr = 0x0}}, _rt = {si_pid = 0x21393257, si_uid = 0x2ab8, si_sigval = {sival_int = 0x0,
sival_ptr = 0x0}}, _sigchld = {si_pid = 0x21393257, si_uid = 0x2ab8, si_status = 0x0,
si_utime = 0x100000000000001, si_stime = 0x2ab8ba351f70}, _sigfault = {si_addr = 0x2ab821393257}, _sigpoll = {
si_band = 0x2ab821393257, si_fd = 0x0}}}
This has got the si_addr value 0x00002AB821393257, which also matches frame 9 in the stack for sqluInitLoadEDU. What was at that line of code, doesn’t appear to be something that ought to generate a SIGILL:
1693 // Set current activity in private agent CB to
1694 // point to the activity that the EDU is working
1695 // on behalf of.
1696 pPrivateACB->agtRqstCB.pActivityCB = pComCB->my_curr_activity_entry;
1697 #ifdef DB2_DEBUG
1698 { //!! This debug code is only useful in conjunction with a trap described by W749645
1699 char mesg[500];
1700 sprintf(mesg,"W749645:uILE pPr->agtR=%p ->pAct=%p",pPrivateACB->agtRqstCB,pPrivateACB->agtRqstCB.pActivi tyCB);
1701 sqlt_logerr_str(SQLT_SQLU, SQLT_sqluInitLoadEDU, __LINE__, mesg, NULL, 0, SQLT_FFSL_INF);
1702 } //!!
1703 #endif
So what is going on? Let’s look at the assembly for the trapping instruction address. Using ‘(gdb) set logging on’, and ‘(gdb) disassemble’ we find:
0x00002ab82139323c
0x00002ab82139323e : mov 0xfffffffffffffd68(%rbp),%rax
0x00002ab821393245 : mov 0x6498(%rax),%rdx
0x00002ab82139324c : mov 0xffffffffffffffb0(%rbp),%rax
0x00002ab821393250 : mov %rdx,0x5bd0(%rax)
0x00002ab821393257 : ud2a
^^^^^^^^^^^^^^^^^^
0x00002ab821393259 : cmpl $0x0,0xffffffffffffffac(%rbp)
0x00002ab82139325d
0x00002ab82139325f : mov 0xfffffffffffffd80(%rbp),%rdi
0x00002ab821393266 : callq 0x2ab81dcd4218
0x00002ab82139326b : mov 0xffffffffffffffd8(%rbp),%rax
0x00002ab82139326f : and $0x82,%eax
0x00002ab821393274 : test %rax,%rax
Hmm. What is a ud2a instruction? Google is our friend and we find that the linux kernel uses this as a “guaranteed invalid instruction”. It is used to fault the processor and halt the kernel in case you did something really really bad.
Other similar references can be found, also explaining the use in the linux kernel. So what is this doing in userspace code? It seems like something too specific to get there by accident and since the instruction stream itself contains this stack corruption or any other sneaky nasty mechanism doesn’t seem likely. The instruction doesn’t immediately follow a callq, so a runtime loader malfunction or something else equally odd doesn’t seem likely.
Perhaps the compiler put this instruction into the code for some reason. A compiler bug perhaps? A new google search for GCC ud2a instruction finds me
...generates this warning (using gcc 4.4.1 but I think it applies to most
gcc versions):
main.cpp:12: warning: cannot pass objects of non-POD type .class A.
through .....; call will abort at runtime
1. Why is this a "warning" rather than an "error"? When I run the program
it hits a "ud2a" instruction emitted by gcc and promptly hits SIGILL.
Oh my! It sounds like GCC has cowardly refused to generate an error, but also bravely refuses to generate bad code for whatever this code sequence is. Do I have such an error in my build log? In fact, I have three, all of which look like:
sqluedus.C:1464: warning: deprecated conversion from string constant to 'char*'
sqluedus.C:1700: warning: cannot pass objects of non-POD type 'struct sqlrw_request_cb' through '...'; call will abort at runtime
At 1700 of that file we have:
sprintf(mesg,"W749645:uILE pPr->agtR=%p ->pAct=%p",pPrivateACB->agtRqstCB,pPrivateACB->agtRqstCB.pActivityCB);
It turns out that agtRqstCB is a rather large structure, and certainly doesn’t match the %p that the developer used in this debug build special code. The debug code actually makes things worse, and certainly won’t help on any platform. It probably also won’t crash on any platform either (except when using the GCC compiler) since there are no subsequent %s format parameters that will get messed up by placing gob-loads of structure data in the varargs data area inappropriately.
This should resolve this issue and allow me to go back to avoiding the (much slower!) intel compiler that is used by our nightly build process.