提问人:Gunther Struyf 提问时间:2/24/2017 最后编辑:Gunther Struyf 更新时间:2/24/2017 访问量:336
编译器将此指针移动到错误的地址
Compiler mov'es this pointer to wrong address
问:
我有一个简单的多态结构,有一个纯虚函数。
在使用它的大型项目中,唯一的缺陷是该项目使用几个全局静态数据进行集中参数加载和事件记录(无法轻松摆脱遗留代码)。Foo
项目信息:
- 平台工具集:
v110_xp
- 静态库中的 MFC
- MBCS 字符集
- 调用约定:
__cdecl
- 禁用所有优化
- 警告级别 4,整个项目无警告
法典:
class Base
{
public:
Base(){}
virtual ~Base(void){}
virtual void Foo(void) = 0;
};
class Derived
: public Base
{
public:
Derived(void) : Base(){}
virtual void Foo(void) override
{
double a = sqrt(4.9);
double b = -a;
}
调用代码(并不重要,到处都是相同的行为)
BOOL MainMFCApp::InitInstance()
{
Derived* d = new Derived();
d->Foo();
delete d;
...
}
问题是,当在调试中运行时(未使用版本进行测试),并且当我们最终进入函数内部时,指针已“损坏”:Foo
this
this = 0xcccccccc
this.__vfptr = <unable to read memory>
当我深入研究进入函数的汇编代码时,我看到以下内容:
13:
14:
15: void Derived::Foo(void)
16: {
015E3570 55 push ebp
015E3571 8B EC mov ebp,esp
015E3573 83 E4 F8 and esp,0FFFFFFF8h
015E3576 81 EC EC 00 00 00 sub esp,0ECh
015E357C 53 push ebx
015E357D 56 push esi
015E357E 57 push edi
015E357F 51 push ecx
015E3580 8D BD 14 FF FF FF lea edi,[ebp-0ECh]
015E3586 B9 3B 00 00 00 mov ecx,3Bh
015E358B B8 CC CC CC CC mov eax,0CCCCCCCCh
015E3590 F3 AB rep stos dword ptr es:[edi]
015E3592 59 pop ecx
015E3593 89 8C 24 F0 00 00 00 mov dword ptr [esp+0F0h],ecx
17: double a = sqrt(4.9);
015E359A F2 0F 10 05 00 55 09 02 movsd xmm0,mmword ptr ds:[2095500h]
015E35A2 E8 72 D4 FD FF call __libm_sse2_sqrt_precise (015C0A19h)
015E35A7 F2 0F 11 84 24 E0 00 00 00 movsd mmword ptr [esp+0E0h],xmm0
18: double b = -a;
015E35B0 F2 0F 10 84 24 E0 00 00 00 movsd xmm0,mmword ptr [esp+0E0h]
015E35B9 66 0F 57 05 10 55 09 02 xorpd xmm0,xmmword ptr ds:[2095510h]
015E35C1 F2 0F 11 84 24 D0 00 00 00 movsd mmword ptr [esp+0D0h],xmm0
19: return;
20: }
015E35CA 5F pop edi
015E35CB 5E pop esi
015E35CC 5B pop ebx
015E35CD 8B E5 mov esp,ebp
015E35CF 5D pop ebp
015E35D0 C3 ret
--- No source file -------------------------------------------------------------
015E35D1 CC int 3
...
015E35EF CC int 3
第 17 行的断点,就在进入函数体之前:使用监视窗口检查寄存器后面的对象(强制转换为)显示包含我需要的指针(指向对象),但由于某种原因,它被“ed 到看似随机的地址”。ecx
Derived*
ecx
mov
[esp+0F0h]
现在是真正有趣/令人震惊的部分:当我改变时
double b = -a;
自
double b = -1.0 * a;
然后再次编译,一切都神奇地工作。函数程序集现在已更改为:
13:
14:
15: void Derived::Foo(void)
16: {
00863570 55 push ebp
00863571 8B EC mov ebp,esp
00863573 81 EC EC 00 00 00 sub esp,0ECh
00863579 53 push ebx
0086357A 56 push esi
0086357B 57 push edi
0086357C 51 push ecx
0086357D 8D BD 14 FF FF FF lea edi,[ebp-0ECh]
00863583 B9 3B 00 00 00 mov ecx,3Bh
00863588 B8 CC CC CC CC mov eax,0CCCCCCCCh
0086358D F3 AB rep stos dword ptr es:[edi]
0086358F 59 pop ecx
00863590 89 4D F8 mov dword ptr [this],ecx
17: double a = sqrt(4.9);
00863593 F2 0F 10 05 00 55 31 01 movsd xmm0,mmword ptr ds:[1315500h]
0086359B E8 79 D4 FD FF call __libm_sse2_sqrt_precise (0840A19h)
008635A0 F2 0F 11 45 E8 movsd mmword ptr [a],xmm0
18: double b = -1.0 * a;
008635A5 F2 0F 10 05 10 55 31 01 movsd xmm0,mmword ptr ds:[1315510h]
008635AD F2 0F 59 45 E8 mulsd xmm0,mmword ptr [a]
008635B2 F2 0F 11 45 D8 movsd mmword ptr [b],xmm0
19: return;
20: }
008635B7 5F pop edi
008635B8 5E pop esi
008635B9 5B pop ebx
008635BA 81 C4 EC 00 00 00 add esp,0ECh
008635C0 3B EC cmp ebp,esp
008635C2 E8 32 CD FC FF call __RTC_CheckEsp (08302F9h)
008635C7 8B E5 mov esp,ebp
008635C9 5D pop ebp
008635CA C3 ret
--- No source file -------------------------------------------------------------
008635CB CC int 3
...
008635EF CC int 3
现在生成的代码很好地将寄存器中的指针移动到 。其他区别:ecx
this
- 不同的内存地址/偏移量
mulsd
而不是否定变量xorpd
and esp,0FFFFFFF8h
消失(?? 用于对齐堆栈指针 ??)esp
- 更多清理(在函数体之后)??(
add cmp call
)
在这两种情况下,将参数推送到堆栈的装配部件是相同的:
53: d->Foo();
011A500B 8B 45 E0 mov eax,dword ptr [d]
011A500E 8B 10 mov edx,dword ptr [eax]
011A5010 8B F4 mov esi,esp
011A5012 8B 4D E0 mov ecx,dword ptr [d]
011A5015 8B 42 04 mov eax,dword ptr [edx+4]
011A5018 FF D0 call eax
当然,当我尝试使用最小、完整和可验证的示例来复制这一点时,一切都按预期工作。但在我的大项目中,它总是失败。 我不确定哪些参数会影响编译,并且对汇编的了解不够,甚至无法看到那里发生了什么; 因此,我在这里问,希望有人以前见过或认识到这种行为。
注意:当我删除通话时,它也可以再次工作。sqrt
更新:
- 发布没有问题
- VS2012 SP4 (v11.0.61030.00)
- 引用成员变量时问题仍然存在(ISO 无成员引用)
- TODO:尝试不使用全局静力学
答: 暂无答案
评论
this
this
Foo()
this
locals
a
b
2.214
this
and esp,0FFFFFFF8h
ebp = esp + 0f0h
mov [esp+0f0h], ecx
mov [ebp-08h], ecx
mov [this], ecx
add/cmp/call
and
mov
ebp
xor
-1.0 * a