又一VC6编译器BUG
NetRoc
http://www.DbgTech.net/
一个XML解析函数,用于获取XML中的子节点。Debug下面正常,而Release下面始终获取不到节点;另外,代码中有一个没有使用到的CString变量,Release下面如果定义它,则运行正常,如果没有定义,则运行出错。函数代码如下:
//到到第一个Child结点指针
Node* CXMLProtocolParse::GetFirstChild(Node* pNode)
{// AfxMessageBox("");
ASSERT (pNode != NULL);
if ( pNode == NULL )
return NULL;
Node* pElem = NULL;
try
{
pNode->get_firstChild(&pElem);
if ( pElem == NULL )
return NULL;
m_ArrayElement.Add(pElem);
//CString strError ; //不加这个变量的申明 Release中无法正确获得节点数据
DOMNodeType tagNodeType = NODE_INVALID;
pElem->get_nodeType(&tagNodeType);
if ( tagNodeType == NODE_TEXT ) //第一个子结点为文本结点, 则把其下一个兄弟结点认为是第一个结点。后来发现问题就出在这个判断对应的跳转语句上
return GetNextSibling(pElem);
}catch(...)
{
ASSERT(FALSE);
}
return pElem; //Release版执行从这里返回时就会出问题,返回值为NULL,但是pElem其实是有值的。
}
未定义CString变量时的汇编代码如下,我们可以跟踪一下出错的过程:
.text:00401D60 push ebp
.text:00401D61 mov ebp, esp
.text:00401D63 push 0FFFFFFFFh
.text:00401D65 push offset unknown_libname_11 ; Microsoft VisualC 2-8/net runtime
.text:00401D6A mov eax, large fs:0
.text:00401D70 push eax
.text:00401D71 mov large fs:0, esp
.text:00401D78 sub esp, 8
.text:00401D7B mov eax, [ebp+arg_0]
.text:00401D7E push ebx
.text:00401D7F push esi
.text:00401D80 push edi
.text:00401D81 xor edi, edi
.text:00401D83 mov [ebp+var_10], esp
.text:00401D86 cmp eax, edi
.text:00401D88 mov esi, ecx
.text:00401D8A jz short loc_401DEC
.text:00401D8A
.text:00401D8C mov ecx, [eax]
.text:00401D8E lea edx, [ebp+arg_0]
.text:00401D91 push edx
.text:00401D92 push eax
.text:00401D93 mov [ebp+arg_0], edi
.text:00401D96 mov [ebp+var_4], edi
.text:00401D99 call dword ptr [ecx+34h] ; pNode->get_firstChild(&pElem);
.text:00401D99
.text:00401D9C mov edx, [ebp+arg_0]
.text:00401D9F cmp edx, edi
.text:00401DA1 jz short loc_401DEC
.text:00401DA1
.text:00401DA3 mov eax, [esi+0Ch]
.text:00401DA6 lea ecx, [esi+4]
.text:00401DA9 push edx
.text:00401DAA push eax
.text:00401DAB call CPtrArray::SetAtGrow(int,void *) ; m_ArrayElement.Add(pElem);
.text:00401DAB
.text:00401DB0 mov eax, [ebp+arg_0]
.text:00401DB3 lea edx, [ebp+var_14]
.text:00401DB6 mov [ebp+var_14], edi
.text:00401DB9 push edx
.text:00401DBA mov ecx, [eax]
.text:00401DBC push eax
.text:00401DBD call dword ptr [ecx+28h] ; pElem->get_nodeType(&tagNodeType);
.text:00401DBD
.text:00401DC0 mov eax, [ebp+var_14]
.text:00401DC3 cmp eax, 3
;这里因为比较的结果不同,所以将pElem的值传给eax,准备返回,到这个位置都还是一切正常的。但是跳转的目标因为优化的关系,重复使用了前面return NULL;这条语句的代码,所以后面会有问题。
.text:00401DC6 mov eax, [ebp+arg_0]
.text:00401DC9 jnz short loc_401DEC
.text:00401DC9
.text:00401DCB push eax
.text:00401DCC mov ecx, esi
.text:00401DCE call CXMLProtocolParse__GetNextSibling
.text:00401DCE
.text:00401DD3 mov ecx, [ebp+var_C]
.text:00401DD6 mov large fs:0, ecx
.text:00401DDD pop edi
.text:00401DDE pop esi
.text:00401DDF pop ebx
.text:00401DE0 mov esp, ebp
.text:00401DE2 pop ebp
.text:00401DE3 retn 4
.text:00401DE3
.text:00401DE6 ; ---------------------------------------------------------------------------
.text:00401DE6
.text:00401DE6 loc_401DE6: ; DATA XREF: .rdata:stru_4049F8o
.text:00401DE6 mov eax, offset loc_401DEC
.text:00401DEB retn
.text:00401DEB
.text:00401DEC ; ---------------------------------------------------------------------------
.text:00401DEC
.text:00401DEC loc_401DEC: ; CODE XREF: CXMLProtocolParse__GetFirstChild+2Aj
.text:00401DEC ; CXMLProtocolParse__GetFirstChild+41j
.text:00401DEC ; CXMLProtocolParse__GetFirstChild+69j
.text:00401DEC ; DATA XREF: CXMLProtocolParse__GetFirstChild:loc_401DE6o
;跳转到这个地方返回,多出来的那个xor eax,eax,问题就来了!!!这条语句块本来用作return NULL;返回的,所以里面有清零eax的操作,错误重用的话出大毛病!!!
.text:00401DEC mov ecx, [ebp+var_C]
.text:00401DEF pop edi
.text:00401DF0 pop esi
.text:00401DF1 xor eax, eax
.text:00401DF3 mov large fs:0, ecx
.text:00401DFA pop ebx
.text:00401DFB mov esp, ebp
.text:00401DFD pop ebp
.text:00401DFE retn 4
.text:00401DFE
.text:00401DFE CXMLProtocolParse__GetFirstChild endp
下面再来看定义了CString变量时的汇编代码,因为多了CString类的构造和析构函数,所以代码的结构发生了改变,return NULL;这条语句的代码没有被重用:
.text:00401D60
.text:00401D60 ; Attributes: bp-based frame
.text:00401D60
.text:00401D60 CXMLProtocolParse__GetFirstChild proc near
.text:00401D60 ; CODE XREF: CXMLProtocolParse__FindNode+73p
.text:00401D60
.text:00401D60 var_18 = byte ptr -18h
.text:00401D60 var_14 = dword ptr -14h
.text:00401D60 var_10 = dword ptr -10h
.text:00401D60 var_C = dword ptr -0Ch
.text:00401D60 var_4 = dword ptr -4
.text:00401D60 arg_0 = dword ptr 8
.text:00401D60
.text:00401D60 push ebp
.text:00401D61 mov ebp, esp
.text:00401D63 push 0FFFFFFFFh
.text:00401D65 push offset unknown_libname_11 ; Microsoft VisualC 2-8/net runtime
.text:00401D6A mov eax, large fs:0
.text:00401D70 push eax
.text:00401D71 mov large fs:0, esp
.text:00401D78 sub esp, 0Ch
.text:00401D7B mov eax, [ebp+arg_0]
.text:00401D7E push ebx
.text:00401D7F push esi
.text:00401D80 xor ebx, ebx
.text:00401D82 push edi
.text:00401D83 cmp eax, ebx
.text:00401D85 mov [ebp+var_10], esp
.text:00401D88 mov esi, ecx
.text:00401D8A jnz short loc_401DA1
.text:00401D8A
.text:00401D8C xor eax, eax
.text:00401D8E mov ecx, [ebp+var_C]
.text:00401D91 mov large fs:0, ecx
.text:00401D98 pop edi
.text:00401D99 pop esi
.text:00401D9A pop ebx
.text:00401D9B mov esp, ebp
.text:00401D9D pop ebp
.text:00401D9E retn 4
.text:00401D9E
.text:00401DA1 ; ---------------------------------------------------------------------------
.text:00401DA1
.text:00401DA1 loc_401DA1: ; CODE XREF: CXMLProtocolParse__GetFirstChild+2Aj
.text:00401DA1 mov ecx, [eax]
.text:00401DA3 lea edx, [ebp+arg_0]
.text:00401DA6 push edx
.text:00401DA7 push eax
.text:00401DA8 mov [ebp+arg_0], ebx
.text:00401DAB mov [ebp+var_4], ebx
.text:00401DAE call dword ptr [ecx+34h] ; pNode->get_firstChild(&pElem);
.text:00401DAE
.text:00401DB1 mov edx, [ebp+arg_0]
.text:00401DB4 cmp edx, ebx
.text:00401DB6 jnz short loc_401DCD
.text:00401DB6
;这里return NULL;
.text:00401DB8 xor eax, eax
.text:00401DBA mov ecx, [ebp+var_C]
.text:00401DBD mov large fs:0, ecx
.text:00401DC4 pop edi
.text:00401DC5 pop esi
.text:00401DC6 pop ebx
.text:00401DC7 mov esp, ebp
.text:00401DC9 pop ebp
.text:00401DCA retn 4
.text:00401DCA
.text:00401DCD ; ---------------------------------------------------------------------------
.text:00401DCD
.text:00401DCD loc_401DCD: ; CODE XREF: CXMLProtocolParse__GetFirstChild+56j
.text:00401DCD mov eax, [esi+0Ch]
.text:00401DD0 lea ecx, [esi+4]
.text:00401DD3 push edx
.text:00401DD4 push eax
.text:00401DD5 call CPtrArray::SetAtGrow(int,void *) ; m_ArrayElement.Add(pElem);
.text:00401DD5
.text:00401DDA lea ecx, [ebp+var_18]
.text:00401DDD call CString::CString(void) ; 初始化CString
.text:00401DDD
.text:00401DE2 mov eax, [ebp+arg_0]
.text:00401DE5 lea edx, [ebp+var_14]
.text:00401DE8 mov [ebp+var_14], ebx
.text:00401DEB push edx
.text:00401DEC mov ecx, [eax]
.text:00401DEE push eax
.text:00401DEF mov byte ptr [ebp+var_4], 1
.text:00401DF3 call dword ptr [ecx+28h] ; pElem->get_nodeType(&tagNodeType);
.text:00401DF3
;这里比较并跳转
.text:00401DF6 cmp [ebp+var_14], 3
.text:00401DFA jnz short loc_401E29
.text:00401DFA
.text:00401DFC mov eax, [ebp+arg_0]
.text:00401DFF mov ecx, esi
.text:00401E01 push eax
.text:00401E02 call CXMLProtocolParse__GetNextSibling
.text:00401E02
.text:00401E07 lea ecx, [ebp+var_18]
.text:00401E0A mov esi, eax
.text:00401E0C mov byte ptr [ebp+var_4], bl
.text:00401E0F call CString::~CString(void)
.text:00401E0F
.text:00401E14 mov eax, esi
.text:00401E16 mov ecx, [ebp+var_C]
.text:00401E19 mov large fs:0, ecx
.text:00401E20 pop edi
.text:00401E21 pop esi
.text:00401E22 pop ebx
.text:00401E23 mov esp, ebp
.text:00401E25 pop ebp
.text:00401E26 retn 4
.text:00401E26
.text:00401E29 ; ---------------------------------------------------------------------------
.text:00401E29
.text:00401E29 loc_401E29: ; CODE XREF: CXMLProtocolParse__GetFirstChild+9Aj
;析构并返回,这里就没有再做eax清零的操作了,所以运行正常
.text:00401E29 lea ecx, [ebp+var_18]
.text:00401E2C mov byte ptr [ebp+var_4], bl
.text:00401E2F call CString::~CString(void)
.text:00401E2F
.text:00401E34
.text:00401E34 loc_401E34: ; DATA XREF: sub_401E4Ao
.text:00401E34 mov ecx, [ebp+var_C]
.text:00401E37 mov eax, [ebp+arg_0]
.text:00401E3A pop edi
.text:00401E3B pop esi
.text:00401E3C mov large fs:0, ecx
.text:00401E43 pop ebx
.text:00401E44 mov esp, ebp
.text:00401E46 pop ebp
.text:00401E47 retn 4
.text:00401E47
.text:00401E47 CXMLProtocolParse__GetFirstChild endp
刚开始以为是栈破坏或者变量未初始化的问题,没想到又发现一个VC6编译器BUG,小小的记录一下,呵呵。经测试,用VS2008编译不会有这个问题。10多年前的东西用起来还真是危险啊~~~