检查code。通过Visual Studio的C ++编译器生成,第1部分 [英] Examining code generated by the Visual Studio C++ compiler, part 1

查看:154
本文介绍了检查code。通过Visual Studio的C ++编译器生成,第1部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可能重复:结果
  <一href=\"http://stackoverflow.com/questions/12692111/why-is-such-complex-$c$c-emitted-for-dividing-a-signed-integer-by-a-power-of-two\">Why在这种复杂的code发出由两个功率分配有符号整数?

我只是通过检查由编译器生成的二进制code学习汇编的x86

Background

I'm just learning x86 asm by examining the binary code generated by the compiler.

code。使用C ++编译器在的Visual Studio 2010 Beta 2中<编译/ A>

Code compiled using the C++ compiler in Visual Studio 2010 beta 2.

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.21003.01 for 80x86

C code(sandbox.c)

int mainCRTStartup()
{
    int x=5;int y=1024;
    while(x) { x--; y/=2; }
    return x+y;
}

使用Visual Studio命令提示符编译

cl /c /O2 /Oy- /MD sandbox.c
link /NODEFAULTLIB /MANIFEST:NO /SUBSYSTEM:CONSOLE sandbox.obj

DISASM sandbox.exe在OllyDgb

从入口点以下开始。

Disasm sandbox.exe in OllyDgb

The following starts from the entry point.

00401000 >/$ B9 05000000    MOV ECX,5
00401005  |. B8 00040000    MOV EAX,400
0040100A  |. 8D9B 00000000  LEA EBX,DWORD PTR DS:[EBX]
00401010  |> 99             /CDQ
00401011  |. 2BC2           |SUB EAX,EDX
00401013  |. D1F8           |SAR EAX,1
00401015  |. 49             |DEC ECX
00401016  |.^75 F8          \JNZ SHORT sandbox.00401010
00401018  \. C3             RETN

检查

MOV ECX, 5          int x=5;
MOV EAX, 400        int y=1024;
LEA  ...            // no idea what LEA does here. seems like ebx=ebx. elaborate please.
                    // in fact, NOPing it does nothing to the original procedure and the values.

CQD                 // sign extends EAX into EDX:EAX, which here: edx = 0. no idea why.
SUB EAX, EDX        // eax=eax-edx, here: eax=eax-0. no idea, pretty redundant. 
SAR EAX,1           // okay, y/= 2
DEC ECX             // okay, x--, sets the zero flag when reaches 0.
JNZ ...             // okay, jump back to CQD if the zero flag is not set.

这部分困扰我:

0040100A  |. 8D9B 00000000  LEA EBX,DWORD PTR DS:[EBX]
00401010  |> 99             /CDQ
00401011  |. 2BC2           |SUB EAX,EDX

您可以NOP这一切,EAX和ECX的值将保持不变底。那么,什么是这些指令的意义呢?

You can nop it all and the values of EAX and ECX will remain the same at the end. So, what's the point of these instructions?

推荐答案

整个事情

00401010  |> 99             /CDQ
00401011  |. 2BC2           |SUB EAX,EDX
00401013  |. D1F8           |SAR EAX,1

Y / = 2 。你看,一个独立的 SAR 不会执行符号整数除法的编译器作者所期望的方式。 C ++ 98标准的建议的签署整数除法舍入结果向0,而 SAR 单独将全面向负无穷大。 (这是可允许的圆朝负无穷大,选择的是留给实现)。为了实现四舍五入为0负的操作数,则使用上述伎俩。如果你使用一个无符号的类型,而不是一个有符号之一,那么编译器将生成只是一个单一的移位指令,因为负分工问题将不会发生。

stands for the y /= 2. You see, a standalone SAR would not perform the signed integer division the way the compiler authors intended. C++98 standard recommends that signed integer division rounds the result towards 0, while SAR alone would round towards the negative infinity. (It is permissible to round towards negative infinity, the choice is left to the implementation). In order to implement rounding to 0 for negative operands, the above trick is used. If you use an unsigned type instead of a signed one, then the compiler will generate just a single shift instruction, since the issue with negative division will not take place.

诀窍是pretty简单:负符号扩展将会把 11111 ... 1 EDX ,这实际上 1 是2的补再presentation。以下 SUB 将有效增加1 EAX 如果原来的值呈阴性。如果原来的为阳性(或0), EDX 将持有 0 符号扩展后 EAX 将保持不变。

The trick is pretty simple: for negative y sign extension will place a pattern of 11111...1 in EDX, which is actually -1 in 2's complement representation. The following SUB will effectively add 1 to EAX if the original y value was negative. If the original y was positive (or 0), the EDX will hold 0 after the sign extension and EAX will remain unchanged.

在换句话说,当你写 Y / = 2 已签署,编译器生成的code,做更多的东西像下面的

In other words, when you write y /= 2 with signed y, the compiler generates the code that does something more like the following

y = (y < 0 ? y + 1 : y) >> 1;

,或更好

y = (y + (y < 0)) >> 1;

请注意,是C ++标准不要求分工的结果向零取整,因此编译器做的只是符号类型单一,甚至转向的权利。然而,通常编译器遵循的建议的圆接近零(或提供一个选项来控制行为)。

Note, that C++ standard does not require the result of the division to be rounded towards zero, so the compiler has the right to do just a single shift even for signed types. However, normally compilers follow the recommendation to round towards zero (or offer an option to control the behavior).

P.S。我不知道肯定 LEA 指令的目的是什么。这的确是一个无操作。不过,我怀疑这可能只是插入code进一步修补的占位符指令。如果我没有记错,MS编译器有强制的占位指令的插入的开头和每个函数的最后一个选项。今后该指令可以的覆盖的通过用 CALL该修补 JMP 指令将执行补丁code。这种特殊的 LEA 被选中只是因为它产生的正确长度的无操作指令的占位符。当然,也可能是完全不同的东西。

P.S. I don't know for sure what the purpose of that LEA instruction is. It is indeed a no-op. However, I suspect that this might be just a placeholder instruction inserted into the code for further patching. If I remember correctly, MS compiler has an option that forces the insertion of placeholder instructions at the beginning and at the end of each function. In the future this instruction can be overwritten by the patcher with a CALL or JMP instruction that will execute the patch code. This specific LEA was chosen just because it produces the a no-op placeholder instruction of the correct length. Of course, it could be something completely different.

这篇关于检查code。通过Visual Studio的C ++编译器生成,第1部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆