我使用Visual Studio C ++ 2008 SP1,x64
C++
编译器编译了以下内容:
我很好奇,为什么编译器会在nop
之后添加call
条指令?
PS1。我会理解第二个和第三个nop
将在4个字节的边距上对齐代码,但第一个nop
打破了这个假设。
PS2。编译的C ++代码中没有循环或特殊的优化内容:
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTestDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//This makes no sense. I used it to set a debugger breakpoint
::GdiFlush();
srand(::GetTickCount());
}
PS3。 其他信息: 首先,感谢大家的意见。
此处还有其他观察结果:
我的第一个猜测是incremental linking可能与它有关。但是,项目Release
中的Visual Studio
版本设置已关闭incremental linking
。
这似乎仅影响x64
版本。构建为x86
(或Win32
)的相同代码没有nop
s,即使使用的指令非常相似:
x64
生成的VS 2013
代码看起来有些不同,它仍会在nop
之后添加call
个dynamic
} S:static
与nop
链接到MFC对这些VS 2013
的存在没有任何影响。这个是使用nop
:near
也可以在far
和call
IDA
之后显示,而且它们与对齐无关。这是我从nop
获得的代码的一部分,如果我再继续一步:如您所见,在far
call
恰好"对齐"之后插入了lea
。 B
地址上的下一条near
指令!如果仅为了对齐而添加这些内容毫无意义。
relative
call
E8
s(即以far
开头的那些)somewhat faster而不是call
} FF
s(或以15
开头的那些,在这种情况下为near
)链接器可能会先尝试使用call
far
,因为它们比call
nop
短一个字节,如果成功,它可能会填充结尾有 dt <- factor(c("No", "Yes", "Residue"), levels = c("No", "Yes", "Residue"))
s的剩余空格。但是上面的例子(5)有点打败了这个假设。
所以我仍然没有明确答案。
答案 0 :(得分:3)
这纯粹是猜测,但它可能是某种SEH优化。我说优化因为SEH似乎在没有NOP的情况下工作正常。 NOP可能有助于加速平仓。
在以下示例(live demo with VC2017)中,在NOP
中调用basic_string::assign
但test1
中没有test2
后插入了#include <stdio.h>
#include <string>
int test1() {
std::string s = "a"; // NOP insterted here
s += getchar();
return (int)s.length();
}
int test2() throw() {
std::string s = "a";
s += getchar();
return (int)s.length();
}
int main()
{
return test1() + test2();
}
声明为非投掷 1 )。
test1:
. . .
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign
npad 1 ; nop
call getchar
. . .
test2:
. . .
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign
call getchar
大会:
/EHsc
请注意,MSVS默认使用NOP
标志进行编译(同步异常处理)。如果没有该标志,/EHa
将消失,并且throw()
(同步和异步异常处理),throw()
不再有所作为,因为SEH始终打开。
1 由于某种原因,只有noexcept
似乎减少了代码大小,使用NOP
会使生成的代码更大,并且召唤更多{{1}} s。 MSVC ...
答案 1 :(得分:0)
这是特殊的填充符,用于让异常处理程序/展开函数正确地检测它是否是函数的序言/结尾/主体。
答案 2 :(得分:-2)
这是由于x64中的调用约定要求堆栈在任何调用指令之前对齐16字节。这不是(我的知识)硬件要求,而是软件要求。这提供了一种方法来确保在输入函数时(即,在调用指令之后),堆栈指针的值始终为8模16。因此允许从堆栈中的对齐位置进行简单的数据对齐和存储/读取。 / p>