Я скомпилировал следующее, используя компилятор Visual Studio С++ 2008 SP1, x64
C++
:
Мне любопытно, почему компилятор добавил эти nop
инструкции после этих call
s?
PS1. Я бы понял, что 2-й и 3-й nop
будут выровнять код по 4-байтовому полю, но 1-й nop
нарушит это предположение.
PS2. Код С++, который был скомпилирован, не содержал в нем циклов или специальных элементов оптимизации:
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. Дополнительная информация: Прежде всего, спасибо всем за ваш вклад.
Здесь дополнительные наблюдения:
-
Мое первое предположение заключалось в том, что инкрементная привязка могла иметь какое-то отношение к ней. Но настройки сборки
Release
вVisual Studio
для проекта имеютincremental linking
off. -
Это, по-видимому, влияет только на сборки
x64
. Тот же код, построенный какx86
(илиWin32
), не имеет этихnop
s, хотя используемые команды очень похожи:
- Я попытался создать его с помощью нового компоновщика, и хотя код
x64
, созданныйVS 2013
, выглядит несколько иначе, он по-прежнему добавляет теnop
после некоторыхcall
s:
- Также
dynamic
vsstatic
, связанная с MFC, не имело никакого отношения к присутствию этихnop
s. Это построено с динамическим связыванием с dll MFC с помощьюVS 2013
:
- Также обратите внимание, что те
nop
могут появляться послеnear
иfar
call
, и они не имеют никакого отношения к выравниванию. Вот часть кода, который я получил отIDA
, если я немного пошагую дальше:
Как вы видите, nop
вставлен после far
call
, который происходит, чтобы "выровнять" следующую инструкцию lea
по адресу B
! Это не имеет смысла, если они были добавлены только для выравнивания.
- Я изначально был склонен полагать, что поскольку
near
relative
call
(т.е. те, которые начинаются сE8
), несколько быстрее чемfar
call
(или те, которые начинаются сFF
,15
в этом случае)
компоновщик может сначала попытаться перейти с near
call
, и поскольку они являются одним байтом, короче far
call
s, если он преуспеет, он может заполнить оставшееся пространство с помощью nop
на конец. Но тогда пример (5) выше своего рода побеждает эту гипотезу.
Таким образом, у меня до сих пор нет четкого ответа на этот вопрос.