visual-c++ - (VC++) 未初始化变量的运行时检查 : How is the test Implemented?

标签 visual-c++ assembly compiler-construction x86

我正试图了解这个测试究竟做了什么。这个玩具代码

int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    printf("%d", i);
    return 0;
}

编译成这样:

int _tmain(int argc, _TCHAR* argv[])

{ 012C2DF0推送ebp
012C2DF1 mov ebp,esp
012C2DF3 sub esp,0D8h
012C2DF9推ebx
012C2DFA推esi
012C2DFB推送编辑
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);

012C2E15 cmp byte ptr [ebp-0D1h],0
012C2E1C jne wmain+3Bh (012C2E2Bh)
012C2E1E推12C2E5Ch
012C2E23 调用__RTC_UninitUse(012C10B9h)

012C2E28 添加esp,4
012C2E2B mov esi,esp
012C2E2D mov eax,dword ptr [i]
012C2E30 推eax
012C2E31推12C5858h
012C2E36 call dword ptr ds:[12C9114h]
012C2E3C 添加esp,8
012C2E3F cmp esi,esp
012C2E41 调用 __RTC_CheckEsp (012C1140h)

return 0;

012C2E46 xor eax,eax
} 012C2E48 流行音乐
012C2E49 流行音乐
012C2E4A流行ebx
012C2E4B 添加esp,0D8h
012C2E51 cmp ebp,esp
012C2E53 调用__RTC_CheckEsp (012C1140h)
012C2E58 mov esp,ebp
012C2E5A pop ebp
012C2E5B 退

强调的 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的实际地址关系不大。

更糟糕的是,如果有这样一个外部(其他线程?)初始化确实初始化了探测的地址但为零,这个测试仍然会喊出变量未初始化。 p>

发生了什么事?也许探针的目的是完全不同的,比如测试某个字节是否可写?

最佳答案

[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;
}

产生以下内容(跳过不相关的部分):

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

在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

这大致对应于:

if ( $T4693 == 0 )
  _RTC_UninitUse("j");
if ( $T4694 == 0 )
  _RTC_UninitUse("j");
printf("%d %d", i, j);

下一部分:

; 8    :        i = 1;    
    mov BYTE PTR $T4694[ebp], 1
    mov DWORD PTR _i$[ebp], 1

所以,一旦 i 被初始化,跟踪变量就会被设置为 1。

; 10   :        j = 2;
mov BYTE PTR $T4693[ebp], 1
mov DWORD PTR _j$[ebp], 2

这里,j也是如此。

关于visual-c++ - (VC++) 未初始化变量的运行时检查 : How is the test Implemented?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27252003/

相关文章:

assembly - 在 NASM 中使用 OR r/m32、imm32

c - - assembly sse - 段错误 - 在另一个矩阵中移动一个矩阵行

c - 在 Linux 上用 C 编写自定义加载器并为 x64 汇编

c# - 如何操作 C# AST?

C++ 使用外部文件中的变量

visual-studio-2010 - 使用 Visual Studio 的调试器查看动态分配的空终止字符串

java - 你能在Geany中用一条命令编译并执行Java文件吗?

memory-management - gfortran 解除分配段错误

c++ - 单独输入线程上的 OpenGL 无效操作错误

c++ - 从控制台应用程序到 .NET Windows 窗体