x86/C++ - 指向指针的指针:编译器违反了 Const?
x86/C++ - Pointer To Pointer: Const being violated by compiler?
我正在为我的学生项目的游戏引擎开发一个共享指针(称为句柄)实现,我们 运行 遇到了一个我们无法解释的错误。出于某种原因,在我们工厂的某个时刻,有一个无效的内部指针通过句柄传递给工厂,这导致我们的原型加载期间发生崩溃。我们调试了几个小时的过程,并将任何复杂的语句分解为最简单的版本,以便于调试。我最终将问题归结为 Handle class' 复制构造函数。但是,似乎仍然存在释放内部指针的中间步骤。我阅读了所有我能找到的关于可能导致此问题的文章,但没有找到任何内容。最后,我决定查看反汇编代码,看看我是否能弄清楚发生了什么。这是没有反汇编的复制构造函数。
template <class TYPE>
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr)
{
if(!m_weak)
++(*m_ref_count);
}
这是反汇编后的拷贝构造函数。
template <class TYPE>
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr)
{
013FFF50 push ebp
013FFF51 mov ebp,esp
013FFF53 sub esp,0CCh
013FFF59 push ebx
013FFF5A push esi
013FFF5B push edi
013FFF5C push ecx
013FFF5D lea edi,[ebp-0CCh]
013FFF63 mov ecx,33h
013FFF68 mov eax,0CCCCCCCCh
013FFF6D rep stos dword ptr es:[edi]
013FFF6F pop ecx
013FFF70 mov dword ptr [this],ecx
013FFF73 mov eax,dword ptr [this]
013FFF76 mov cl,byte ptr [weak]
013FFF79 mov byte ptr [eax],cl
013FFF7B mov eax,dword ptr [this]
013FFF7E mov ecx,dword ptr [rhs]
013FFF81 mov edx,dword ptr [ecx+4]
013FFF84 mov dword ptr [eax+4],edx
013FFF87 mov eax,dword ptr [this]
013FFF8A mov ecx,dword ptr [rhs]
013FFF8D mov edx,dword ptr [ecx+8]
013FFF90 mov dword ptr [eax+8],edx
if(!m_weak)
013FFF93 mov eax,dword ptr [this]
013FFF96 movzx ecx,byte ptr [eax]
013FFF99 test ecx,ecx
013FFF9B jne Handle<Component>::Handle<Component>+60h (013FFFB0h)
++(*m_ref_count);
013FFF9D mov eax,dword ptr [this]
013FFFA0 mov ecx,dword ptr [eax+4]
013FFFA3 mov edx,dword ptr [ecx]
013FFFA5 add edx,1
013FFFA8 mov eax,dword ptr [this]
013FFFAB mov ecx,dword ptr [eax+4]
013FFFAE mov dword ptr [ecx],edx
}
问题(据我所知)在这里:
013FFF6D rep stos dword ptr es:[edi]
众所周知,这是用来快速清理内存的。但是,这条指令清除了 rhs Handle 的内部指针指向的指针,不仅如此,它还违反了 const,因为传入的 Handle 是一个 const 引用。我不确定这是否是 VS 问题,是不是在我们的构建设置中设置的某种标志导致它发生,但复制构造函数工作正常,直到我们尝试用它制作原型。
如有任何帮助,我们将不胜感激!
编辑 1:在一些评论表明我可能 return 在我的代码中的某处设置临时变量后,我进一步研究了这个问题。我找不到任何暗示我正在这样做的东西,但我确实筛选了一些更多的反汇编代码,看看是否能找到任何提示。这是我们的 CreateGenericArchetype 函数,这是问题出现的地方。
Handle<Object> ObjectFactory::CreateGenericArchetype(const std::wstring &ArchetypeName)
{
/* Create The Object */
auto archetype = mComponentFactoryMap.find(std::wstring(L"Object"));
Handle<Component> created_component = archetype->second->CreateArchetypeComponent();
Handle<Object> retVal = static_handle_cast<Object>(created_component);
retVal->mArchetypeName = ArchetypeName;
mArchetypeMap.insert(std::pair<std::wstring, Handle<Object>>(ArchetypeName, retVal));
return Handle<Object>(retVal, true);
}
导致我们所有问题(目前)的行是:
Handle<Component> created_component = archetype->second->CreateArchetypeComponent();
当然,我也将调试扩展到 CreateArchetypeComponent,所以这里也是:
Handle<Component> ComponentTypeFactory<Object>::CreateArchetypeComponent() const
{
Object* created_object = new Object;
Component* casted_object = static_cast<Component*>(created_object);
Handle<Component> newComponent(casted_object);
newComponent->mType = mType;
newComponent->mID = GetNewID();
return newComponent;
}
Object继承自Component,所以static_castObject到Component的向下转型是有效的,构造函数成功从Component*构造了Handle。当我们尝试 return 我们在函数内部构造的句柄时,问题就出现了。这是该交互的反汇编:
return newComponent;
005602AD push 0
005602AF lea eax,[newComponent]
005602B2 push eax
005602B3 mov ecx,dword ptr [ebp+8]
005602B6 call Handle<Component>::Handle<Component> (04EA050h)
005602BB mov ecx,dword ptr [ebp-110h]
005602C1 or ecx,1
005602C4 mov dword ptr [ebp-110h],ecx
005602CA mov byte ptr [ebp-4],0
005602CE lea ecx,[newComponent]
005602D1 call Handle<Component>::~Handle<Component> (04F2E7Bh)
005602D6 mov eax,dword ptr [ebp+8]
}
00EB02D9 push edx
00EB02DA mov ecx,ebp
00EB02DC push eax
00EB02DD lea edx,ds:[0EB0318h]
00EB02E3 call @_RTC_CheckStackVars@8 (0E4BA9Eh)
00EB02E8 pop eax
00EB02E9 pop edx
00EB02EA mov ecx,dword ptr [ebp-0Ch]
00EB02ED mov dword ptr fs:[0],ecx
00EB02F4 pop ecx
00EB02F5 pop edi
00EB02F6 pop esi
00EB02F7 pop ebx
00EB02F8 mov ecx,dword ptr [ebp-10h]
00EB02FB xor ecx,ebp
00EB02FD call @__security_check_cookie@4 (0E4E9BAh)
00EB0302 add esp,130h
00EB0308 cmp ebp,esp
00EB030A call __RTC_CheckEsp (0E41C3Dh)
00EB030F mov esp,ebp
00EB0311 pop ebp
00EB0312 ret 4
*******回到 CreateGenericArchetype*******
005C2639 call __RTC_CheckEsp (04F1C3Dh)
005C263E mov dword ptr [ebp-1D4h],eax
005C2644 mov ecx,dword ptr [ebp-1D4h]
005C264A mov dword ptr [ebp-1D8h],ecx
005C2650 mov byte ptr [ebp-4],4
005C2654 mov edx,dword ptr [ebp-1D8h]
005C265A push edx
005C265B lea ecx,[created_component]
005C265E call Handle<Component>::Handle<Component> (04EA050h)
*******从此处复制构造函数反汇编*******
005C2663 mov byte ptr [ebp-4],6
005C2667 lea ecx,[ebp-174h]
005C266D call Handle<Component>::~Handle<Component> (04F2E7Bh)
Handle<Object> retVal = static_handle_cast<Object>(created_component);
除非使用 "created_component" 调用我的复制构造函数,因为 rhs 句柄被认为是 return 局部变量,否则我看不出哪里可能出错,unless 这是因为 CreateArchetypeComponent 的 returned Handle 在传递给复制构造函数时在堆栈上,然后被清除。
感谢您对这个问题的大力帮助,但我们弄清楚了是怎么回事。发生的事情是,在一些 Handle 构造函数中,我们需要为 Handle 的内部指针到指针将要指向的指针分配内存。所以发生的事情是 Handle 本身正在使用保存在堆栈上的指针,并且当调用 rep stos
操作码时,它正在清除所述指针。
我正在为我的学生项目的游戏引擎开发一个共享指针(称为句柄)实现,我们 运行 遇到了一个我们无法解释的错误。出于某种原因,在我们工厂的某个时刻,有一个无效的内部指针通过句柄传递给工厂,这导致我们的原型加载期间发生崩溃。我们调试了几个小时的过程,并将任何复杂的语句分解为最简单的版本,以便于调试。我最终将问题归结为 Handle class' 复制构造函数。但是,似乎仍然存在释放内部指针的中间步骤。我阅读了所有我能找到的关于可能导致此问题的文章,但没有找到任何内容。最后,我决定查看反汇编代码,看看我是否能弄清楚发生了什么。这是没有反汇编的复制构造函数。
template <class TYPE>
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr)
{
if(!m_weak)
++(*m_ref_count);
}
这是反汇编后的拷贝构造函数。
template <class TYPE>
Handle<TYPE>::Handle(const Handle<TYPE>& rhs, bool weak) : m_weak(weak), m_ref_count(rhs.m_ref_count), m_ptr(rhs.m_ptr)
{
013FFF50 push ebp
013FFF51 mov ebp,esp
013FFF53 sub esp,0CCh
013FFF59 push ebx
013FFF5A push esi
013FFF5B push edi
013FFF5C push ecx
013FFF5D lea edi,[ebp-0CCh]
013FFF63 mov ecx,33h
013FFF68 mov eax,0CCCCCCCCh
013FFF6D rep stos dword ptr es:[edi]
013FFF6F pop ecx
013FFF70 mov dword ptr [this],ecx
013FFF73 mov eax,dword ptr [this]
013FFF76 mov cl,byte ptr [weak]
013FFF79 mov byte ptr [eax],cl
013FFF7B mov eax,dword ptr [this]
013FFF7E mov ecx,dword ptr [rhs]
013FFF81 mov edx,dword ptr [ecx+4]
013FFF84 mov dword ptr [eax+4],edx
013FFF87 mov eax,dword ptr [this]
013FFF8A mov ecx,dword ptr [rhs]
013FFF8D mov edx,dword ptr [ecx+8]
013FFF90 mov dword ptr [eax+8],edx
if(!m_weak)
013FFF93 mov eax,dword ptr [this]
013FFF96 movzx ecx,byte ptr [eax]
013FFF99 test ecx,ecx
013FFF9B jne Handle<Component>::Handle<Component>+60h (013FFFB0h)
++(*m_ref_count);
013FFF9D mov eax,dword ptr [this]
013FFFA0 mov ecx,dword ptr [eax+4]
013FFFA3 mov edx,dword ptr [ecx]
013FFFA5 add edx,1
013FFFA8 mov eax,dword ptr [this]
013FFFAB mov ecx,dword ptr [eax+4]
013FFFAE mov dword ptr [ecx],edx
}
问题(据我所知)在这里:
013FFF6D rep stos dword ptr es:[edi]
众所周知,这是用来快速清理内存的。但是,这条指令清除了 rhs Handle 的内部指针指向的指针,不仅如此,它还违反了 const,因为传入的 Handle 是一个 const 引用。我不确定这是否是 VS 问题,是不是在我们的构建设置中设置的某种标志导致它发生,但复制构造函数工作正常,直到我们尝试用它制作原型。
如有任何帮助,我们将不胜感激!
编辑 1:在一些评论表明我可能 return 在我的代码中的某处设置临时变量后,我进一步研究了这个问题。我找不到任何暗示我正在这样做的东西,但我确实筛选了一些更多的反汇编代码,看看是否能找到任何提示。这是我们的 CreateGenericArchetype 函数,这是问题出现的地方。
Handle<Object> ObjectFactory::CreateGenericArchetype(const std::wstring &ArchetypeName)
{
/* Create The Object */
auto archetype = mComponentFactoryMap.find(std::wstring(L"Object"));
Handle<Component> created_component = archetype->second->CreateArchetypeComponent();
Handle<Object> retVal = static_handle_cast<Object>(created_component);
retVal->mArchetypeName = ArchetypeName;
mArchetypeMap.insert(std::pair<std::wstring, Handle<Object>>(ArchetypeName, retVal));
return Handle<Object>(retVal, true);
}
导致我们所有问题(目前)的行是:
Handle<Component> created_component = archetype->second->CreateArchetypeComponent();
当然,我也将调试扩展到 CreateArchetypeComponent,所以这里也是:
Handle<Component> ComponentTypeFactory<Object>::CreateArchetypeComponent() const
{
Object* created_object = new Object;
Component* casted_object = static_cast<Component*>(created_object);
Handle<Component> newComponent(casted_object);
newComponent->mType = mType;
newComponent->mID = GetNewID();
return newComponent;
}
Object继承自Component,所以static_castObject到Component的向下转型是有效的,构造函数成功从Component*构造了Handle。当我们尝试 return 我们在函数内部构造的句柄时,问题就出现了。这是该交互的反汇编:
return newComponent;
005602AD push 0
005602AF lea eax,[newComponent]
005602B2 push eax
005602B3 mov ecx,dword ptr [ebp+8]
005602B6 call Handle<Component>::Handle<Component> (04EA050h)
005602BB mov ecx,dword ptr [ebp-110h]
005602C1 or ecx,1
005602C4 mov dword ptr [ebp-110h],ecx
005602CA mov byte ptr [ebp-4],0
005602CE lea ecx,[newComponent]
005602D1 call Handle<Component>::~Handle<Component> (04F2E7Bh)
005602D6 mov eax,dword ptr [ebp+8]
}
00EB02D9 push edx
00EB02DA mov ecx,ebp
00EB02DC push eax
00EB02DD lea edx,ds:[0EB0318h]
00EB02E3 call @_RTC_CheckStackVars@8 (0E4BA9Eh)
00EB02E8 pop eax
00EB02E9 pop edx
00EB02EA mov ecx,dword ptr [ebp-0Ch]
00EB02ED mov dword ptr fs:[0],ecx
00EB02F4 pop ecx
00EB02F5 pop edi
00EB02F6 pop esi
00EB02F7 pop ebx
00EB02F8 mov ecx,dword ptr [ebp-10h]
00EB02FB xor ecx,ebp
00EB02FD call @__security_check_cookie@4 (0E4E9BAh)
00EB0302 add esp,130h
00EB0308 cmp ebp,esp
00EB030A call __RTC_CheckEsp (0E41C3Dh)
00EB030F mov esp,ebp
00EB0311 pop ebp
00EB0312 ret 4
*******回到 CreateGenericArchetype*******
005C2639 call __RTC_CheckEsp (04F1C3Dh)
005C263E mov dword ptr [ebp-1D4h],eax
005C2644 mov ecx,dword ptr [ebp-1D4h]
005C264A mov dword ptr [ebp-1D8h],ecx
005C2650 mov byte ptr [ebp-4],4
005C2654 mov edx,dword ptr [ebp-1D8h]
005C265A push edx
005C265B lea ecx,[created_component]
005C265E call Handle<Component>::Handle<Component> (04EA050h)
*******从此处复制构造函数反汇编*******
005C2663 mov byte ptr [ebp-4],6
005C2667 lea ecx,[ebp-174h]
005C266D call Handle<Component>::~Handle<Component> (04F2E7Bh)
Handle<Object> retVal = static_handle_cast<Object>(created_component);
除非使用 "created_component" 调用我的复制构造函数,因为 rhs 句柄被认为是 return 局部变量,否则我看不出哪里可能出错,unless 这是因为 CreateArchetypeComponent 的 returned Handle 在传递给复制构造函数时在堆栈上,然后被清除。
感谢您对这个问题的大力帮助,但我们弄清楚了是怎么回事。发生的事情是,在一些 Handle 构造函数中,我们需要为 Handle 的内部指针到指针将要指向的指针分配内存。所以发生的事情是 Handle 本身正在使用保存在堆栈上的指针,并且当调用 rep stos
操作码时,它正在清除所述指针。