This post’s goal is to guide a starter to analysis a crash by reading into the assemble code.
But the example listed here is not a good one, because the crash point is not an obvious one, the real reason of the crash for this example is still remain uncovered.
My point here is, we can use a such kind of way to analysis some crash, and once you read this post, you can start the first step. If you run into any problems when analysis your crash, well, we can discuss wih them together here. Here we go.
As you know, you can get the call stack by adding a backtrace support for your app.
You can compile your app by adding -rdynamic, when the crash happened, you can use addr2line to locate the exact line of your source code which caused the crash. https://oddmeta.net/archives/1656.html
But when a formal product is released, we will not compile our components with -rdynamic, then we need to analysis the source code ourself.
Here is an example:
The crash report log which contains the call stack when the crash happened.
2015-7-17 16:37:28 ---------------------------------pid=31603,tid=32136<STACKAPP>------------------- ----------------SIGSEGV------------------- SI code = 1 (Address not mapped to object) Fault addr = 0x7 ----------------stack------------------- h323sadp() [0x806b9de] h323sadp() [0x806bc30] [0x2d5410] /opt/mcu/pas/libkdv323adapter.so(raAdd+0x3b) [0x51dbdb] /opt/mcu/pas/libkdv323adapter.so(+0x1436cf) [0x5206cf] /opt/mcu/pas/libkdv323adapter.so(rtAddBrother+0x35) [0x520765] /opt/mcu/pas/libkdv323adapter.so(+0x1107ef) [0x4ed7ef] /opt/mcu/pas/libkdv323adapter.so(pvtAdd+0x43) [0x4edac3] /opt/mcu/pas/libkdv323adapter.so(perDecodeChoice+0x2ad) [0x4bcf4d] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x557) [0x4bb1d7] /opt/mcu/pas/libkdv323adapter.so(perDecodeSequece+0x16d) [0x4bf2cd] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x50f) [0x4bb18f] /opt/mcu/pas/libkdv323adapter.so(perDecodeSequeceOF+0x1c0) [0x4bfe30] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x4e5) [0x4bb165] /opt/mcu/pas/libkdv323adapter.so(perDecodeSequece+0x1a1) [0x4bf301] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x50f) [0x4bb18f] /opt/mcu/pas/libkdv323adapter.so(perDecodeChoice+0x3b9) [0x4bd059] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x557) [0x4bb1d7] /opt/mcu/pas/libkdv323adapter.so(perDecodeChoice+0x318) [0x4bcfb8] /opt/mcu/pas/libkdv323adapter.so(perDecNode+0x557) [0x4bb1d7] /opt/mcu/pas/libkdv323adapter.so(+0xde634) [0x4bb634] /opt/mcu/pas/libkdv323adapter.so(emDecode+0xa2) [0x4ba962] /opt/mcu/pas/libkdv323adapter.so(cmEmDecode+0x5c) [0x4ba7cc] /opt/mcu/pas/libkdv323adapter.so(cmEvTransNewRawMessage+0x64) [0x4a4d24] /opt/mcu/pas/libkdv323adapter.so(decodeIncomingMessage+0x1b7) [0x5096a7] /opt/mcu/pas/libkdv323adapter.so(transH245Handler+0x32a) [0x50affa] /opt/mcu/pas/libkdv323adapter.so(tpktEvent+0x12a) [0x507d6a] /opt/mcu/pas/libkdv323adapter.so(Rv2SelectHandleEPollFds+0xe6) [0x527716] /opt/mcu/pas/libkdv323adapter.so(Rv2SelectWaitAndBlock+0x1bd) [0x527b1d] /opt/mcu/pas/libkdv323adapter.so(seliSelectUntil+0x44) [0x4e9a84] /opt/mcu/pas/libkdv323adapter.so(_Z15kdvCheckMessagev+0x32) [0x446462] h323sadp() [0x80687d5] /opt/mcu/nm/libosp.so(OspAppEntry+0x62c) [0x88648c] /opt/mcu/nm/libosp.so(+0x28e2d) [0x894e2d] /lib/libpthread.so.0() [0xd26b39] /lib/libc.so.6(clone+0x5e) [0x6dfd6e] ----------------context struct---------------- ct->uc_flags = 0 ct->uc_link = 0x(nil) ct->uc_sigmask = 00 ct->uc_stack.ss_sp = 0x(nil) ct->uc_stack.ss_flags = 0x2 ct->uc_stack.ss_size = 0x0
Get the libkdv323adapter.so and disassemble it, here is the code we get.
00140ba0 <raAdd>:
140ba0: 55 push %ebp
140ba1: 89 e5 mov %esp,%ebp
140ba3: 57 push %edi
140ba4: 56 push %esi
140ba5: 53 push %ebx
140ba6: 83 ec 1c sub $0x1c,%esp
140ba9: 8b 5d 08 mov 0x8(%ebp),%ebx //ra
140bac: 85 db test %ebx,%ebx //if ( ra == NULL )
140bae: 0f 84 ef 01 00 00 je 140da3 <raAdd+0x203>
140bb4: 8b 43 64 mov 0x64(%ebx),%eax //ra->threadSafe
140bb7: 85 c0 test %eax,%eax //if (ra->threadSafe)
140bb9: 0f 85 a1 00 00 00 jne 140c60 <raAdd+0xc0>
140bbf: 8b 73 24 mov 0x24(%ebx),%esi //ra->firstVacantElement
140bc2: 85 f6 test %esi,%esi //if (ra->firstVacantElement == NULL)
140bc4: 0f 84 7b 01 00 00 je 140d45 <raAdd+0x1a5>
140bca: 8b 46 08 mov 0x8(%esi),%eax //find variant allocatedElement
140bcd: 89 43 24 mov %eax,0x24(%ebx) //allocatedElement = ra->firstVacantElement;
140bd0: 8b 43 28 mov 0x28(%ebx),%eax //ra->firstVacantElement = (RAElement)((vacantNode *)allocatedElement)->next;
140bd3: 85 c0 test %eax,%eax //if (((vacantNode *)ra->workingSetElement) == NULL)
140bd5: 0f 84 aa 00 00 00 je 140c85 <raAdd+0xe5>
-------------------------raAdd+0x3b----------------------------------
140bdb: 8b 40 08 mov 0x8(%eax),%eax //((vacantNode *)ra->workingSetElement)->next
---------------------------------------------------------------------
140bde: 85 c0 test %eax,%eax //if (((vacantNode *)ra->workingSetElement) && ((vacantNode *)ra->workingSetElement)->next != NULL)
140be0: 0f 84 da 00 00 00 je 140cc0 <raAdd+0x120>
140be6: 89 43 28 mov %eax,0x28(%ebx) //ra->workingSetElement = (RAElement)(((vacantNode *)ra->workingSetElement)->next);
140be9: 8b 43 24 mov 0x24(%ebx),%eax //ra->firstVacantElement
140bec: 85 c0 test %eax,%eax //if (ra->firstVacantElement == NULL)
140bee: 0f 84 12 01 00 00 je 140d06 <raAdd+0x166>
140bf4: c7 40 04 00 00 00 00 movl $0x0,0x4(%eax) //((vacantNode *)ra->firstVacantElement)->prev = NULL;
140bfb: 8b 7e 0c mov 0xc(%esi),%edi //((vacantNode *)allocatedElement)->nodeIndex;
140bfe: b8 80 00 00 00 mov $0x80,%eax
140c03: 89 fa mov %edi,%edx
140c05: 89 f9 mov %edi,%ecx
140c07: c1 ea 03 shr $0x3,%edx
140c0a: 83 e1 07 and $0x7,%ecx
140c0d: d3 f8 sar %cl,%eax
140c0f: 08 44 13 6c or %al,0x6c(%ebx,%edx,1)
140c13: 8b 43 38 mov 0x38(%ebx),%eax
140c16: 83 c0 01 add $0x1,%eax
140c19: 3b 43 3c cmp 0x3c(%ebx),%eax
140c1c: 89 43 38 mov %eax,0x38(%ebx)
140c1f: 7f 5f jg 140c80 <raAdd+0xe0>
140c21: 8b 43 58 mov 0x58(%ebx),%eax
140c24: 85 c0 test %eax,%eax
140c26: 74 18 je 140c40 <raAdd+0xa0>
140c28: c7 44 24 04 08 00 00 movl $0x8,0x4(%esp)
140c2f: 00
140c30: 89 04 24 mov %eax,(%esp)
140c33: e8 fc ff ff ff call 140c34 <raAdd+0x94>
140c38: 85 c0 test %eax,%eax
140c3a: 0f 85 90 00 00 00 jne 140cd0 <raAdd+0x130>
140c40: 8b 43 64 mov 0x64(%ebx),%eax
140c43: 85 c0 test %eax,%eax
140c45: 75 29 jne 140c70 <raAdd+0xd0>
140c47: 8b 45 0c mov 0xc(%ebp),%eax
140c4a: 85 c0 test %eax,%eax
140c4c: 74 05 je 140c53 <raAdd+0xb3>
140c4e: 8b 45 0c mov 0xc(%ebp),%eax
140c51: 89 30 mov %esi,(%eax)
140c53: 83 c4 1c add $0x1c,%esp
140c56: 89 f8 mov %edi,%eax
140c58: 5b pop %ebx
140c59: 5e pop %esi
140c5a: 5f pop %edi
140c5b: 5d pop %ebp
140c5c: c3 ret
140c5d: 8d 76 00 lea 0x0(%esi),%esi
140c60: c7 43 68 00 00 00 00 movl $0x0,0x68(%ebx)
140c67: e9 53 ff ff ff jmp 140bbf <raAdd+0x1f>
140c6c: 8d 74 26 00 lea 0x0(%esi),%esi
140c70: c7 43 68 00 00 00 00 movl $0x0,0x68(%ebx)
140c77: eb ce jmp 140c47 <raAdd+0xa7>
140c79: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi
140c80: 89 43 3c mov %eax,0x3c(%ebx)
140c83: eb 9c jmp 140c21 <raAdd+0x81>
140c85: 8b 43 58 mov 0x58(%ebx),%eax
140c88: 85 c0 test %eax,%eax
140c8a: 74 18 je 140ca4 <raAdd+0x104>
140c8c: c7 44 24 04 02 00 00 movl $0x2,0x4(%esp)
140c93: 00
140c94: 89 04 24 mov %eax,(%esp)
140c97: e8 fc ff ff ff call 140c98 <raAdd+0xf8>
140c9c: 85 c0 test %eax,%eax
140c9e: 0f 85 85 00 00 00 jne 140d29 <raAdd+0x189>
140ca4: 89 5c 24 04 mov %ebx,0x4(%esp)
140ca8: c7 04 24 7c 8b 1b 00 movl $0x1b8b7c,(%esp)
140caf: e8 fc ff ff ff call 140cb0 <raAdd+0x110>
140cb4: 8b 43 28 mov 0x28(%ebx),%eax
140cb7: 85 c0 test %eax,%eax
140cb9: 0f 85 1c ff ff ff jne 140bdb <raAdd+0x3b>
140cbf: 90 nop
140cc0: 83 6b 30 01 subl $0x1,0x30(%ebx)
140cc4: e9 20 ff ff ff jmp 140be9 <raAdd+0x49>
140cc9: 8d b4 26 00 00 00 00 lea 0x0(%esi),%esi
140cd0: 89 7c 24 14 mov %edi,0x14(%esp)
140cd4: 89 74 24 10 mov %esi,0x10(%esp)
140cd8: 8b 43 38 mov 0x38(%ebx),%eax
140cdb: 89 5c 24 08 mov %ebx,0x8(%esp)
140cdf: c7 44 24 04 f0 8b 1b movl $0x1b8bf0,0x4(%esp)
140ce6: 00
140ce7: 89 44 24 0c mov %eax,0xc(%esp)
140ceb: 8b 43 58 mov 0x58(%ebx),%eax
140cee: 89 04 24 mov %eax,(%esp)
140cf1: e8 fc ff ff ff call 140cf2 <raAdd+0x152>
140cf6: 8b 43 64 mov 0x64(%ebx),%eax
140cf9: 85 c0 test %eax,%eax
140cfb: 0f 84 46 ff ff ff je 140c47 <raAdd+0xa7>
140d01: e9 6a ff ff ff jmp 140c70 <raAdd+0xd0>
140d06: 89 5c 24 04 mov %ebx,0x4(%esp)
140d0a: c7 04 24 c4 8b 1b 00 movl $0x1b8bc4,(%esp)
140d11: e8 fc ff ff ff call 140d12 <raAdd+0x172>
140d16: c7 43 28 00 00 00 00 movl $0x0,0x28(%ebx)
140d1d: c7 43 30 00 00 00 00 movl $0x0,0x30(%ebx)
140d24: e9 d2 fe ff ff jmp 140bfb <raAdd+0x5b>
140d29: 89 5c 24 08 mov %ebx,0x8(%esp)
140d2d: c7 44 24 04 34 8b 1b movl $0x1b8b34,0x4(%esp)
140d34: 00
140d35: 8b 43 58 mov 0x58(%ebx),%eax
140d38: 89 04 24 mov %eax,(%esp)
140d3b: e8 fc ff ff ff call 140d3c <raAdd+0x19c>
140d40: e9 5f ff ff ff jmp 140ca4 <raAdd+0x104>
140d45: 8b 43 58 mov 0x58(%ebx),%eax
140d48: 85 c0 test %eax,%eax
140d4a: 74 14 je 140d60 <raAdd+0x1c0>
140d4c: c7 44 24 04 02 00 00 movl $0x2,0x4(%esp)
140d53: 00
140d54: 89 04 24 mov %eax,(%esp)
140d57: e8 fc ff ff ff call 140d58 <raAdd+0x1b8>
140d5c: 85 c0 test %eax,%eax
140d5e: 75 59 jne 140db9 <raAdd+0x219>
140d60: 8b 43 34 mov 0x34(%ebx),%eax
140d63: 89 5c 24 04 mov %ebx,0x4(%esp)
140d67: c7 04 24 0c 8b 1b 00 movl $0x1b8b0c,(%esp)
140d6e: 89 44 24 08 mov %eax,0x8(%esp)
140d72: e8 fc ff ff ff call 140d73 <raAdd+0x1d3>
140d77: 8b 45 0c mov 0xc(%ebp),%eax
140d7a: 85 c0 test %eax,%eax
140d7c: 74 09 je 140d87 <raAdd+0x1e7>
140d7e: 8b 45 0c mov 0xc(%ebp),%eax
140d81: c7 00 00 00 00 00 movl $0x0,(%eax)
140d87: 8b 43 64 mov 0x64(%ebx),%eax
140d8a: bf ff ff ff ff mov $0xffffffff,%edi
140d8f: 85 c0 test %eax,%eax
140d91: 0f 84 bc fe ff ff je 140c53 <raAdd+0xb3>
140d97: c7 43 68 00 00 00 00 movl $0x0,0x68(%ebx)
140d9e: e9 b0 fe ff ff jmp 140c53 <raAdd+0xb3>
140da3: c7 04 24 56 8a 1b 00 movl $0x1b8a56,(%esp)
140daa: bf ff ff ff ff mov $0xffffffff,%edi
140daf: e8 fc ff ff ff call 140db0 <raAdd+0x210>
140db4: e9 9a fe ff ff jmp 140c53 <raAdd+0xb3>
140db9: 8b 43 34 mov 0x34(%ebx),%eax
140dbc: 89 5c 24 08 mov %ebx,0x8(%esp)
140dc0: c7 44 24 04 e4 8a 1b movl $0x1b8ae4,0x4(%esp)
140dc7: 00
140dc8: 89 44 24 0c mov %eax,0xc(%esp)
140dcc: 8b 43 58 mov 0x58(%ebx),%eax
140dcf: 89 04 24 mov %eax,(%esp)
140dd2: e8 fc ff ff ff call 140dd3 <raAdd+0x233>
140dd7: eb 87 jmp 140d60 <raAdd+0x1c0>
140dd9: 90 nop
140dda: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
The source code of the crash point
***********************************
typedef struct vacantNode_tag vacantNode;
struct vacantNode_tag
{
void* unused; /* This unused pointer is here to protect the first parameter people
will put in structs inside RA even when the element is vacant.
It is used by EMA. */
vacantNode* prev; /* Pointer to the previous vacant element. NULL if this one is the first */
vacantNode* next; /* Pointer to the next vacant element. NULL if this one is the last */
int nodeIndex; /* Current node's index. This is used for faster calculation of the raAdd() function. */
};
int raAdd(IN HRA raH, OUT RAElement *pOutElem)
{
raHeader *ra = (raHeader *)raH;
RAElement allocatedElement;
int vLocation;
if ( ra == NULL )
{
printf("raAdd raH is NULL!\n");
return RV_ERROR_UNKNOWN;
}
if (ra->threadSafe)
Rv2LockGet(&ra->lock, ra->logMgr);
/* See if there's any place in this RA */
if (ra->firstVacantElement == NULL)
{
RvLogError(ra->pLog,
(ra->pLog, "raAdd (%s): Array full (%d elements)", ra->name, ra->maxNumOfElements));
Rv2Printf("raAdd (%s): Array full (%d elements)\n", ra->name, ra->maxNumOfElements);
if (pOutElem != NULL) *pOutElem = NULL;
if (ra->threadSafe)
Rv2LockRelease(&ra->lock, ra->logMgr);
return RV_ERROR_UNKNOWN;
}
allocatedElement = ra->firstVacantElement;
/* Get the element from list of vacant elements and fix that list */
ra->firstVacantElement = (RAElement)((vacantNode *)allocatedElement)->next;
if (((vacantNode *)ra->workingSetElement) == NULL)
{
RvLogError(ra->pLog, (ra->pLog, "raAdd (%s):[test][raAdd] ((vacantNode *)ra->workingSetElement)==NULL", ra->name));
Rv2Printf("raAdd (%s): [test][raAdd] ((vacantNode *)ra->workingSetElement)==NULL\n", ra->name);
}
if (((vacantNode *)ra->workingSetElement) && ((vacantNode *)ra->workingSetElement)->next != NULL)
ra->workingSetElement = (RAElement)(((vacantNode *)ra->workingSetElement)->next);
else
ra->workingSetDistance--;
/* Let's see what we have to do with our free list */
if (ra->firstVacantElement == NULL)
{
Rv2Printf("raAdd (%s): ra->firstVacantElement ==NULL\n", ra->name);
/* No more free elements - update the working set information */
ra->workingSetElement = NULL;
ra->workingSetDistance = 0;
}
else
{
/* Since we chopped off the head of the free list - we should set the new head
of the free list as the first element: i.e - prev=NULL */
((vacantNode *)ra->firstVacantElement)->prev = NULL;
}
......
return vLocation;
}
Analysis steps:
1. Disassemble the library/excutable.
objdump -D libkdv323adapter.so > libkdv323adapter.so.asm
2. Locate the crash point
Use the crash backtrace information to locate the crash point.
/opt/mcu/pas/libkdv323adapter.so(raAdd+0x3b) [0x51dbdb]
Find raAdd in the asm code, the address is 00140ba0, crash point is raAdd+0x3b, that is 00143765
Now we got the crash point which code is:
140bdb: 8b 40 08 mov 0x8(%eax),%eax
Seems it was happend when reading/assigning a value.
3. Translate the asm code to C code.
If see into the definition of struct vacantNode_tag, we can get that 0x8(%eax) means ra->workingSetElement->next, so the crash was happened in this line
if (((vacantNode *)ra->workingSetElement) && ((vacantNode *)ra->workingSetElement)->next != NULL)
ra->workingSetElement = (RAElement)(((vacantNode *)ra->workingSetElement)->next);
else
ra->workingSetDistance--;
I’ve matched and listed the C code together with the asm code in the previous asm contents.
Done!
Like what I said at the beginning, this is really not a good example to explain crash analysis by reading the asm code. Because we did not find the real reason of the crash for this example.
And if you read the C code carefully, you can find a really weired thing, how could this crash happen in such a code?
if (((vacantNode *)ra->workingSetElement) && ((vacantNode *)ra->workingSetElement)->next != NULL)
{
...
}
————————-raAdd+0x3b———————————-
140bdb: 8b 40 08 mov 0x8(%eax),%eax //((vacantNode *)ra->workingSetElement)->next
———————————————————————
140bde: 85 c0 test %eax,%eax //if (((vacantNode *)ra->workingSetElement) && ((vacantNode *)ra->workingSetElement)->next != NULL)
Allow me to explain it later.
One thought on “Trouble shooting: step by step to analysis crashes”
There’s certainly a great deal to find out about this
issue. I love all the points you have made.