Ofe*_*lon 2 compiler-construction x86 assembly visual-c++
我试图理解这个测试究竟做了什么.这个玩具代码
int _tmain(int argc, _TCHAR* argv[])
{
int i;
printf("%d", i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译成:
int _tmain(int argc, _TCHAR* argv[])
Run Code Online (Sandbox Code Playgroud)
{012C2DF0 push ebp
012C2DF1 mov ebp,esp
012C2DF3 sub esp,0D8h
012C2DF9 push ebx
012C2DFA push esi
012C2DFB push edi
012C2DFC lea edi,[ebp-0D8h]
012C2E02 mov ecx,36h
012C2E07 mov eax,0CCCCCCCCh
012C2E0C rep stos dword ptr es:[ edi]
012C2E0E mov byte ptr [ebp-0D1h],0
int i;
printf("%d", i);
Run Code Online (Sandbox Code Playgroud)
012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain + 3Bh(012C2E2Bh)
012C2E1E push 12C2E5Ch
012C2E23 call __RTC_UninitUse(012C10B9h)
012C2E28 add esp,4
012C2E2B mov esi,esp
012C2E2D mov eax,dword ptr [i]
012C2E30 push eax
012C2E31按12C5858h
012C2E36调用dword ptr ds:[12C9114h]
012C2E3C添加esp,8
012C2E3F cmp esi,esp
012C2E41调用__RTC_CheckEsp(012C1140h)
return 0;
Run Code Online (Sandbox Code Playgroud)
012C2E46 xor eax,eax
} 012C2E48 pop edi
012C2E49 pop esi
012C2E4A pop ebx
012C2E4B add esp,0D8h
012C2E51 cmp ebp,esp
012C2E53 call __RTC_CheckEsp(012C1140h)
012C2E58 mov esp,ebp
012C2E5A pop ebp
012C2E5B ret
强调的5行是通过正确初始化变量i而删除的唯一行.行'push 12C2E5Ch,call __RTC_UninitUse'调用显示错误框的函数,指向包含变量名("i")作为参数的字符串.
我无法理解的是执行实际测试的3行:
012C2E0E mov byte ptr [ebp-0D1h],0
012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain + 3Bh(012C2E2Bh)
似乎编译器正在探测i的堆栈区域(将一个字节设置为零并立即测试它是否为零),只是为了确保它没有在构建期间无法看到的地方初始化.但是,探测地址ebp-0D1h与i的实际地址没有多大关系.
更糟糕的是,似乎有这样一个外部(其他线程?)初始化初始化探测地址但是为零,这个测试仍然会对未初始化的变量大喊大叫.
这是怎么回事?也许探针是针对完全不同的东西,比如测试某个字节是否可写?
[ebp-0D1h]是编译器用于跟踪变量"初始化"状态的临时变量.如果我们稍微修改一下源,那就更清楚了:
int _tmain(int argc, _TCHAR* argv[])
{
int i, j;
printf("%d %d", i, j);
i = 1;
printf("%d %d", i, j);
j = 2;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
生成以下内容(跳过不相关的部分):
mov DWORD PTR [ebp-12], -858993460 ; ccccccccH
mov DWORD PTR [ebp-8], -858993460 ; ccccccccH
mov DWORD PTR [ebp-4], -858993460 ; ccccccccH
mov BYTE PTR $T4694[ebp], 0
mov BYTE PTR $T4693[ebp], 0
Run Code Online (Sandbox Code Playgroud)
在prolog中,变量用0xCC填充,两个跟踪变量(一个用于i,一个用于j)设置为0.
; 7 : printf("%d %d", i, j);
cmp BYTE PTR $T4693[ebp], 0
jne SHORT $LN3@main
push OFFSET $LN4@main
call __RTC_UninitUse
add esp, 4
$LN3@main:
cmp BYTE PTR $T4694[ebp], 0
jne SHORT $LN5@main
push OFFSET $LN6@main
call __RTC_UninitUse
add esp, 4
$LN5@main:
mov eax, DWORD PTR _j$[ebp]
push eax
mov ecx, DWORD PTR _i$[ebp]
push ecx
push OFFSET $SG4678
call _printf
add esp, 12 ; 0000000cH
Run Code Online (Sandbox Code Playgroud)
这大致相当于:
if ( $T4693 == 0 )
_RTC_UninitUse("j");
if ( $T4694 == 0 )
_RTC_UninitUse("j");
printf("%d %d", i, j);
Run Code Online (Sandbox Code Playgroud)
下一部分:
; 8 : i = 1;
mov BYTE PTR $T4694[ebp], 1
mov DWORD PTR _i$[ebp], 1
Run Code Online (Sandbox Code Playgroud)
因此,一旦i初始化,跟踪变量将设置为1.
; 10 : j = 2;
mov BYTE PTR $T4693[ebp], 1
mov DWORD PTR _j$[ebp], 2
Run Code Online (Sandbox Code Playgroud)
在这里,同样的事情正在发生j.