NetRoc's Blog

N-Tech

 

又一VC6编译器BUG

又一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多年前的东西用起来还真是危险啊~~~

posted on 2010-07-20 11:22 NetRoc 阅读(741) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。

导航

统计

常用链接

留言簿(7)

随笔档案(99)

文章分类(35)

文章档案(32)

Friends

Mirror

搜索

最新评论

阅读排行榜

评论排行榜